/*************************************************************************\ * 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 NULL #define NULL 0 #endif #define WAIT_FOR_ACK typedef struct testItem { chid chix; char name[128]; int type; int count; void * pValue; } ti; typedef void tf ( ti *pItems, unsigned iterations, unsigned *pInlineIter ); /* * test_pend() */ static void test_pend( ti *pItems, unsigned iterations, unsigned *pInlineIter ) { unsigned i; int status; for (i=0; itype, pi->count, pi->chix, pi->pValue); SEVCHK (status, NULL); status = ca_array_put( pi->type, pi->count, pi->chix, pi->pValue); SEVCHK (status, NULL); status = ca_array_put( pi->type, pi->count, pi->chix, pi->pValue); SEVCHK (status, NULL); status = ca_array_put( pi->type, pi->count, pi->chix, pi->pValue); SEVCHK (status, NULL); status = ca_array_put( pi->type, pi->count, pi->chix, pi->pValue); SEVCHK (status, NULL); status = ca_array_put( pi->type, pi->count, pi->chix, pi->pValue); SEVCHK (status, NULL); status = ca_array_put( pi->type, pi->count, pi->chix, pi->pValue); SEVCHK (status, NULL); status = ca_array_put( pi->type, pi->count, pi->chix, pi->pValue); SEVCHK (status, NULL); status = ca_array_put( pi->type, pi->count, pi->chix, pi->pValue); SEVCHK (status, NULL); status = ca_array_put( pi->type, pi->count, pi->chix, pi->pValue); 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].pValue); SEVCHK (status, NULL); status = ca_flush_io(); SEVCHK (status, NULL); *pInlineIter = 10; } /* * test_get () */ static 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->pValue); SEVCHK (status, NULL); status = ca_array_get( pi->type, pi->count, pi->chix, pi->pValue); SEVCHK (status, NULL); status = ca_array_get( pi->type, pi->count, pi->chix, pi->pValue); SEVCHK (status, NULL); status = ca_array_get( pi->type, pi->count, pi->chix, pi->pValue); SEVCHK (status, NULL); status = ca_array_get( pi->type, pi->count, pi->chix, pi->pValue); SEVCHK (status, NULL); status = ca_array_get( pi->type, pi->count, pi->chix, pi->pValue); SEVCHK (status, NULL); status = ca_array_get( pi->type, pi->count, pi->chix, pi->pValue); SEVCHK (status, NULL); status = ca_array_get( pi->type, pi->count, pi->chix, pi->pValue); SEVCHK (status, NULL); status = ca_array_get( pi->type, pi->count, pi->chix, pi->pValue); SEVCHK (status, NULL); status = ca_array_get( pi->type, pi->count, pi->chix, pi->pValue); SEVCHK (status, NULL); } status = ca_pend_io(1e20); SEVCHK (status, NULL); *pInlineIter = 10; } /* * test_wait () */ static 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->pValue); SEVCHK (status, NULL); status = ca_pend_io(100.0); SEVCHK (status, NULL); } *pInlineIter = 1; } /* * measure_get_latency */ static 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->pValue ); 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 ( "Get Latency - " "mean = %3.1f uS, " "std dev = %3.1f uS, " "min = %3.1f uS " "max = %3.1f uS\n", mean * 1e6, stdDev * 1e6, min * 1e6, max * 1e6 ); } /* * printSearchStat() */ static 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 = %3.1f " "std dev = %3.1f " "min = %3.1f " "max = %3.1f\n", mean, stdDev, min, max); } /* * timeIt () */ void timeIt ( tf *pfunc, ti *pItems, unsigned iterations, unsigned nBytesSent, unsigned nBytesRecv ) { 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 ( "Per Op, %8.4f uS ( %8.4f MHz )", 1e6 / freq, freq / 1e6 ); if ( pItems != NULL ) { printf(", %8.4f snd Mbps, %8.4f rcv Mbps\n", (inlineIter*nBytesSent*CHAR_BIT)/(delay*1e6), (inlineIter*nBytesRecv*CHAR_BIT)/(delay*1e6) ); } else { printf ("\n"); } } } /* * test () */ static void test ( ti *pItems, unsigned iterations ) { unsigned payloadSize, dblPayloadSize; unsigned nBytesSent, nBytesRecv; payloadSize = dbr_size_n ( pItems[0].type, pItems[0].count ); payloadSize = CA_MESSAGE_ALIGN ( payloadSize ); dblPayloadSize = dbr_size [ DBR_DOUBLE ]; dblPayloadSize = CA_MESSAGE_ALIGN ( dblPayloadSize ); if ( payloadSize > dblPayloadSize ) { unsigned factor = payloadSize / dblPayloadSize; while ( factor ) { if ( iterations > 10 * factor ) { iterations /= factor; break; } factor /= 2; } } printf ( "\t### async put test ###\n"); nBytesSent = sizeof ( caHdr ) + CA_MESSAGE_ALIGN( payloadSize ); nBytesRecv = 0u; timeIt ( test_put, pItems, iterations, nBytesSent * iterations, nBytesRecv * iterations ); printf ( "\t### async get test ###\n"); nBytesSent = sizeof ( caHdr ); nBytesRecv = sizeof ( caHdr ) + CA_MESSAGE_ALIGN ( payloadSize ); timeIt ( test_get, pItems, iterations, nBytesSent * ( iterations ), nBytesRecv * ( iterations ) ); printf ("\t### synch get test ###\n"); nBytesSent = sizeof ( caHdr ); nBytesRecv = sizeof ( caHdr ) + CA_MESSAGE_ALIGN ( payloadSize ); if ( iterations > 100 ) { iterations /= 100; } else if ( iterations > 10 ) { iterations /= 10; } timeIt ( test_wait, pItems, iterations, nBytesSent * iterations, nBytesRecv * iterations ); } /* * catime () */ int catime ( const char * channelName, unsigned channelCount, enum appendNumberFlag appNF ) { unsigned i; int j; unsigned strsize; unsigned nBytesSent, nBytesRecv; 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; nBytesSent = 0; nBytesRecv = 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 = 0; pItemList[i].pValue = 0; nBytesSent += 2 * ( CA_MESSAGE_ALIGN ( strlen ( pItemList[i].name ) ) + sizeof (caHdr) ); nBytesRecv += 2 * sizeof (caHdr); } printf ( "Channel Connect Test\n" ); printf ( "--------------------\n" ); timeIt ( test_search, pItemList, channelCount, nBytesSent, nBytesRecv ); printSearchStat ( pItemList, channelCount ); for ( i = 0; i < channelCount; i++ ) { size_t count = ca_element_count ( pItemList[i].chix ); size_t size = sizeof ( dbr_string_t ) * count; pItemList[i].count = count; pItemList[i].pValue = malloc ( size ); assert ( pItemList[i].pValue ); } printf ( "channel name=%s, native type=%d, native count=%u\n", ca_name (pItemList[0].chix), ca_field_type (pItemList[0].chix), pItemList[0].count ); printf ("Pend Event Test\n"); printf ( "----------------\n" ); timeIt ( test_pend, NULL, 100, 0, 0 ); for ( i = 0; i < channelCount; i++ ) { dbr_float_t * pFltVal = ( dbr_float_t * ) pItemList[i].pValue; double val = i; val /= channelCount; for ( j = 0; j < pItemList[i].count; j++ ) { pFltVal[j] = (dbr_float_t) val; } pItemList[i].type = DBR_FLOAT; } printf ( "DBR_FLOAT Test\n" ); printf ( "--------------\n" ); test ( pItemList, channelCount ); for ( i = 0; i < channelCount; i++ ) { dbr_double_t * pDblVal = ( dbr_double_t * ) pItemList[i].pValue; double val = i; val /= channelCount; for ( j = 0; j < pItemList[i].count; j++ ) { pDblVal[j] = (dbr_double_t) val; } pItemList[i].type = DBR_DOUBLE; } printf ( "DBR_DOUBLE Test\n" ); printf ( "---------------\n" ); test ( pItemList, channelCount ); for ( i = 0; i < channelCount; i++ ) { dbr_string_t * pStrVal = ( dbr_string_t * ) pItemList[i].pValue; double val = i; val /= channelCount; for ( j = 0; j < pItemList[i].count; j++ ) { sprintf ( pStrVal[j], "%f", val ); } pItemList[i].type = DBR_STRING; } printf ( "DBR_STRING Test\n" ); printf ( "---------------\n" ); test ( pItemList, channelCount ); for ( i = 0; i < channelCount; i++ ) { dbr_int_t * pIntVal = ( dbr_int_t * ) pItemList[i].pValue; double val = i; val /= channelCount; for ( j = 0; j < pItemList[i].count; j++ ) { pIntVal[j] = (dbr_int_t) val; } pItemList[i].type = DBR_INT; } printf ( "DBR_INT Test\n" ); printf ( "------------\n" ); test ( pItemList, channelCount ); printf ( "Get Latency Test\n" ); printf ( "----------------\n" ); for ( i = 0; i < channelCount; i++ ) { dbr_double_t * pDblVal = ( dbr_double_t * ) pItemList[i].pValue; for ( j = 0; j < pItemList[i].count; j++ ) { pDblVal[j] = 0; } pItemList[i].type = DBR_DOUBLE; } measure_get_latency ( pItemList, channelCount ); printf ( "Free Channel Test\n" ); printf ( "-----------------\n" ); timeIt ( test_free, pItemList, channelCount, 0, 0 ); SEVCHK ( ca_task_exit (), "Unable to free resources at exit" ); for ( i = 0; i < channelCount; i++ ) { free ( pItemList[i].pValue ); } free ( pItemList ); return CATIME_OK; }