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