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