/*************************************************************************\ * Copyright (c) 2002 The University of Chicago, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE Versions 3.13.7 * and higher are distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * * CA performance test * * History * joh 09-12-89 Initial release * joh 12-20-94 portability * * */ #include #include #include #include #include #include #define epicsAssertAuthor "Jeff Hill johill@lanl.gov" #include "epicsAssert.h" #include "epicsTime.h" #include "cadef.h" #include "caProto.h" #include "caDiagnostics.h" #ifndef LOCAL #define LOCAL static #endif #ifndef NULL #define NULL 0 #endif #define WAIT_FOR_ACK typedef struct testItem { chid chix; char name[40]; int type; int count; union { dbr_double_t doubleval; dbr_float_t fltval; dbr_short_t intval; dbr_string_t strval; } val; } ti; typedef void tf ( ti *pItems, unsigned iterations, unsigned *pInlineIter ); /* * test_pend() */ LOCAL void test_pend( ti *pItems, unsigned iterations, unsigned *pInlineIter ) { unsigned i; int status; for (i=0; itype, pi->count, pi->chix, &pi->val); SEVCHK (status, NULL); status = ca_array_put( pi->type, pi->count, pi->chix, &pi->val); SEVCHK (status, NULL); status = ca_array_put( pi->type, pi->count, pi->chix, &pi->val); SEVCHK (status, NULL); status = ca_array_put( pi->type, pi->count, pi->chix, &pi->val); SEVCHK (status, NULL); status = ca_array_put( pi->type, pi->count, pi->chix, &pi->val); SEVCHK (status, NULL); status = ca_array_put( pi->type, pi->count, pi->chix, &pi->val); SEVCHK (status, NULL); status = ca_array_put( pi->type, pi->count, pi->chix, &pi->val); SEVCHK (status, NULL); status = ca_array_put( pi->type, pi->count, pi->chix, &pi->val); SEVCHK (status, NULL); status = ca_array_put( pi->type, pi->count, pi->chix, &pi->val); SEVCHK (status, NULL); status = ca_array_put( pi->type, pi->count, pi->chix, &pi->val); SEVCHK (status, NULL); } #ifdef WAIT_FOR_ACK status = ca_array_get (DBR_INT, 1, pItems[0].chix, &val); SEVCHK (status, NULL); ca_pend_io(100.0); #endif status = ca_array_put( pItems[0].type, pItems[0].count, pItems[0].chix, &pItems[0].val); SEVCHK (status, NULL); status = ca_flush_io(); SEVCHK (status, NULL); *pInlineIter = 10; } /* * test_get () */ LOCAL void test_get( ti *pItems, unsigned iterations, unsigned *pInlineIter ) { ti *pi; int status; for (pi=pItems; pi<&pItems[iterations]; pi++) { status = ca_array_get( pi->type, pi->count, pi->chix, &pi->val); SEVCHK (status, NULL); status = ca_array_get( pi->type, pi->count, pi->chix, &pi->val); SEVCHK (status, NULL); status = ca_array_get( pi->type, pi->count, pi->chix, &pi->val); SEVCHK (status, NULL); status = ca_array_get( pi->type, pi->count, pi->chix, &pi->val); SEVCHK (status, NULL); status = ca_array_get( pi->type, pi->count, pi->chix, &pi->val); SEVCHK (status, NULL); status = ca_array_get( pi->type, pi->count, pi->chix, &pi->val); SEVCHK (status, NULL); status = ca_array_get( pi->type, pi->count, pi->chix, &pi->val); SEVCHK (status, NULL); status = ca_array_get( pi->type, pi->count, pi->chix, &pi->val); SEVCHK (status, NULL); status = ca_array_get( pi->type, pi->count, pi->chix, &pi->val); SEVCHK (status, NULL); status = ca_array_get( pi->type, pi->count, pi->chix, &pi->val); SEVCHK (status, NULL); } status = ca_pend_io(100.0); SEVCHK (status, NULL); *pInlineIter = 10; } /* * test_wait () */ LOCAL void test_wait ( ti *pItems, unsigned iterations, unsigned *pInlineIter ) { ti *pi; int status; for (pi=pItems; pi<&pItems[iterations]; pi++) { status = ca_array_get( pi->type, pi->count, pi->chix, &pi->val); SEVCHK (status, NULL); status = ca_pend_io(100.0); SEVCHK (status, NULL); } *pInlineIter = 1; } /* * measure_get_latency */ LOCAL void measure_get_latency (ti *pItems, unsigned iterations) { epicsTimeStamp end_time; epicsTimeStamp start_time; double delay; double X = 0u; double XX = 0u; double max = DBL_MIN; double min = DBL_MAX; double mean; double stdDev; ti *pi; int status; for ( pi = pItems; pi < &pItems[iterations]; pi++ ) { epicsTimeGetCurrent ( &start_time ); status = ca_array_get ( pi->type, pi->count, pi->chix, &pi->val ); SEVCHK ( status, NULL ); status = ca_pend_io ( 100.0 ); SEVCHK ( status, NULL ); epicsTimeGetCurrent ( &end_time ); delay = epicsTimeDiffInSeconds ( &end_time,&start_time ); X += delay; XX += delay*delay; if ( delay > max ) { max = delay; } if ( delay < min ) { min = delay; } } mean = X/iterations; stdDev = sqrt ( XX/iterations - mean*mean ); printf ( "Round trip get delays - mean=%f sec, std dev=%f sec, min=%f sec max=%f sec\n", mean, stdDev, min, max ); } /* * printSearchStat() */ LOCAL void printSearchStat ( const ti *pi, unsigned iterations ) { unsigned i; double X = 0u; double XX = 0u; double max = DBL_MIN; double min = DBL_MAX; double mean; double stdDev; for ( i = 0; i < iterations; i++ ) { double retry = ca_search_attempts ( pi[i].chix ); X += retry; XX += retry * retry; if ( retry > max ) { max = retry; } if ( retry < min ) { min = retry; } } mean = X / iterations; stdDev = sqrt( XX / iterations - mean * mean ); printf ( "Search tries per chan - mean=%f std dev=%f min=%f max=%f\n", mean, stdDev, min, max); } /* * timeIt () */ void timeIt ( tf *pfunc, ti *pItems, unsigned iterations, unsigned nBytes ) { epicsTimeStamp end_time; epicsTimeStamp start_time; double delay; unsigned inlineIter; epicsTimeGetCurrent (&start_time); (*pfunc) (pItems, iterations, &inlineIter); epicsTimeGetCurrent (&end_time); delay = epicsTimeDiffInSeconds (&end_time, &start_time); if (delay>0.0) { double freq = ( iterations * inlineIter ) / delay; printf ( "Elapsed Per Item = %12.8f sec, %10.1f Items per sec", 1.0 / freq, freq ); if ( pItems != NULL ) { printf(", %3.1f Mbps\n", (inlineIter*nBytes*CHAR_BIT)/(delay*1e6)); } else { printf ("\n"); } } else { printf ("Elapsed Per Item = %12.8f sec\n", delay/(iterations*inlineIter)); } } /* * test () */ LOCAL void test ( ti *pItems, unsigned iterations ) { unsigned nBytes; printf ( "\tasync put test\n"); nBytes = sizeof ( caHdr ) + OCT_ROUND( dbr_size[pItems[0].type] ); timeIt ( test_put, pItems, iterations, nBytes * iterations ); printf ( "\tasync get test\n"); nBytes = 2 * sizeof ( caHdr ) + OCT_ROUND ( dbr_size[pItems[0].type] ); timeIt ( test_get, pItems, iterations/2, nBytes * ( iterations / 2 ) ); printf ("\tsynch get test\n"); nBytes = 2 * sizeof ( caHdr ) + OCT_ROUND ( dbr_size[pItems[0].type] ); timeIt ( test_wait, pItems, iterations/100, nBytes * ( iterations / 100 ) ); } /* * catime () */ int catime ( const char * channelName, unsigned channelCount, enum appendNumberFlag appNF ) { unsigned i; unsigned strsize; unsigned nBytes; ti *pItemList; if ( channelCount == 0 ) { printf ( "channel count was zero\n" ); return 0; } pItemList = calloc ( channelCount, sizeof (ti) ); if ( ! pItemList ) { return -1; } SEVCHK ( ca_context_create ( ca_disable_preemptive_callback ), "Unable to initialize" ); if ( appNF == appendNumber ) { printf ( "Testing with %u channels named %snnn\n", channelCount, channelName ); } else { printf ( "Testing with %u channels named %s\n", channelCount, channelName ); } strsize = sizeof ( pItemList[0].name ) - 1; nBytes = 0; for ( i=0; i < channelCount; i++ ) { if ( appNF == appendNumber ) { sprintf ( pItemList[i].name,"%.*s%.6u", (int) (strsize - 15u), channelName, i ); } else { strncpy ( pItemList[i].name, channelName, strsize); } pItemList[i].name[strsize]= '\0'; pItemList[i].count = 1; nBytes += 2 * ( OCT_ROUND ( strlen ( pItemList[i].name ) ) + 2 * sizeof (caHdr) ); } printf ( "channel connect test\n" ); timeIt ( test_search, pItemList, channelCount, nBytes ); printSearchStat ( pItemList, channelCount ); printf ( "channel name=%s, native type=%d, native count=%lu\n", ca_name (pItemList[0].chix), ca_field_type (pItemList[0].chix), ca_element_count (pItemList[0].chix)); printf ("\tpend event test\n"); timeIt (test_pend, NULL, 100, 0); for ( i = 0; i < channelCount; i++ ) { pItemList[i].val.fltval = 0.0f; pItemList[i].type = DBR_FLOAT; } printf ( "float test\n" ); test ( pItemList, channelCount ); for ( i = 0; i < channelCount; i++ ) { pItemList[i].val.doubleval = 0.0; pItemList[i].type = DBR_DOUBLE; } printf ( "double test\n" ); test ( pItemList, channelCount ); for ( i = 0; i < channelCount; i++ ) { strcpy ( pItemList[i].val.strval, "0.0" ); pItemList[i].type = DBR_STRING; } printf ( "string test\n" ); test ( pItemList, channelCount ); for ( i = 0; i < channelCount; i++ ) { pItemList[i].val.intval = 0; pItemList[i].type = DBR_INT; } printf ( "integer test\n" ); test ( pItemList, channelCount ); printf ( "round trip jitter test\n" ); for ( i = 0; i < channelCount; i++ ) { pItemList[i].val.fltval = 0.0f; pItemList[i].type = DBR_DOUBLE; } measure_get_latency ( pItemList, channelCount ); printf ("free test\n"); timeIt ( test_free, pItemList, channelCount, 0 ); SEVCHK ( ca_task_exit (), "Unable to free resources at exit" ); free ( pItemList ); return CATIME_OK; }