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