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