]> git.saurik.com Git - apple/icu.git/blame - icuSources/tools/ctestfw/uperf.cpp
ICU-491.11.3.tar.gz
[apple/icu.git] / icuSources / tools / ctestfw / uperf.cpp
CommitLineData
b75a7d8f
A
1/********************************************************************
2 * COPYRIGHT:
4388f060 3 * Copyright (c) 2002-2011, International Business Machines Corporation and
b75a7d8f
A
4 * others. All Rights Reserved.
5 ********************************************************************/
6
4388f060
A
7// Defines _XOPEN_SOURCE for access to POSIX functions.
8// Must be before any other #includes.
9#include "uposixdefs.h"
46f4442e 10
73c04bcf
A
11#include "unicode/uperf.h"
12#include "uoptions.h"
13#include "cmemory.h"
14#include <stdio.h>
15#include <stdlib.h>
b75a7d8f 16
73c04bcf 17#if !UCONFIG_NO_CONVERSION
4388f060
A
18
19UPerfFunction::~UPerfFunction() {}
20
b75a7d8f
A
21static const char delim = '/';
22static int32_t execCount = 0;
23UPerfTest* UPerfTest::gTest = NULL;
374ca955
A
24static const int MAXLINES = 40000;
25const 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"
46f4442e
A
36 "\t-p or --passes Number of passes to be performed. Requires Numeric argument.\n"
37 "\t Cannot be used with --time\n"
374ca955 38 "\t-i or --iterations Number of iterations to be performed. Requires Numeric argument\n"
46f4442e
A
39 "\t-t or --time Threshold time for looping until in seconds. Requires Numeric argument.\n"
40 "\t Cannot be used with --iterations\n"
374ca955 41 "\t-l or --line-mode The data file should be processed in line mode\n"
46f4442e
A
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"
374ca955
A
44 "\t-L or --locale Locale for the test\n";
45
b75a7d8f
A
46enum
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,
46f4442e
A
60 LOCALE,
61 OPTIONS_COUNT
b75a7d8f
A
62};
63
64
46f4442e 65static UOption options[OPTIONS_COUNT+20]={
374ca955
A
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};
b75a7d8f 80
46f4442e
A
81UPerfTest::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
94UPerfTest::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
110void UPerfTest::init(UOption addOptions[], int32_t addOptionsCount,
111 UErrorCode& status) {
b75a7d8f 112 //initialize the argument list
46f4442e
A
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
b75a7d8f 124 //parse the arguments
46f4442e
A
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 }
b75a7d8f
A
131
132 // Now setup the arguments
46f4442e 133 if(_argc==1 || options[HELP1].doesOccur || options[HELP2].doesOccur) {
b75a7d8f
A
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);
46f4442e
A
163 if(options[TIME].doesOccur) {
164 status = U_ILLEGAL_ARGUMENT_ERROR;
165 return;
166 }
167 } else if(options[TIME].doesOccur) {
b75a7d8f 168 time = atoi(options[TIME].value);
46f4442e
A
169 } else {
170 iterations = 1000; // some default
b75a7d8f 171 }
46f4442e 172
b75a7d8f
A
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) {
46f4442e 184 locale = options[LOCALE].value;
b75a7d8f
A
185 }
186
187 int32_t len = 0;
b75a7d8f
A
188 if(fileName!=NULL){
189 //pre-flight
73c04bcf 190 ucbuf_resolveFileName(sourceDir, fileName, NULL, &len, &status);
b75a7d8f 191 resolvedFileName = (char*) uprv_malloc(len);
73c04bcf 192 if(resolvedFileName==NULL){
b75a7d8f
A
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
209ULine* UPerfTest::getLines(UErrorCode& status){
4388f060
A
210 if (U_FAILURE(status)) {
211 return NULL;
212 }
213 if (lines != NULL) {
214 return lines; // don't do it again
215 }
b75a7d8f
A
216 lines = new ULine[MAXLINES];
217 int maxLines = MAXLINES;
218 numLines=0;
219 const UChar* line=NULL;
220 int32_t len =0;
221 for (;;) {
374ca955
A
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;
73c04bcf 238 delete []lines;
374ca955 239 return NULL;
b75a7d8f 240 }
374ca955
A
241
242 memcpy(newLines, lines, numLines*sizeof(ULine));
73c04bcf 243 delete []lines;
374ca955
A
244 lines = newLines;
245 }
b75a7d8f
A
246 }
247 return lines;
248}
249const UChar* UPerfTest::getBuffer(int32_t& len, UErrorCode& status){
46f4442e
A
250 if (U_FAILURE(status)) {
251 return NULL;
252 }
b75a7d8f
A
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}
260UBool 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}
289UBool 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
319void UPerfTest::setPath( char* pathVal )
320{
321 this->path = pathVal;
322}
323
324// call individual tests, to be overriden to call implementations
325UPerfFunction* 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 name = ""; exec = exec; index = index; par = par;
337 return NULL;
338}
339
340
341UBool UPerfTest::runTestLoop( char* testname, char* par )
342{
343 int32_t index = 0;
344 const char* name;
345 UBool run_this_test;
346 UBool rval = FALSE;
347 UErrorCode status = U_ZERO_ERROR;
348 UPerfTest* saveTest = gTest;
349 gTest = this;
350 int32_t loops = 0;
351 double t=0;
352 int32_t n = 1;
46f4442e 353 long ops;
b75a7d8f
A
354 do {
355 this->runIndexedTest( index, FALSE, name );
356 if (!name || (name[0] == 0))
357 break;
358 if (!testname) {
359 run_this_test = TRUE;
360 }else{
361 run_this_test = (UBool) (strcmp( name, testname ) == 0);
362 }
363 if (run_this_test) {
364 UPerfFunction* testFunction = this->runIndexedTest( index, TRUE, name, par );
365 execCount++;
366 rval=TRUE;
367 if(testFunction==NULL){
368 fprintf(stderr,"%s function returned NULL", name);
369 return FALSE;
370 }
46f4442e
A
371 ops = testFunction->getOperationsPerIteration();
372 if (ops < 1) {
b75a7d8f
A
373 fprintf(stderr, "%s returned an illegal operations/iteration()\n", name);
374 return FALSE;
375 }
376 if(iterations == 0) {
374ca955
A
377 n = time;
378 // Run for specified duration in seconds
379 if(verbose==TRUE){
380 fprintf(stdout,"= %s calibrating %i seconds \n", name, (int)n);
381 }
382
383 //n *= 1000; // s => ms
384 //System.out.println("# " + meth.getName() + " " + n + " sec");
385 int32_t failsafe = 1; // last resort for very fast methods
386 t = 0;
387 while (t < (int)(n * 0.9)) { // 90% is close enough
388 if (loops == 0 || t == 0) {
389 loops = failsafe;
390 failsafe *= 10;
391 } else {
392 //System.out.println("# " + meth.getName() + " x " + loops + " = " + t);
393 loops = (int)((double)n / t * loops + 0.5);
394 if (loops == 0) {
395 fprintf(stderr,"Unable to converge on desired duration");
396 return FALSE;
397 }
398 }
399 //System.out.println("# " + meth.getName() + " x " + loops);
400 t = testFunction->time(loops,&status);
401 if(U_FAILURE(status)){
402 printf("Performance test failed with error: %s \n", u_errorName(status));
403 break;
404 }
405 }
b75a7d8f 406 } else {
374ca955 407 loops = iterations;
b75a7d8f
A
408 }
409
46f4442e
A
410 double min_t=1000000.0, sum_t=0.0;
411 long events = -1;
412
b75a7d8f 413 for(int32_t ps =0; ps < passes; ps++){
374ca955
A
414 fprintf(stdout,"= %s begin " ,name);
415 if(verbose==TRUE){
416 if(iterations > 0) {
417 fprintf(stdout, "%i\n", (int)loops);
418 } else {
419 fprintf(stdout, "%i\n", (int)n);
420 }
421 } else {
422 fprintf(stdout, "\n");
423 }
424 t = testFunction->time(loops, &status);
425 if(U_FAILURE(status)){
426 printf("Performance test failed with error: %s \n", u_errorName(status));
427 break;
428 }
46f4442e
A
429 sum_t+=t;
430 if(t<min_t) {
431 min_t=t;
432 }
374ca955
A
433 events = testFunction->getEventsPerIteration();
434 //print info only in verbose mode
435 if(verbose==TRUE){
374ca955 436 if(events == -1){
46f4442e 437 fprintf(stdout, "= %s end: %f loops: %i operations: %li \n", name, t, (int)loops, ops);
374ca955 438 }else{
46f4442e 439 fprintf(stdout, "= %s end: %f loops: %i operations: %li events: %li\n", name, t, (int)loops, ops, events);
374ca955
A
440 }
441 }else{
374ca955 442 if(events == -1){
46f4442e 443 fprintf(stdout,"= %s end %f %i %li\n", name, t, (int)loops, ops);
374ca955 444 }else{
46f4442e 445 fprintf(stdout,"= %s end %f %i %li %li\n", name, t, (int)loops, ops, events);
374ca955
A
446 }
447 }
b75a7d8f 448 }
46f4442e
A
449 if(verbose && U_SUCCESS(status)) {
450 double avg_t = sum_t/passes;
451 if (loops == 0 || ops == 0) {
452 fprintf(stderr, "%s did not run\n", name);
453 }
454 else if(events == -1) {
455 fprintf(stdout, "%%= %s avg: %.4g loops: %i avg/op: %.4g ns\n",
456 name, avg_t, (int)loops, (avg_t*1E9)/(loops*ops));
457 fprintf(stdout, "_= %s min: %.4g loops: %i min/op: %.4g ns\n",
458 name, min_t, (int)loops, (min_t*1E9)/(loops*ops));
459 }
460 else {
461 fprintf(stdout, "%%= %s avg: %.4g loops: %i avg/op: %.4g ns avg/event: %.4g ns\n",
462 name, avg_t, (int)loops, (avg_t*1E9)/(loops*ops), (avg_t*1E9)/(loops*events));
463 fprintf(stdout, "_= %s min: %.4g loops: %i min/op: %.4g ns min/event: %.4g ns\n",
464 name, min_t, (int)loops, (min_t*1E9)/(loops*ops), (min_t*1E9)/(loops*events));
465 }
466 }
b75a7d8f
A
467 delete testFunction;
468 }
469 index++;
470 }while(name);
471
472 gTest = saveTest;
473 return rval;
474}
475
476/**
477* Print a usage message for this test class.
478*/
479void UPerfTest::usage( void )
480{
46f4442e
A
481 puts(gUsageString);
482 if (_addUsage != NULL) {
483 puts(_addUsage);
484 }
485
b75a7d8f
A
486 UBool save_verbose = verbose;
487 verbose = TRUE;
488 fprintf(stdout,"Test names:\n");
489 fprintf(stdout,"-----------\n");
490
491 int32_t index = 0;
492 const char* name = NULL;
493 do{
494 this->runIndexedTest( index, FALSE, name );
374ca955
A
495 if (!name)
496 break;
4388f060 497 fprintf(stdout, "%s\n", name);
b75a7d8f
A
498 index++;
499 }while (name && (name[0] != 0));
500 verbose = save_verbose;
501}
502
503
504
505
506void UPerfTest::setCaller( UPerfTest* callingTest )
507{
508 caller = callingTest;
509 if (caller) {
510 verbose = caller->verbose;
511 }
512}
513
514UBool UPerfTest::callTest( UPerfTest& testToBeCalled, char* par )
515{
516 execCount--; // correct a previously assumed test-exec, as this only calls a subtest
517 testToBeCalled.setCaller( this );
518 return testToBeCalled.runTest( path, par );
519}
520
521UPerfTest::~UPerfTest(){
522 if(lines!=NULL){
523 delete[] lines;
524 }
525 if(buffer!=NULL){
526 uprv_free(buffer);
527 }
73c04bcf
A
528 if(resolvedFileName!=NULL){
529 uprv_free(resolvedFileName);
530 }
b75a7d8f
A
531 ucbuf_close(ucharBuf);
532}
533
73c04bcf 534#endif