Home | History | Annotate | Download | only in ctestfw
      1 //  2016 and later: Unicode, Inc. and others.
      2 // License & terms of use: http://www.unicode.org/copyright.html
      3 /********************************************************************
      4  * COPYRIGHT:
      5  * Copyright (c) 2002-2012, International Business Machines Corporation and
      6  * others. All Rights Reserved.
      7  ********************************************************************/
      8 
      9 // Defines _XOPEN_SOURCE for access to POSIX functions.
     10 // Must be before any other #includes.
     11 #include "uposixdefs.h"
     12 
     13 #include "unicode/uperf.h"
     14 #include "uoptions.h"
     15 #include "cmemory.h"
     16 #include <stdio.h>
     17 #include <stdlib.h>
     18 
     19 #if !UCONFIG_NO_CONVERSION
     20 
     21 UPerfFunction::~UPerfFunction() {}
     22 
     23 static const char delim = '/';
     24 static int32_t execCount = 0;
     25 UPerfTest* UPerfTest::gTest = NULL;
     26 static const int MAXLINES = 40000;
     27 const char UPerfTest::gUsageString[] =
     28     "Usage: %s [OPTIONS] [FILES]\n"
     29     "\tReads the input file and prints out time taken in seconds\n"
     30     "Options:\n"
     31     "\t-h or -? or --help   this usage text\n"
     32     "\t-v or --verbose      print extra information when processing files\n"
     33     "\t-s or --sourcedir    source directory for files followed by path\n"
     34     "\t                     followed by path\n"
     35     "\t-e or --encoding     encoding of source files\n"
     36     "\t-u or --uselen       perform timing analysis on non-null terminated buffer using length\n"
     37     "\t-f or --file-name    file to be used as input data\n"
     38     "\t-p or --passes       Number of passes to be performed. Requires Numeric argument.\n"
     39     "\t                     Cannot be used with --time\n"
     40     "\t-i or --iterations   Number of iterations to be performed. Requires Numeric argument\n"
     41     "\t-t or --time         Threshold time for looping until in seconds. Requires Numeric argument.\n"
     42     "\t                     Cannot be used with --iterations\n"
     43     "\t-l or --line-mode    The data file should be processed in line mode\n"
     44     "\t-b or --bulk-mode    The data file should be processed in file based.\n"
     45     "\t                     Cannot be used with --line-mode\n"
     46     "\t-L or --locale       Locale for the test\n";
     47 
     48 enum
     49 {
     50     HELP1,
     51     HELP2,
     52     VERBOSE,
     53     SOURCEDIR,
     54     ENCODING,
     55     USELEN,
     56     FILE_NAME,
     57     PASSES,
     58     ITERATIONS,
     59     TIME,
     60     LINE_MODE,
     61     BULK_MODE,
     62     LOCALE,
     63     OPTIONS_COUNT
     64 };
     65 
     66 
     67 static UOption options[OPTIONS_COUNT+20]={
     68     UOPTION_HELP_H,
     69     UOPTION_HELP_QUESTION_MARK,
     70     UOPTION_VERBOSE,
     71     UOPTION_SOURCEDIR,
     72     UOPTION_ENCODING,
     73     UOPTION_DEF( "uselen",        'u', UOPT_NO_ARG),
     74     UOPTION_DEF( "file-name",     'f', UOPT_REQUIRES_ARG),
     75     UOPTION_DEF( "passes",        'p', UOPT_REQUIRES_ARG),
     76     UOPTION_DEF( "iterations",    'i', UOPT_REQUIRES_ARG),
     77     UOPTION_DEF( "time",          't', UOPT_REQUIRES_ARG),
     78     UOPTION_DEF( "line-mode",     'l', UOPT_NO_ARG),
     79     UOPTION_DEF( "bulk-mode",     'b', UOPT_NO_ARG),
     80     UOPTION_DEF( "locale",        'L', UOPT_REQUIRES_ARG)
     81 };
     82 
     83 UPerfTest::UPerfTest(int32_t argc, const char* argv[], UErrorCode& status)
     84         : _argc(argc), _argv(argv), _addUsage(NULL),
     85           ucharBuf(NULL), encoding(""),
     86           uselen(FALSE),
     87           fileName(NULL), sourceDir("."),
     88           lines(NULL), numLines(0), line_mode(TRUE),
     89           buffer(NULL), bufferLen(0),
     90           verbose(FALSE), bulk_mode(FALSE),
     91           passes(1), iterations(0), time(0),
     92           locale(NULL) {
     93     init(NULL, 0, status);
     94 }
     95 
     96 UPerfTest::UPerfTest(int32_t argc, const char* argv[],
     97                      UOption addOptions[], int32_t addOptionsCount,
     98                      const char *addUsage,
     99                      UErrorCode& status)
    100         : _argc(argc), _argv(argv), _addUsage(addUsage),
    101           ucharBuf(NULL), encoding(""),
    102           uselen(FALSE),
    103           fileName(NULL), sourceDir("."),
    104           lines(NULL), numLines(0), line_mode(TRUE),
    105           buffer(NULL), bufferLen(0),
    106           verbose(FALSE), bulk_mode(FALSE),
    107           passes(1), iterations(0), time(0),
    108           locale(NULL) {
    109     init(addOptions, addOptionsCount, status);
    110 }
    111 
    112 void UPerfTest::init(UOption addOptions[], int32_t addOptionsCount,
    113                      UErrorCode& status) {
    114     //initialize the argument list
    115     U_MAIN_INIT_ARGS(_argc, _argv);
    116 
    117     resolvedFileName = NULL;
    118 
    119     // add specific options
    120     int32_t optionsCount = OPTIONS_COUNT;
    121     if (addOptionsCount > 0) {
    122         memcpy(options+optionsCount, addOptions, addOptionsCount*sizeof(UOption));
    123         optionsCount += addOptionsCount;
    124     }
    125 
    126     //parse the arguments
    127     _remainingArgc = u_parseArgs(_argc, (char**)_argv, optionsCount, options);
    128 
    129     // copy back values for additional options
    130     if (addOptionsCount > 0) {
    131         memcpy(addOptions, options+OPTIONS_COUNT, addOptionsCount*sizeof(UOption));
    132     }
    133 
    134     // Now setup the arguments
    135     if(_argc==1 || options[HELP1].doesOccur || options[HELP2].doesOccur) {
    136         status = U_ILLEGAL_ARGUMENT_ERROR;
    137         return;
    138     }
    139 
    140     if(options[VERBOSE].doesOccur) {
    141         verbose = TRUE;
    142     }
    143 
    144     if(options[SOURCEDIR].doesOccur) {
    145         sourceDir = options[SOURCEDIR].value;
    146     }
    147 
    148     if(options[ENCODING].doesOccur) {
    149         encoding = options[ENCODING].value;
    150     }
    151 
    152     if(options[USELEN].doesOccur) {
    153         uselen = TRUE;
    154     }
    155 
    156     if(options[FILE_NAME].doesOccur){
    157         fileName = options[FILE_NAME].value;
    158     }
    159 
    160     if(options[PASSES].doesOccur) {
    161         passes = atoi(options[PASSES].value);
    162     }
    163     if(options[ITERATIONS].doesOccur) {
    164         iterations = atoi(options[ITERATIONS].value);
    165         if(options[TIME].doesOccur) {
    166             status = U_ILLEGAL_ARGUMENT_ERROR;
    167             return;
    168         }
    169     } else if(options[TIME].doesOccur) {
    170         time = atoi(options[TIME].value);
    171     } else {
    172         iterations = 1000; // some default
    173     }
    174 
    175     if(options[LINE_MODE].doesOccur) {
    176         line_mode = TRUE;
    177         bulk_mode = FALSE;
    178     }
    179 
    180     if(options[BULK_MODE].doesOccur) {
    181         bulk_mode = TRUE;
    182         line_mode = FALSE;
    183     }
    184 
    185     if(options[LOCALE].doesOccur) {
    186         locale = options[LOCALE].value;
    187     }
    188 
    189     int32_t len = 0;
    190     if(fileName!=NULL){
    191         //pre-flight
    192         ucbuf_resolveFileName(sourceDir, fileName, NULL, &len, &status);
    193         resolvedFileName = (char*) uprv_malloc(len);
    194         if(resolvedFileName==NULL){
    195             status= U_MEMORY_ALLOCATION_ERROR;
    196             return;
    197         }
    198         if(status == U_BUFFER_OVERFLOW_ERROR){
    199             status = U_ZERO_ERROR;
    200         }
    201         ucbuf_resolveFileName(sourceDir, fileName, resolvedFileName, &len, &status);
    202         ucharBuf = ucbuf_open(resolvedFileName,&encoding,TRUE,FALSE,&status);
    203 
    204         if(U_FAILURE(status)){
    205             printf("Could not open the input file %s. Error: %s\n", fileName, u_errorName(status));
    206             return;
    207         }
    208     }
    209 }
    210 
    211 ULine* UPerfTest::getLines(UErrorCode& status){
    212     if (U_FAILURE(status)) {
    213         return NULL;
    214     }
    215     if (lines != NULL) {
    216         return lines;  // don't do it again
    217     }
    218     lines     = new ULine[MAXLINES];
    219     int maxLines = MAXLINES;
    220     numLines=0;
    221     const UChar* line=NULL;
    222     int32_t len =0;
    223     for (;;) {
    224         line = ucbuf_readline(ucharBuf,&len,&status);
    225         if(line == NULL || U_FAILURE(status)){
    226             break;
    227         }
    228         lines[numLines].name  = new UChar[len];
    229         lines[numLines].len   = len;
    230         memcpy(lines[numLines].name, line, len * U_SIZEOF_UCHAR);
    231 
    232         numLines++;
    233         len = 0;
    234         if (numLines >= maxLines) {
    235             maxLines += MAXLINES;
    236             ULine *newLines = new ULine[maxLines];
    237             if(newLines == NULL) {
    238                 fprintf(stderr, "Out of memory reading line %d.\n", (int)numLines);
    239                 status= U_MEMORY_ALLOCATION_ERROR;
    240                 delete []lines;
    241                 return NULL;
    242             }
    243 
    244             memcpy(newLines, lines, numLines*sizeof(ULine));
    245             delete []lines;
    246             lines = newLines;
    247         }
    248     }
    249     return lines;
    250 }
    251 const UChar* UPerfTest::getBuffer(int32_t& len, UErrorCode& status){
    252     if (U_FAILURE(status)) {
    253         return NULL;
    254     }
    255     len = ucbuf_size(ucharBuf);
    256     buffer =  (UChar*) uprv_malloc(U_SIZEOF_UCHAR * (len+1));
    257     u_strncpy(buffer,ucbuf_getBuffer(ucharBuf,&bufferLen,&status),len);
    258     buffer[len]=0;
    259     len = bufferLen;
    260     return buffer;
    261 }
    262 UBool UPerfTest::run(){
    263     if(_remainingArgc==1){
    264         // Testing all methods
    265         return runTest();
    266     }
    267     UBool res=FALSE;
    268     // Test only the specified fucntion
    269     for (int i = 1; i < _remainingArgc; ++i) {
    270         if (_argv[i][0] != '-') {
    271             char* name = (char*) _argv[i];
    272             if(verbose==TRUE){
    273                 //fprintf(stdout, "\n=== Handling test: %s: ===\n", name);
    274                 //fprintf(stdout, "\n%s:\n", name);
    275             }
    276             char* parameter = strchr( name, '@' );
    277             if (parameter) {
    278                 *parameter = 0;
    279                 parameter += 1;
    280             }
    281             execCount = 0;
    282             res = runTest( name, parameter );
    283             if (!res || (execCount <= 0)) {
    284                 fprintf(stdout, "\n---ERROR: Test doesn't exist: %s!\n", name);
    285                 return FALSE;
    286             }
    287         }
    288     }
    289     return res;
    290 }
    291 UBool UPerfTest::runTest(char* name, char* par ){
    292     UBool rval;
    293     char* pos = NULL;
    294 
    295     if (name)
    296         pos = strchr( name, delim ); // check if name contains path (by looking for '/')
    297     if (pos) {
    298         path = pos+1;   // store subpath for calling subtest
    299         *pos = 0;       // split into two strings
    300     }else{
    301         path = NULL;
    302     }
    303 
    304     if (!name || (name[0] == 0) || (strcmp(name, "*") == 0)) {
    305         rval = runTestLoop( NULL, NULL );
    306 
    307     }else if (strcmp( name, "LIST" ) == 0) {
    308         this->usage();
    309         rval = TRUE;
    310 
    311     }else{
    312         rval = runTestLoop( name, par );
    313     }
    314 
    315     if (pos)
    316         *pos = delim;  // restore original value at pos
    317     return rval;
    318 }
    319 
    320 
    321 void UPerfTest::setPath( char* pathVal )
    322 {
    323     this->path = pathVal;
    324 }
    325 
    326 // call individual tests, to be overridden to call implementations
    327 UPerfFunction* UPerfTest::runIndexedTest( int32_t /*index*/, UBool /*exec*/, const char* & /*name*/, char* /*par*/ )
    328 {
    329     // to be overridden by a method like:
    330     /*
    331     switch (index) {
    332         case 0: name = "First Test"; if (exec) FirstTest( par ); break;
    333         case 1: name = "Second Test"; if (exec) SecondTest( par ); break;
    334         default: name = ""; break;
    335     }
    336     */
    337     fprintf(stderr,"*** runIndexedTest needs to be overridden! ***");
    338     return NULL;
    339 }
    340 
    341 
    342 UBool UPerfTest::runTestLoop( char* testname, char* par )
    343 {
    344     int32_t    index = 0;
    345     const char*   name;
    346     UBool  run_this_test;
    347     UBool  rval = FALSE;
    348     UErrorCode status = U_ZERO_ERROR;
    349     UPerfTest* saveTest = gTest;
    350     gTest = this;
    351     int32_t loops = 0;
    352     double t=0;
    353     int32_t n = 1;
    354     long ops;
    355     do {
    356         this->runIndexedTest( index, FALSE, name );
    357         if (!name || (name[0] == 0))
    358             break;
    359         if (!testname) {
    360             run_this_test = TRUE;
    361         }else{
    362             run_this_test = (UBool) (strcmp( name, testname ) == 0);
    363         }
    364         if (run_this_test) {
    365             UPerfFunction* testFunction = this->runIndexedTest( index, TRUE, name, par );
    366             execCount++;
    367             rval=TRUE;
    368             if(testFunction==NULL){
    369                 fprintf(stderr,"%s function returned NULL", name);
    370                 return FALSE;
    371             }
    372             ops = testFunction->getOperationsPerIteration();
    373             if (ops < 1) {
    374                 fprintf(stderr, "%s returned an illegal operations/iteration()\n", name);
    375                 return FALSE;
    376             }
    377             if(iterations == 0) {
    378                 n = time;
    379                 // Run for specified duration in seconds
    380                 if(verbose==TRUE){
    381                     fprintf(stdout,"= %s calibrating %i seconds \n", name, (int)n);
    382                 }
    383 
    384                 //n *=  1000; // s => ms
    385                 //System.out.println("# " + meth.getName() + " " + n + " sec");
    386                 int32_t failsafe = 1; // last resort for very fast methods
    387                 t = 0;
    388                 while (t < (int)(n * 0.9)) { // 90% is close enough
    389                     if (loops == 0 || t == 0) {
    390                         loops = failsafe;
    391                         failsafe *= 10;
    392                     } else {
    393                         //System.out.println("# " + meth.getName() + " x " + loops + " = " + t);
    394                         loops = (int)((double)n / t * loops + 0.5);
    395                         if (loops == 0) {
    396                             fprintf(stderr,"Unable to converge on desired duration");
    397                             return FALSE;
    398                         }
    399                     }
    400                     //System.out.println("# " + meth.getName() + " x " + loops);
    401                     t = testFunction->time(loops,&status);
    402                     if(U_FAILURE(status)){
    403                         printf("Performance test failed with error: %s \n", u_errorName(status));
    404                         break;
    405                     }
    406                 }
    407             } else {
    408                 loops = iterations;
    409             }
    410 
    411             double min_t=1000000.0, sum_t=0.0;
    412             long events = -1;
    413 
    414             for(int32_t ps =0; ps < passes; ps++){
    415                 fprintf(stdout,"= %s begin " ,name);
    416                 if(verbose==TRUE){
    417                     if(iterations > 0) {
    418                         fprintf(stdout, "%i\n", (int)loops);
    419                     } else {
    420                         fprintf(stdout, "%i\n", (int)n);
    421                     }
    422                 } else {
    423                     fprintf(stdout, "\n");
    424                 }
    425                 t = testFunction->time(loops, &status);
    426                 if(U_FAILURE(status)){
    427                     printf("Performance test failed with error: %s \n", u_errorName(status));
    428                     break;
    429                 }
    430                 sum_t+=t;
    431                 if(t<min_t) {
    432                     min_t=t;
    433                 }
    434                 events = testFunction->getEventsPerIteration();
    435                 //print info only in verbose mode
    436                 if(verbose==TRUE){
    437                     if(events == -1){
    438                         fprintf(stdout, "= %s end: %f loops: %i operations: %li \n", name, t, (int)loops, ops);
    439                     }else{
    440                         fprintf(stdout, "= %s end: %f loops: %i operations: %li events: %li\n", name, t, (int)loops, ops, events);
    441                     }
    442                 }else{
    443                     if(events == -1){
    444                         fprintf(stdout,"= %s end %f %i %li\n", name, t, (int)loops, ops);
    445                     }else{
    446                         fprintf(stdout,"= %s end %f %i %li %li\n", name, t, (int)loops, ops, events);
    447                     }
    448                 }
    449             }
    450             if(verbose && U_SUCCESS(status)) {
    451                 double avg_t = sum_t/passes;
    452                 if (loops == 0 || ops == 0) {
    453                     fprintf(stderr, "%s did not run\n", name);
    454                 }
    455                 else if(events == -1) {
    456                     fprintf(stdout, "%%= %s avg: %.4g loops: %i avg/op: %.4g ns\n",
    457                             name, avg_t, (int)loops, (avg_t*1E9)/(loops*ops));
    458                     fprintf(stdout, "_= %s min: %.4g loops: %i min/op: %.4g ns\n",
    459                             name, min_t, (int)loops, (min_t*1E9)/(loops*ops));
    460                 }
    461                 else {
    462                     fprintf(stdout, "%%= %s avg: %.4g loops: %i avg/op: %.4g ns avg/event: %.4g ns\n",
    463                             name, avg_t, (int)loops, (avg_t*1E9)/(loops*ops), (avg_t*1E9)/(loops*events));
    464                     fprintf(stdout, "_= %s min: %.4g loops: %i min/op: %.4g ns min/event: %.4g ns\n",
    465                             name, min_t, (int)loops, (min_t*1E9)/(loops*ops), (min_t*1E9)/(loops*events));
    466                 }
    467             }
    468             delete testFunction;
    469         }
    470         index++;
    471     }while(name);
    472 
    473     gTest = saveTest;
    474     return rval;
    475 }
    476 
    477 /**
    478 * Print a usage message for this test class.
    479 */
    480 void UPerfTest::usage( void )
    481 {
    482     puts(gUsageString);
    483     if (_addUsage != NULL) {
    484         puts(_addUsage);
    485     }
    486 
    487     UBool save_verbose = verbose;
    488     verbose = TRUE;
    489     fprintf(stdout,"Test names:\n");
    490     fprintf(stdout,"-----------\n");
    491 
    492     int32_t index = 0;
    493     const char* name = NULL;
    494     do{
    495         this->runIndexedTest( index, FALSE, name );
    496         if (!name)
    497             break;
    498         fprintf(stdout, "%s\n", name);
    499         index++;
    500     }while (name && (name[0] != 0));
    501     verbose = save_verbose;
    502 }
    503 
    504 
    505 
    506 
    507 void UPerfTest::setCaller( UPerfTest* callingTest )
    508 {
    509     caller = callingTest;
    510     if (caller) {
    511         verbose = caller->verbose;
    512     }
    513 }
    514 
    515 UBool UPerfTest::callTest( UPerfTest& testToBeCalled, char* par )
    516 {
    517     execCount--; // correct a previously assumed test-exec, as this only calls a subtest
    518     testToBeCalled.setCaller( this );
    519     return testToBeCalled.runTest( path, par );
    520 }
    521 
    522 UPerfTest::~UPerfTest(){
    523     if(lines!=NULL){
    524         delete[] lines;
    525     }
    526     if(buffer!=NULL){
    527         uprv_free(buffer);
    528     }
    529     if(resolvedFileName!=NULL){
    530         uprv_free(resolvedFileName);
    531     }
    532     ucbuf_close(ucharBuf);
    533 }
    534 
    535 #endif
    536