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