tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

uperf.cpp (17679B)


      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 = nullptr;
     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(nullptr),
     85          ucharBuf(nullptr), encoding(""),
     86          uselen(false),
     87          fileName(nullptr), sourceDir("."),
     88          lines(nullptr), numLines(0), line_mode(true),
     89          buffer(nullptr), bufferLen(0),
     90          verbose(false), bulk_mode(false),
     91          passes(1), iterations(0), time(0),
     92          locale(nullptr) {
     93    init(nullptr, 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(nullptr), encoding(""),
    102          uselen(false),
    103          fileName(nullptr), sourceDir("."),
    104          lines(nullptr), numLines(0), line_mode(true),
    105          buffer(nullptr), bufferLen(0),
    106          verbose(false), bulk_mode(false),
    107          passes(1), iterations(0), time(0),
    108          locale(nullptr) {
    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 = nullptr;
    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, const_cast<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!=nullptr){
    191        //pre-flight
    192        UErrorCode bufferStatus = U_ZERO_ERROR;
    193        ucbuf_resolveFileName(sourceDir, fileName, nullptr, &len, &bufferStatus);
    194        resolvedFileName = static_cast<char*>(uprv_malloc(len));
    195        if(resolvedFileName==nullptr){
    196            status= U_MEMORY_ALLOCATION_ERROR;
    197            return;
    198        }
    199        if(bufferStatus == U_BUFFER_OVERFLOW_ERROR){
    200            bufferStatus = U_ZERO_ERROR;
    201        }
    202        ucbuf_resolveFileName(sourceDir, fileName, resolvedFileName, &len, &bufferStatus);
    203        if (U_FAILURE(bufferStatus)) {
    204            status = bufferStatus;
    205        }
    206        ucharBuf = ucbuf_open(resolvedFileName,&encoding,true,false,&status);
    207 
    208        if(U_FAILURE(status)){
    209            printf("Could not open the input file %s. Error: %s\n", fileName, u_errorName(status));
    210            return;
    211        }
    212    }
    213 }
    214 
    215 ULine* UPerfTest::getLines(UErrorCode& status){
    216    if (U_FAILURE(status)) {
    217        return nullptr;
    218    }
    219    if (lines != nullptr) {
    220        return lines;  // don't do it again
    221    }
    222    lines     = new ULine[MAXLINES];
    223    int maxLines = MAXLINES;
    224    numLines=0;
    225    const char16_t* line=nullptr;
    226    int32_t len =0;
    227    for (;;) {
    228        line = ucbuf_readline(ucharBuf,&len,&status);
    229        if(line == nullptr || U_FAILURE(status)){
    230            break;
    231        }
    232        lines[numLines].name  = new char16_t[len];
    233        lines[numLines].len   = len;
    234        memcpy(lines[numLines].name, line, len * U_SIZEOF_UCHAR);
    235 
    236        numLines++;
    237        len = 0;
    238        if (numLines >= maxLines) {
    239            maxLines += MAXLINES;
    240            ULine *newLines = new ULine[maxLines];
    241            if(newLines == nullptr) {
    242                fprintf(stderr, "Out of memory reading line %d.\n", static_cast<int>(numLines));
    243                status= U_MEMORY_ALLOCATION_ERROR;
    244                delete []lines;
    245                return nullptr;
    246            }
    247 
    248            memcpy(newLines, lines, numLines*sizeof(ULine));
    249            delete []lines;
    250            lines = newLines;
    251        }
    252    }
    253    return lines;
    254 }
    255 const char16_t* UPerfTest::getBuffer(int32_t& len, UErrorCode& status){
    256    if (U_FAILURE(status)) {
    257        return nullptr;
    258    }
    259    len = ucbuf_size(ucharBuf);
    260    buffer = static_cast<char16_t*>(uprv_malloc(U_SIZEOF_UCHAR * (len + 1)));
    261    u_strncpy(buffer,ucbuf_getBuffer(ucharBuf,&bufferLen,&status),len);
    262    buffer[len]=0;
    263    len = bufferLen;
    264    return buffer;
    265 }
    266 UBool UPerfTest::run(){
    267    if(_remainingArgc==1){
    268        // Testing all methods
    269        return runTest();
    270    }
    271    UBool res=false;
    272    // Test only the specified function
    273    for (int i = 1; i < _remainingArgc; ++i) {
    274        if (_argv[i][0] != '-') {
    275            char* name = const_cast<char*>(_argv[i]);
    276            if(verbose==true){
    277                //fprintf(stdout, "\n=== Handling test: %s: ===\n", name);
    278                //fprintf(stdout, "\n%s:\n", name);
    279            }
    280            char* parameter = strchr( name, '@' );
    281            if (parameter) {
    282                *parameter = 0;
    283                parameter += 1;
    284            }
    285            execCount = 0;
    286            res = runTest( name, parameter );
    287            if (!res || (execCount <= 0)) {
    288                fprintf(stdout, "\n---ERROR: Test doesn't exist: %s!\n", name);
    289                return false;
    290            }
    291        }
    292    }
    293    return res;
    294 }
    295 UBool UPerfTest::runTest(char* name, char* par ){
    296    UBool rval;
    297    char* pos = nullptr;
    298 
    299    if (name)
    300        pos = strchr( name, delim ); // check if name contains path (by looking for '/')
    301    if (pos) {
    302        path = pos+1;   // store subpath for calling subtest
    303        *pos = 0;       // split into two strings
    304    }else{
    305        path = nullptr;
    306    }
    307 
    308    if (!name || (name[0] == 0) || (strcmp(name, "*") == 0)) {
    309        rval = runTestLoop( nullptr, nullptr );
    310 
    311    }else if (strcmp( name, "LIST" ) == 0) {
    312        this->usage();
    313        rval = true;
    314 
    315    }else{
    316        rval = runTestLoop( name, par );
    317    }
    318 
    319    if (pos)
    320        *pos = delim;  // restore original value at pos
    321    return rval;
    322 }
    323 
    324 
    325 void UPerfTest::setPath( char* pathVal )
    326 {
    327    this->path = pathVal;
    328 }
    329 
    330 // call individual tests, to be overridden to call implementations
    331 UPerfFunction* UPerfTest::runIndexedTest( int32_t /*index*/, UBool /*exec*/, const char* & /*name*/, char* /*par*/ )
    332 {
    333    // to be overridden by a method like:
    334    /*
    335    switch (index) {
    336        case 0: name = "First Test"; if (exec) FirstTest( par ); break;
    337        case 1: name = "Second Test"; if (exec) SecondTest( par ); break;
    338        default: name = ""; break;
    339    }
    340    */
    341    fprintf(stderr,"*** runIndexedTest needs to be overridden! ***");
    342    return nullptr;
    343 }
    344 
    345 
    346 UBool UPerfTest::runTestLoop( char* testname, char* par )
    347 {
    348    int32_t    index = 0;
    349    const char*   name;
    350    UBool  run_this_test;
    351    UBool  rval = false;
    352    UErrorCode status = U_ZERO_ERROR;
    353    UPerfTest* saveTest = gTest;
    354    gTest = this;
    355    int32_t loops = 0;
    356    double t=0;
    357    int32_t n = 1;
    358    long ops;
    359    do {
    360        this->runIndexedTest( index, false, name );
    361        if (!name || (name[0] == 0))
    362            break;
    363        if (!testname) {
    364            run_this_test = true;
    365        }else{
    366            run_this_test = static_cast<UBool>(strcmp(name, testname) == 0);
    367        }
    368        if (run_this_test) {
    369            UPerfFunction* testFunction = this->runIndexedTest( index, true, name, par );
    370            execCount++;
    371            rval=true;
    372            if(testFunction==nullptr){
    373                fprintf(stderr,"%s function returned nullptr", name);
    374                return false;
    375            }
    376            ops = testFunction->getOperationsPerIteration();
    377            if (ops < 1) {
    378                fprintf(stderr, "%s returned an illegal operations/iteration()\n", name);
    379                return false;
    380            }
    381            if(iterations == 0) {
    382                n = time;
    383                // Run for specified duration in seconds
    384                if(verbose==true){
    385                    fprintf(stdout, "= %s calibrating %i seconds \n", name, static_cast<int>(n));
    386                }
    387 
    388                //n *=  1000; // s => ms
    389                //System.out.println("# " + meth.getName() + " " + n + " sec");
    390                int32_t failsafe = 1; // last resort for very fast methods
    391                t = 0;
    392                while (t < static_cast<int>(n * 0.9)) { // 90% is close enough
    393                    if (loops == 0 || t == 0) {
    394                        loops = failsafe;
    395                        failsafe *= 10;
    396                    } else {
    397                        //System.out.println("# " + meth.getName() + " x " + loops + " = " + t);
    398                        loops = static_cast<int>(static_cast<double>(n) / t * loops + 0.5);
    399                        if (loops == 0) {
    400                            fprintf(stderr,"Unable to converge on desired duration");
    401                            return false;
    402                        }
    403                    }
    404                    //System.out.println("# " + meth.getName() + " x " + loops);
    405                    t = testFunction->time(loops,&status);
    406                    if(U_FAILURE(status)){
    407                        printf("Performance test failed with error: %s \n", u_errorName(status));
    408                        break;
    409                    }
    410                }
    411            } else {
    412                loops = iterations;
    413            }
    414 
    415            double min_t=1000000.0, sum_t=0.0;
    416            long events = -1;
    417 
    418            for(int32_t ps =0; ps < passes; ps++){
    419                if(verbose==true){
    420                    fprintf(stdout,"= %s begin " ,name);
    421                    if(iterations > 0) {
    422                        fprintf(stdout, "%i\n", static_cast<int>(loops));
    423                    } else {
    424                        fprintf(stdout, "%i\n", static_cast<int>(n));
    425                    }
    426                }
    427                t = testFunction->time(loops, &status);
    428                if(U_FAILURE(status)){
    429                    printf("Performance test failed with error: %s \n", u_errorName(status));
    430                    break;
    431                }
    432                sum_t+=t;
    433                if(t<min_t) {
    434                    min_t=t;
    435                }
    436                events = testFunction->getEventsPerIteration();
    437                //print info only in verbose mode
    438                if(verbose==true){
    439                    if(events == -1){
    440                        fprintf(stdout, "= %s end: %f loops: %i operations: %li \n", name, t, static_cast<int>(loops), ops);
    441                    }else{
    442                        fprintf(stdout, "= %s end: %f loops: %i operations: %li events: %li\n", name, t, static_cast<int>(loops), ops, events);
    443                    }
    444                }
    445            }
    446            if(verbose && U_SUCCESS(status)) {
    447                double avg_t = sum_t/passes;
    448                if (loops == 0 || ops == 0) {
    449                    fprintf(stderr, "%s did not run\n", name);
    450                }
    451                else if(events == -1) {
    452                    fprintf(stdout, "%%= %s avg: %.4g loops: %i avg/op: %.4g ns\n",
    453                            name, avg_t, static_cast<int>(loops), (avg_t * 1E9) / (loops * ops));
    454                    fprintf(stdout, "_= %s min: %.4g loops: %i min/op: %.4g ns\n",
    455                            name, min_t, static_cast<int>(loops), (min_t * 1E9) / (loops * ops));
    456                }
    457                else {
    458                    fprintf(stdout, "%%= %s avg: %.4g loops: %i avg/op: %.4g ns avg/event: %.4g ns\n",
    459                            name, avg_t, static_cast<int>(loops), (avg_t * 1E9) / (loops * ops), (avg_t * 1E9) / (loops * events));
    460                    fprintf(stdout, "_= %s min: %.4g loops: %i min/op: %.4g ns min/event: %.4g ns\n",
    461                            name, min_t, static_cast<int>(loops), (min_t * 1E9) / (loops * ops), (min_t * 1E9) / (loops * events));
    462                }
    463            }
    464            else if(U_SUCCESS(status)) {
    465                // Print results in ndjson format for GHA Benchmark to process.
    466                fprintf(stdout,
    467                        "{\"biggerIsBetter\":false,\"name\":\"%s\",\"unit\":\"ns/iter\",\"value\":%.4f}\n",
    468                        name, (min_t*1E9)/(loops*ops));
    469            }
    470            delete testFunction;
    471        }
    472        index++;
    473    }while(name);
    474 
    475    gTest = saveTest;
    476    return rval;
    477 }
    478 
    479 /**
    480 * Print a usage message for this test class.
    481 */
    482 void UPerfTest::usage()
    483 {
    484    puts(gUsageString);
    485    if (_addUsage != nullptr) {
    486        puts(_addUsage);
    487    }
    488 
    489    UBool save_verbose = verbose;
    490    verbose = true;
    491    fprintf(stdout,"Test names:\n");
    492    fprintf(stdout,"-----------\n");
    493 
    494    int32_t index = 0;
    495    const char* name = nullptr;
    496    do{
    497        this->runIndexedTest( index, false, name );
    498        if (!name)
    499            break;
    500        fprintf(stdout, "%s\n", name);
    501        index++;
    502    }while (name && (name[0] != 0));
    503    verbose = save_verbose;
    504 }
    505 
    506 
    507 
    508 
    509 void UPerfTest::setCaller( UPerfTest* callingTest )
    510 {
    511    caller = callingTest;
    512    if (caller) {
    513        verbose = caller->verbose;
    514    }
    515 }
    516 
    517 UBool UPerfTest::callTest( UPerfTest& testToBeCalled, char* par )
    518 {
    519    execCount--; // correct a previously assumed test-exec, as this only calls a subtest
    520    testToBeCalled.setCaller( this );
    521    return testToBeCalled.runTest( path, par );
    522 }
    523 
    524 UPerfTest::~UPerfTest(){
    525    delete[] lines;
    526    if(buffer!=nullptr){
    527        uprv_free(buffer);
    528    }
    529    if(resolvedFileName!=nullptr){
    530        uprv_free(resolvedFileName);
    531    }
    532    ucbuf_close(ucharBuf);
    533 }
    534 
    535 #endif