1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/intl/icu/source/tools/ctestfw/uperf.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,533 @@ 1.4 +/******************************************************************** 1.5 + * COPYRIGHT: 1.6 + * Copyright (c) 2002-2012, International Business Machines Corporation and 1.7 + * others. All Rights Reserved. 1.8 + ********************************************************************/ 1.9 + 1.10 +// Defines _XOPEN_SOURCE for access to POSIX functions. 1.11 +// Must be before any other #includes. 1.12 +#include "uposixdefs.h" 1.13 + 1.14 +#include "unicode/uperf.h" 1.15 +#include "uoptions.h" 1.16 +#include "cmemory.h" 1.17 +#include <stdio.h> 1.18 +#include <stdlib.h> 1.19 + 1.20 +#if !UCONFIG_NO_CONVERSION 1.21 + 1.22 +UPerfFunction::~UPerfFunction() {} 1.23 + 1.24 +static const char delim = '/'; 1.25 +static int32_t execCount = 0; 1.26 +UPerfTest* UPerfTest::gTest = NULL; 1.27 +static const int MAXLINES = 40000; 1.28 +const char UPerfTest::gUsageString[] = 1.29 + "Usage: %s [OPTIONS] [FILES]\n" 1.30 + "\tReads the input file and prints out time taken in seconds\n" 1.31 + "Options:\n" 1.32 + "\t-h or -? or --help this usage text\n" 1.33 + "\t-v or --verbose print extra information when processing files\n" 1.34 + "\t-s or --sourcedir source directory for files followed by path\n" 1.35 + "\t followed by path\n" 1.36 + "\t-e or --encoding encoding of source files\n" 1.37 + "\t-u or --uselen perform timing analysis on non-null terminated buffer using length\n" 1.38 + "\t-f or --file-name file to be used as input data\n" 1.39 + "\t-p or --passes Number of passes to be performed. Requires Numeric argument.\n" 1.40 + "\t Cannot be used with --time\n" 1.41 + "\t-i or --iterations Number of iterations to be performed. Requires Numeric argument\n" 1.42 + "\t-t or --time Threshold time for looping until in seconds. Requires Numeric argument.\n" 1.43 + "\t Cannot be used with --iterations\n" 1.44 + "\t-l or --line-mode The data file should be processed in line mode\n" 1.45 + "\t-b or --bulk-mode The data file should be processed in file based.\n" 1.46 + "\t Cannot be used with --line-mode\n" 1.47 + "\t-L or --locale Locale for the test\n"; 1.48 + 1.49 +enum 1.50 +{ 1.51 + HELP1, 1.52 + HELP2, 1.53 + VERBOSE, 1.54 + SOURCEDIR, 1.55 + ENCODING, 1.56 + USELEN, 1.57 + FILE_NAME, 1.58 + PASSES, 1.59 + ITERATIONS, 1.60 + TIME, 1.61 + LINE_MODE, 1.62 + BULK_MODE, 1.63 + LOCALE, 1.64 + OPTIONS_COUNT 1.65 +}; 1.66 + 1.67 + 1.68 +static UOption options[OPTIONS_COUNT+20]={ 1.69 + UOPTION_HELP_H, 1.70 + UOPTION_HELP_QUESTION_MARK, 1.71 + UOPTION_VERBOSE, 1.72 + UOPTION_SOURCEDIR, 1.73 + UOPTION_ENCODING, 1.74 + UOPTION_DEF( "uselen", 'u', UOPT_NO_ARG), 1.75 + UOPTION_DEF( "file-name", 'f', UOPT_REQUIRES_ARG), 1.76 + UOPTION_DEF( "passes", 'p', UOPT_REQUIRES_ARG), 1.77 + UOPTION_DEF( "iterations", 'i', UOPT_REQUIRES_ARG), 1.78 + UOPTION_DEF( "time", 't', UOPT_REQUIRES_ARG), 1.79 + UOPTION_DEF( "line-mode", 'l', UOPT_NO_ARG), 1.80 + UOPTION_DEF( "bulk-mode", 'b', UOPT_NO_ARG), 1.81 + UOPTION_DEF( "locale", 'L', UOPT_REQUIRES_ARG) 1.82 +}; 1.83 + 1.84 +UPerfTest::UPerfTest(int32_t argc, const char* argv[], UErrorCode& status) 1.85 + : _argc(argc), _argv(argv), _addUsage(NULL), 1.86 + ucharBuf(NULL), encoding(""), 1.87 + uselen(FALSE), 1.88 + fileName(NULL), sourceDir("."), 1.89 + lines(NULL), numLines(0), line_mode(TRUE), 1.90 + buffer(NULL), bufferLen(0), 1.91 + verbose(FALSE), bulk_mode(FALSE), 1.92 + passes(1), iterations(0), time(0), 1.93 + locale(NULL) { 1.94 + init(NULL, 0, status); 1.95 +} 1.96 + 1.97 +UPerfTest::UPerfTest(int32_t argc, const char* argv[], 1.98 + UOption addOptions[], int32_t addOptionsCount, 1.99 + const char *addUsage, 1.100 + UErrorCode& status) 1.101 + : _argc(argc), _argv(argv), _addUsage(addUsage), 1.102 + ucharBuf(NULL), encoding(""), 1.103 + uselen(FALSE), 1.104 + fileName(NULL), sourceDir("."), 1.105 + lines(NULL), numLines(0), line_mode(TRUE), 1.106 + buffer(NULL), bufferLen(0), 1.107 + verbose(FALSE), bulk_mode(FALSE), 1.108 + passes(1), iterations(0), time(0), 1.109 + locale(NULL) { 1.110 + init(addOptions, addOptionsCount, status); 1.111 +} 1.112 + 1.113 +void UPerfTest::init(UOption addOptions[], int32_t addOptionsCount, 1.114 + UErrorCode& status) { 1.115 + //initialize the argument list 1.116 + U_MAIN_INIT_ARGS(_argc, _argv); 1.117 + 1.118 + resolvedFileName = NULL; 1.119 + 1.120 + // add specific options 1.121 + int32_t optionsCount = OPTIONS_COUNT; 1.122 + if (addOptionsCount > 0) { 1.123 + memcpy(options+optionsCount, addOptions, addOptionsCount*sizeof(UOption)); 1.124 + optionsCount += addOptionsCount; 1.125 + } 1.126 + 1.127 + //parse the arguments 1.128 + _remainingArgc = u_parseArgs(_argc, (char**)_argv, optionsCount, options); 1.129 + 1.130 + // copy back values for additional options 1.131 + if (addOptionsCount > 0) { 1.132 + memcpy(addOptions, options+OPTIONS_COUNT, addOptionsCount*sizeof(UOption)); 1.133 + } 1.134 + 1.135 + // Now setup the arguments 1.136 + if(_argc==1 || options[HELP1].doesOccur || options[HELP2].doesOccur) { 1.137 + status = U_ILLEGAL_ARGUMENT_ERROR; 1.138 + return; 1.139 + } 1.140 + 1.141 + if(options[VERBOSE].doesOccur) { 1.142 + verbose = TRUE; 1.143 + } 1.144 + 1.145 + if(options[SOURCEDIR].doesOccur) { 1.146 + sourceDir = options[SOURCEDIR].value; 1.147 + } 1.148 + 1.149 + if(options[ENCODING].doesOccur) { 1.150 + encoding = options[ENCODING].value; 1.151 + } 1.152 + 1.153 + if(options[USELEN].doesOccur) { 1.154 + uselen = TRUE; 1.155 + } 1.156 + 1.157 + if(options[FILE_NAME].doesOccur){ 1.158 + fileName = options[FILE_NAME].value; 1.159 + } 1.160 + 1.161 + if(options[PASSES].doesOccur) { 1.162 + passes = atoi(options[PASSES].value); 1.163 + } 1.164 + if(options[ITERATIONS].doesOccur) { 1.165 + iterations = atoi(options[ITERATIONS].value); 1.166 + if(options[TIME].doesOccur) { 1.167 + status = U_ILLEGAL_ARGUMENT_ERROR; 1.168 + return; 1.169 + } 1.170 + } else if(options[TIME].doesOccur) { 1.171 + time = atoi(options[TIME].value); 1.172 + } else { 1.173 + iterations = 1000; // some default 1.174 + } 1.175 + 1.176 + if(options[LINE_MODE].doesOccur) { 1.177 + line_mode = TRUE; 1.178 + bulk_mode = FALSE; 1.179 + } 1.180 + 1.181 + if(options[BULK_MODE].doesOccur) { 1.182 + bulk_mode = TRUE; 1.183 + line_mode = FALSE; 1.184 + } 1.185 + 1.186 + if(options[LOCALE].doesOccur) { 1.187 + locale = options[LOCALE].value; 1.188 + } 1.189 + 1.190 + int32_t len = 0; 1.191 + if(fileName!=NULL){ 1.192 + //pre-flight 1.193 + ucbuf_resolveFileName(sourceDir, fileName, NULL, &len, &status); 1.194 + resolvedFileName = (char*) uprv_malloc(len); 1.195 + if(resolvedFileName==NULL){ 1.196 + status= U_MEMORY_ALLOCATION_ERROR; 1.197 + return; 1.198 + } 1.199 + if(status == U_BUFFER_OVERFLOW_ERROR){ 1.200 + status = U_ZERO_ERROR; 1.201 + } 1.202 + ucbuf_resolveFileName(sourceDir, fileName, resolvedFileName, &len, &status); 1.203 + ucharBuf = ucbuf_open(resolvedFileName,&encoding,TRUE,FALSE,&status); 1.204 + 1.205 + if(U_FAILURE(status)){ 1.206 + printf("Could not open the input file %s. Error: %s\n", fileName, u_errorName(status)); 1.207 + return; 1.208 + } 1.209 + } 1.210 +} 1.211 + 1.212 +ULine* UPerfTest::getLines(UErrorCode& status){ 1.213 + if (U_FAILURE(status)) { 1.214 + return NULL; 1.215 + } 1.216 + if (lines != NULL) { 1.217 + return lines; // don't do it again 1.218 + } 1.219 + lines = new ULine[MAXLINES]; 1.220 + int maxLines = MAXLINES; 1.221 + numLines=0; 1.222 + const UChar* line=NULL; 1.223 + int32_t len =0; 1.224 + for (;;) { 1.225 + line = ucbuf_readline(ucharBuf,&len,&status); 1.226 + if(line == NULL || U_FAILURE(status)){ 1.227 + break; 1.228 + } 1.229 + lines[numLines].name = new UChar[len]; 1.230 + lines[numLines].len = len; 1.231 + memcpy(lines[numLines].name, line, len * U_SIZEOF_UCHAR); 1.232 + 1.233 + numLines++; 1.234 + len = 0; 1.235 + if (numLines >= maxLines) { 1.236 + maxLines += MAXLINES; 1.237 + ULine *newLines = new ULine[maxLines]; 1.238 + if(newLines == NULL) { 1.239 + fprintf(stderr, "Out of memory reading line %d.\n", (int)numLines); 1.240 + status= U_MEMORY_ALLOCATION_ERROR; 1.241 + delete []lines; 1.242 + return NULL; 1.243 + } 1.244 + 1.245 + memcpy(newLines, lines, numLines*sizeof(ULine)); 1.246 + delete []lines; 1.247 + lines = newLines; 1.248 + } 1.249 + } 1.250 + return lines; 1.251 +} 1.252 +const UChar* UPerfTest::getBuffer(int32_t& len, UErrorCode& status){ 1.253 + if (U_FAILURE(status)) { 1.254 + return NULL; 1.255 + } 1.256 + len = ucbuf_size(ucharBuf); 1.257 + buffer = (UChar*) uprv_malloc(U_SIZEOF_UCHAR * (len+1)); 1.258 + u_strncpy(buffer,ucbuf_getBuffer(ucharBuf,&bufferLen,&status),len); 1.259 + buffer[len]=0; 1.260 + len = bufferLen; 1.261 + return buffer; 1.262 +} 1.263 +UBool UPerfTest::run(){ 1.264 + if(_remainingArgc==1){ 1.265 + // Testing all methods 1.266 + return runTest(); 1.267 + } 1.268 + UBool res=FALSE; 1.269 + // Test only the specified fucntion 1.270 + for (int i = 1; i < _remainingArgc; ++i) { 1.271 + if (_argv[i][0] != '-') { 1.272 + char* name = (char*) _argv[i]; 1.273 + if(verbose==TRUE){ 1.274 + //fprintf(stdout, "\n=== Handling test: %s: ===\n", name); 1.275 + //fprintf(stdout, "\n%s:\n", name); 1.276 + } 1.277 + char* parameter = strchr( name, '@' ); 1.278 + if (parameter) { 1.279 + *parameter = 0; 1.280 + parameter += 1; 1.281 + } 1.282 + execCount = 0; 1.283 + res = runTest( name, parameter ); 1.284 + if (!res || (execCount <= 0)) { 1.285 + fprintf(stdout, "\n---ERROR: Test doesn't exist: %s!\n", name); 1.286 + return FALSE; 1.287 + } 1.288 + } 1.289 + } 1.290 + return res; 1.291 +} 1.292 +UBool UPerfTest::runTest(char* name, char* par ){ 1.293 + UBool rval; 1.294 + char* pos = NULL; 1.295 + 1.296 + if (name) 1.297 + pos = strchr( name, delim ); // check if name contains path (by looking for '/') 1.298 + if (pos) { 1.299 + path = pos+1; // store subpath for calling subtest 1.300 + *pos = 0; // split into two strings 1.301 + }else{ 1.302 + path = NULL; 1.303 + } 1.304 + 1.305 + if (!name || (name[0] == 0) || (strcmp(name, "*") == 0)) { 1.306 + rval = runTestLoop( NULL, NULL ); 1.307 + 1.308 + }else if (strcmp( name, "LIST" ) == 0) { 1.309 + this->usage(); 1.310 + rval = TRUE; 1.311 + 1.312 + }else{ 1.313 + rval = runTestLoop( name, par ); 1.314 + } 1.315 + 1.316 + if (pos) 1.317 + *pos = delim; // restore original value at pos 1.318 + return rval; 1.319 +} 1.320 + 1.321 + 1.322 +void UPerfTest::setPath( char* pathVal ) 1.323 +{ 1.324 + this->path = pathVal; 1.325 +} 1.326 + 1.327 +// call individual tests, to be overriden to call implementations 1.328 +UPerfFunction* UPerfTest::runIndexedTest( int32_t /*index*/, UBool /*exec*/, const char* & /*name*/, char* /*par*/ ) 1.329 +{ 1.330 + // to be overriden by a method like: 1.331 + /* 1.332 + switch (index) { 1.333 + case 0: name = "First Test"; if (exec) FirstTest( par ); break; 1.334 + case 1: name = "Second Test"; if (exec) SecondTest( par ); break; 1.335 + default: name = ""; break; 1.336 + } 1.337 + */ 1.338 + fprintf(stderr,"*** runIndexedTest needs to be overriden! ***"); 1.339 + return NULL; 1.340 +} 1.341 + 1.342 + 1.343 +UBool UPerfTest::runTestLoop( char* testname, char* par ) 1.344 +{ 1.345 + int32_t index = 0; 1.346 + const char* name; 1.347 + UBool run_this_test; 1.348 + UBool rval = FALSE; 1.349 + UErrorCode status = U_ZERO_ERROR; 1.350 + UPerfTest* saveTest = gTest; 1.351 + gTest = this; 1.352 + int32_t loops = 0; 1.353 + double t=0; 1.354 + int32_t n = 1; 1.355 + long ops; 1.356 + do { 1.357 + this->runIndexedTest( index, FALSE, name ); 1.358 + if (!name || (name[0] == 0)) 1.359 + break; 1.360 + if (!testname) { 1.361 + run_this_test = TRUE; 1.362 + }else{ 1.363 + run_this_test = (UBool) (strcmp( name, testname ) == 0); 1.364 + } 1.365 + if (run_this_test) { 1.366 + UPerfFunction* testFunction = this->runIndexedTest( index, TRUE, name, par ); 1.367 + execCount++; 1.368 + rval=TRUE; 1.369 + if(testFunction==NULL){ 1.370 + fprintf(stderr,"%s function returned NULL", name); 1.371 + return FALSE; 1.372 + } 1.373 + ops = testFunction->getOperationsPerIteration(); 1.374 + if (ops < 1) { 1.375 + fprintf(stderr, "%s returned an illegal operations/iteration()\n", name); 1.376 + return FALSE; 1.377 + } 1.378 + if(iterations == 0) { 1.379 + n = time; 1.380 + // Run for specified duration in seconds 1.381 + if(verbose==TRUE){ 1.382 + fprintf(stdout,"= %s calibrating %i seconds \n", name, (int)n); 1.383 + } 1.384 + 1.385 + //n *= 1000; // s => ms 1.386 + //System.out.println("# " + meth.getName() + " " + n + " sec"); 1.387 + int32_t failsafe = 1; // last resort for very fast methods 1.388 + t = 0; 1.389 + while (t < (int)(n * 0.9)) { // 90% is close enough 1.390 + if (loops == 0 || t == 0) { 1.391 + loops = failsafe; 1.392 + failsafe *= 10; 1.393 + } else { 1.394 + //System.out.println("# " + meth.getName() + " x " + loops + " = " + t); 1.395 + loops = (int)((double)n / t * loops + 0.5); 1.396 + if (loops == 0) { 1.397 + fprintf(stderr,"Unable to converge on desired duration"); 1.398 + return FALSE; 1.399 + } 1.400 + } 1.401 + //System.out.println("# " + meth.getName() + " x " + loops); 1.402 + t = testFunction->time(loops,&status); 1.403 + if(U_FAILURE(status)){ 1.404 + printf("Performance test failed with error: %s \n", u_errorName(status)); 1.405 + break; 1.406 + } 1.407 + } 1.408 + } else { 1.409 + loops = iterations; 1.410 + } 1.411 + 1.412 + double min_t=1000000.0, sum_t=0.0; 1.413 + long events = -1; 1.414 + 1.415 + for(int32_t ps =0; ps < passes; ps++){ 1.416 + fprintf(stdout,"= %s begin " ,name); 1.417 + if(verbose==TRUE){ 1.418 + if(iterations > 0) { 1.419 + fprintf(stdout, "%i\n", (int)loops); 1.420 + } else { 1.421 + fprintf(stdout, "%i\n", (int)n); 1.422 + } 1.423 + } else { 1.424 + fprintf(stdout, "\n"); 1.425 + } 1.426 + t = testFunction->time(loops, &status); 1.427 + if(U_FAILURE(status)){ 1.428 + printf("Performance test failed with error: %s \n", u_errorName(status)); 1.429 + break; 1.430 + } 1.431 + sum_t+=t; 1.432 + if(t<min_t) { 1.433 + min_t=t; 1.434 + } 1.435 + events = testFunction->getEventsPerIteration(); 1.436 + //print info only in verbose mode 1.437 + if(verbose==TRUE){ 1.438 + if(events == -1){ 1.439 + fprintf(stdout, "= %s end: %f loops: %i operations: %li \n", name, t, (int)loops, ops); 1.440 + }else{ 1.441 + fprintf(stdout, "= %s end: %f loops: %i operations: %li events: %li\n", name, t, (int)loops, ops, events); 1.442 + } 1.443 + }else{ 1.444 + if(events == -1){ 1.445 + fprintf(stdout,"= %s end %f %i %li\n", name, t, (int)loops, ops); 1.446 + }else{ 1.447 + fprintf(stdout,"= %s end %f %i %li %li\n", name, t, (int)loops, ops, events); 1.448 + } 1.449 + } 1.450 + } 1.451 + if(verbose && U_SUCCESS(status)) { 1.452 + double avg_t = sum_t/passes; 1.453 + if (loops == 0 || ops == 0) { 1.454 + fprintf(stderr, "%s did not run\n", name); 1.455 + } 1.456 + else if(events == -1) { 1.457 + fprintf(stdout, "%%= %s avg: %.4g loops: %i avg/op: %.4g ns\n", 1.458 + name, avg_t, (int)loops, (avg_t*1E9)/(loops*ops)); 1.459 + fprintf(stdout, "_= %s min: %.4g loops: %i min/op: %.4g ns\n", 1.460 + name, min_t, (int)loops, (min_t*1E9)/(loops*ops)); 1.461 + } 1.462 + else { 1.463 + fprintf(stdout, "%%= %s avg: %.4g loops: %i avg/op: %.4g ns avg/event: %.4g ns\n", 1.464 + name, avg_t, (int)loops, (avg_t*1E9)/(loops*ops), (avg_t*1E9)/(loops*events)); 1.465 + fprintf(stdout, "_= %s min: %.4g loops: %i min/op: %.4g ns min/event: %.4g ns\n", 1.466 + name, min_t, (int)loops, (min_t*1E9)/(loops*ops), (min_t*1E9)/(loops*events)); 1.467 + } 1.468 + } 1.469 + delete testFunction; 1.470 + } 1.471 + index++; 1.472 + }while(name); 1.473 + 1.474 + gTest = saveTest; 1.475 + return rval; 1.476 +} 1.477 + 1.478 +/** 1.479 +* Print a usage message for this test class. 1.480 +*/ 1.481 +void UPerfTest::usage( void ) 1.482 +{ 1.483 + puts(gUsageString); 1.484 + if (_addUsage != NULL) { 1.485 + puts(_addUsage); 1.486 + } 1.487 + 1.488 + UBool save_verbose = verbose; 1.489 + verbose = TRUE; 1.490 + fprintf(stdout,"Test names:\n"); 1.491 + fprintf(stdout,"-----------\n"); 1.492 + 1.493 + int32_t index = 0; 1.494 + const char* name = NULL; 1.495 + do{ 1.496 + this->runIndexedTest( index, FALSE, name ); 1.497 + if (!name) 1.498 + break; 1.499 + fprintf(stdout, "%s\n", name); 1.500 + index++; 1.501 + }while (name && (name[0] != 0)); 1.502 + verbose = save_verbose; 1.503 +} 1.504 + 1.505 + 1.506 + 1.507 + 1.508 +void UPerfTest::setCaller( UPerfTest* callingTest ) 1.509 +{ 1.510 + caller = callingTest; 1.511 + if (caller) { 1.512 + verbose = caller->verbose; 1.513 + } 1.514 +} 1.515 + 1.516 +UBool UPerfTest::callTest( UPerfTest& testToBeCalled, char* par ) 1.517 +{ 1.518 + execCount--; // correct a previously assumed test-exec, as this only calls a subtest 1.519 + testToBeCalled.setCaller( this ); 1.520 + return testToBeCalled.runTest( path, par ); 1.521 +} 1.522 + 1.523 +UPerfTest::~UPerfTest(){ 1.524 + if(lines!=NULL){ 1.525 + delete[] lines; 1.526 + } 1.527 + if(buffer!=NULL){ 1.528 + uprv_free(buffer); 1.529 + } 1.530 + if(resolvedFileName!=NULL){ 1.531 + uprv_free(resolvedFileName); 1.532 + } 1.533 + ucbuf_close(ucharBuf); 1.534 +} 1.535 + 1.536 +#endif