• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /********************************************************************
2  * COPYRIGHT:
3  * Copyright (c) 2002-2007, 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 
UPerfTest(int32_t argc,const char * argv[],UErrorCode & status)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 
UPerfTest(int32_t argc,const char * argv[],UOption addOptions[],int32_t addOptionsCount,const char * addUsage,UErrorCode & status)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 
init(UOption addOptions[],int32_t addOptionsCount,UErrorCode & status)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     // add specific options
114     int32_t optionsCount = OPTIONS_COUNT;
115     if (addOptionsCount > 0) {
116         memcpy(options+optionsCount, addOptions, addOptionsCount*sizeof(UOption));
117         optionsCount += addOptionsCount;
118     }
119 
120     //parse the arguments
121     _remainingArgc = u_parseArgs(_argc, (char**)_argv, optionsCount, options);
122 
123     // copy back values for additional options
124     if (addOptionsCount > 0) {
125         memcpy(addOptions, options+OPTIONS_COUNT, addOptionsCount*sizeof(UOption));
126     }
127 
128     // Now setup the arguments
129     if(_argc==1 || options[HELP1].doesOccur || options[HELP2].doesOccur) {
130         status = U_ILLEGAL_ARGUMENT_ERROR;
131         return;
132     }
133 
134     if(options[VERBOSE].doesOccur) {
135         verbose = TRUE;
136     }
137 
138     if(options[SOURCEDIR].doesOccur) {
139         sourceDir = options[SOURCEDIR].value;
140     }
141 
142     if(options[ENCODING].doesOccur) {
143         encoding = options[ENCODING].value;
144     }
145 
146     if(options[USELEN].doesOccur) {
147         uselen = TRUE;
148     }
149 
150     if(options[FILE_NAME].doesOccur){
151         fileName = options[FILE_NAME].value;
152     }
153 
154     if(options[PASSES].doesOccur) {
155         passes = atoi(options[PASSES].value);
156     }
157     if(options[ITERATIONS].doesOccur) {
158         iterations = atoi(options[ITERATIONS].value);
159         if(options[TIME].doesOccur) {
160             status = U_ILLEGAL_ARGUMENT_ERROR;
161             return;
162         }
163     } else if(options[TIME].doesOccur) {
164         time = atoi(options[TIME].value);
165     } else {
166         iterations = 1000; // some default
167     }
168 
169     if(options[LINE_MODE].doesOccur) {
170         line_mode = TRUE;
171         bulk_mode = FALSE;
172     }
173 
174     if(options[BULK_MODE].doesOccur) {
175         bulk_mode = TRUE;
176         line_mode = FALSE;
177     }
178 
179     if(options[LOCALE].doesOccur) {
180       locale = options[LOCALE].value;
181     }
182 
183     int32_t len = 0;
184     resolvedFileName = NULL;
185     if(fileName!=NULL){
186         //pre-flight
187         ucbuf_resolveFileName(sourceDir, fileName, NULL, &len, &status);
188         resolvedFileName = (char*) uprv_malloc(len);
189         if(resolvedFileName==NULL){
190             status= U_MEMORY_ALLOCATION_ERROR;
191             return;
192         }
193         if(status == U_BUFFER_OVERFLOW_ERROR){
194             status = U_ZERO_ERROR;
195         }
196         ucbuf_resolveFileName(sourceDir, fileName, resolvedFileName, &len, &status);
197         ucharBuf = ucbuf_open(resolvedFileName,&encoding,TRUE,FALSE,&status);
198 
199         if(U_FAILURE(status)){
200             printf("Could not open the input file %s. Error: %s\n", fileName, u_errorName(status));
201             return;
202         }
203     }
204 }
205 
getLines(UErrorCode & status)206 ULine* UPerfTest::getLines(UErrorCode& status){
207     lines     = new ULine[MAXLINES];
208     int maxLines = MAXLINES;
209     numLines=0;
210     const UChar* line=NULL;
211     int32_t len =0;
212     for (;;) {
213         line = ucbuf_readline(ucharBuf,&len,&status);
214         if(line == NULL || U_FAILURE(status)){
215             break;
216         }
217         lines[numLines].name  = new UChar[len];
218         lines[numLines].len   = len;
219         memcpy(lines[numLines].name, line, len * U_SIZEOF_UCHAR);
220 
221         numLines++;
222         len = 0;
223         if (numLines >= maxLines) {
224             maxLines += MAXLINES;
225             ULine *newLines = new ULine[maxLines];
226             if(newLines == NULL) {
227                 fprintf(stderr, "Out of memory reading line %d.\n", (int)numLines);
228                 status= U_MEMORY_ALLOCATION_ERROR;
229                 delete []lines;
230                 return NULL;
231             }
232 
233             memcpy(newLines, lines, numLines*sizeof(ULine));
234             delete []lines;
235             lines = newLines;
236         }
237     }
238     return lines;
239 }
getBuffer(int32_t & len,UErrorCode & status)240 const UChar* UPerfTest::getBuffer(int32_t& len, UErrorCode& status){
241     if (U_FAILURE(status)) {
242         return NULL;
243     }
244     len = ucbuf_size(ucharBuf);
245     buffer =  (UChar*) uprv_malloc(U_SIZEOF_UCHAR * (len+1));
246     u_strncpy(buffer,ucbuf_getBuffer(ucharBuf,&bufferLen,&status),len);
247     buffer[len]=0;
248     len = bufferLen;
249     return buffer;
250 }
run()251 UBool UPerfTest::run(){
252     if(_remainingArgc==1){
253         // Testing all methods
254         return runTest();
255     }
256     UBool res=FALSE;
257     // Test only the specified fucntion
258     for (int i = 1; i < _remainingArgc; ++i) {
259         if (_argv[i][0] != '-') {
260             char* name = (char*) _argv[i];
261             if(verbose==TRUE){
262                 //fprintf(stdout, "\n=== Handling test: %s: ===\n", name);
263                 //fprintf(stdout, "\n%s:\n", name);
264             }
265             char* parameter = strchr( name, '@' );
266             if (parameter) {
267                 *parameter = 0;
268                 parameter += 1;
269             }
270             execCount = 0;
271             res = runTest( name, parameter );
272             if (!res || (execCount <= 0)) {
273                 fprintf(stdout, "\n---ERROR: Test doesn't exist: %s!\n", name);
274                 return FALSE;
275             }
276         }
277     }
278     return res;
279 }
runTest(char * name,char * par)280 UBool UPerfTest::runTest(char* name, char* par ){
281     UBool rval;
282     char* pos = NULL;
283 
284     if (name)
285         pos = strchr( name, delim ); // check if name contains path (by looking for '/')
286     if (pos) {
287         path = pos+1;   // store subpath for calling subtest
288         *pos = 0;       // split into two strings
289     }else{
290         path = NULL;
291     }
292 
293     if (!name || (name[0] == 0) || (strcmp(name, "*") == 0)) {
294         rval = runTestLoop( NULL, NULL );
295 
296     }else if (strcmp( name, "LIST" ) == 0) {
297         this->usage();
298         rval = TRUE;
299 
300     }else{
301         rval = runTestLoop( name, par );
302     }
303 
304     if (pos)
305         *pos = delim;  // restore original value at pos
306     return rval;
307 }
308 
309 
setPath(char * pathVal)310 void UPerfTest::setPath( char* pathVal )
311 {
312     this->path = pathVal;
313 }
314 
315 // call individual tests, to be overriden to call implementations
runIndexedTest(int32_t index,UBool exec,const char * & name,char * par)316 UPerfFunction* UPerfTest::runIndexedTest( int32_t index, UBool exec, const char* &name, char* par )
317 {
318     // to be overriden by a method like:
319     /*
320     switch (index) {
321         case 0: name = "First Test"; if (exec) FirstTest( par ); break;
322         case 1: name = "Second Test"; if (exec) SecondTest( par ); break;
323         default: name = ""; break;
324     }
325     */
326     fprintf(stderr,"*** runIndexedTest needs to be overriden! ***");
327     name = ""; exec = exec; index = index; par = par;
328     return NULL;
329 }
330 
331 
runTestLoop(char * testname,char * par)332 UBool UPerfTest::runTestLoop( char* testname, char* par )
333 {
334     int32_t    index = 0;
335     const char*   name;
336     UBool  run_this_test;
337     UBool  rval = FALSE;
338     UErrorCode status = U_ZERO_ERROR;
339     UPerfTest* saveTest = gTest;
340     gTest = this;
341     int32_t loops = 0;
342     double t=0;
343     int32_t n = 1;
344     long ops;
345     do {
346         this->runIndexedTest( index, FALSE, name );
347         if (!name || (name[0] == 0))
348             break;
349         if (!testname) {
350             run_this_test = TRUE;
351         }else{
352             run_this_test = (UBool) (strcmp( name, testname ) == 0);
353         }
354         if (run_this_test) {
355             UPerfFunction* testFunction = this->runIndexedTest( index, TRUE, name, par );
356             execCount++;
357             rval=TRUE;
358             if(testFunction==NULL){
359                 fprintf(stderr,"%s function returned NULL", name);
360                 return FALSE;
361             }
362             ops = testFunction->getOperationsPerIteration();
363             if (ops < 1) {
364                 fprintf(stderr, "%s returned an illegal operations/iteration()\n", name);
365                 return FALSE;
366             }
367             if(iterations == 0) {
368                 n = time;
369                 // Run for specified duration in seconds
370                 if(verbose==TRUE){
371                     fprintf(stdout,"= %s calibrating %i seconds \n", name, (int)n);
372                 }
373 
374                 //n *=  1000; // s => ms
375                 //System.out.println("# " + meth.getName() + " " + n + " sec");
376                 int32_t failsafe = 1; // last resort for very fast methods
377                 t = 0;
378                 while (t < (int)(n * 0.9)) { // 90% is close enough
379                     if (loops == 0 || t == 0) {
380                         loops = failsafe;
381                         failsafe *= 10;
382                     } else {
383                         //System.out.println("# " + meth.getName() + " x " + loops + " = " + t);
384                         loops = (int)((double)n / t * loops + 0.5);
385                         if (loops == 0) {
386                             fprintf(stderr,"Unable to converge on desired duration");
387                             return FALSE;
388                         }
389                     }
390                     //System.out.println("# " + meth.getName() + " x " + loops);
391                     t = testFunction->time(loops,&status);
392                     if(U_FAILURE(status)){
393                         printf("Performance test failed with error: %s \n", u_errorName(status));
394                         break;
395                     }
396                 }
397             } else {
398                 loops = iterations;
399             }
400 
401             double min_t=1000000.0, sum_t=0.0;
402             long events = -1;
403 
404             for(int32_t ps =0; ps < passes; ps++){
405                 fprintf(stdout,"= %s begin " ,name);
406                 if(verbose==TRUE){
407                     if(iterations > 0) {
408                         fprintf(stdout, "%i\n", (int)loops);
409                     } else {
410                         fprintf(stdout, "%i\n", (int)n);
411                     }
412                 } else {
413                     fprintf(stdout, "\n");
414                 }
415                 t = testFunction->time(loops, &status);
416                 if(U_FAILURE(status)){
417                     printf("Performance test failed with error: %s \n", u_errorName(status));
418                     break;
419                 }
420                 sum_t+=t;
421                 if(t<min_t) {
422                     min_t=t;
423                 }
424                 events = testFunction->getEventsPerIteration();
425                 //print info only in verbose mode
426                 if(verbose==TRUE){
427                     if(events == -1){
428                         fprintf(stdout, "= %s end: %f loops: %i operations: %li \n", name, t, (int)loops, ops);
429                     }else{
430                         fprintf(stdout, "= %s end: %f loops: %i operations: %li events: %li\n", name, t, (int)loops, ops, events);
431                     }
432                 }else{
433                     if(events == -1){
434                         fprintf(stdout,"= %s end %f %i %li\n", name, t, (int)loops, ops);
435                     }else{
436                         fprintf(stdout,"= %s end %f %i %li %li\n", name, t, (int)loops, ops, events);
437                     }
438                 }
439             }
440             if(verbose && U_SUCCESS(status)) {
441                 double avg_t = sum_t/passes;
442                 if (loops == 0 || ops == 0) {
443                     fprintf(stderr, "%s did not run\n", name);
444                 }
445                 else if(events == -1) {
446                     fprintf(stdout, "%%= %s avg: %.4g loops: %i avg/op: %.4g ns\n",
447                             name, avg_t, (int)loops, (avg_t*1E9)/(loops*ops));
448                     fprintf(stdout, "_= %s min: %.4g loops: %i min/op: %.4g ns\n",
449                             name, min_t, (int)loops, (min_t*1E9)/(loops*ops));
450                 }
451                 else {
452                     fprintf(stdout, "%%= %s avg: %.4g loops: %i avg/op: %.4g ns avg/event: %.4g ns\n",
453                             name, avg_t, (int)loops, (avg_t*1E9)/(loops*ops), (avg_t*1E9)/(loops*events));
454                     fprintf(stdout, "_= %s min: %.4g loops: %i min/op: %.4g ns min/event: %.4g ns\n",
455                             name, min_t, (int)loops, (min_t*1E9)/(loops*ops), (min_t*1E9)/(loops*events));
456                 }
457             }
458             delete testFunction;
459         }
460         index++;
461     }while(name);
462 
463     gTest = saveTest;
464     return rval;
465 }
466 
467 /**
468 * Print a usage message for this test class.
469 */
usage(void)470 void UPerfTest::usage( void )
471 {
472     puts(gUsageString);
473     if (_addUsage != NULL) {
474         puts(_addUsage);
475     }
476 
477     UBool save_verbose = verbose;
478     verbose = TRUE;
479     fprintf(stdout,"Test names:\n");
480     fprintf(stdout,"-----------\n");
481 
482     int32_t index = 0;
483     const char* name = NULL;
484     do{
485         this->runIndexedTest( index, FALSE, name );
486         if (!name)
487             break;
488         fprintf(stdout,name);
489         fprintf(stdout,"\n");
490         index++;
491     }while (name && (name[0] != 0));
492     verbose = save_verbose;
493 }
494 
495 
496 
497 
setCaller(UPerfTest * callingTest)498 void UPerfTest::setCaller( UPerfTest* callingTest )
499 {
500     caller = callingTest;
501     if (caller) {
502         verbose = caller->verbose;
503     }
504 }
505 
callTest(UPerfTest & testToBeCalled,char * par)506 UBool UPerfTest::callTest( UPerfTest& testToBeCalled, char* par )
507 {
508     execCount--; // correct a previously assumed test-exec, as this only calls a subtest
509     testToBeCalled.setCaller( this );
510     return testToBeCalled.runTest( path, par );
511 }
512 
~UPerfTest()513 UPerfTest::~UPerfTest(){
514     if(lines!=NULL){
515         delete[] lines;
516     }
517     if(buffer!=NULL){
518         uprv_free(buffer);
519     }
520     if(resolvedFileName!=NULL){
521         uprv_free(resolvedFileName);
522     }
523     ucbuf_close(ucharBuf);
524 }
525 
526 #endif
527