• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/local/bin/perl
2# *******************************************************************************
3# * Copyright (C) 2002-2012 International Business Machines Corporation and     *
4# * others. All Rights Reserved.                                                *
5# *******************************************************************************
6
7use XML::LibXML;
8
9# Assume we are running within the icu4j root directory
10use lib 'src/com/ibm/icu/dev/test/perf';
11use Dataset;
12my $OS=$^O;
13
14my $CLASSPATH;
15if ($OS eq "linux" || $OS eq "darwin") {
16	$CLASSPATH="../icu4j.jar:../tools/misc/out/lib/icu4j-tools.jar:out/bin";
17} else {
18	$CLASSPATH="../icu4j.jar;../tools/misc/out/lib/icu4j-tools.jar;out/bin";
19}
20#---------------------------------------------------------------------
21
22# Methods to be tested.  Each pair represents a test method and
23# a baseline method which is used for comparison.
24my @METHODS  = (
25                 ['TestJDKConstruction',     'TestICUConstruction'],
26                 ['TestJDKParse',            'TestICUParse'],
27                 ['TestJDKFormat',           'TestICUFormat']
28               );
29# Patterns which define the set of characters used for testing.
30my @OPTIONS = (
31#                 locale    pattern              date string
32                [ "en_US",  "dddd MMM yyyy",     "15 Jan 2007"],
33                [ "sw_KE",  "dddd MMM yyyy",     "15 Jan 2007"],
34                [ "en_US",  "HH:mm",             "13:13"],
35                [ "en_US",  "HH:mm zzzz",        "13:13 Pacific Standard Time"],
36                [ "en_US",  "HH:mm z",           "13:13 PST"],
37                [ "en_US",  "HH:mm Z",           "13:13 -0800"],
38              );
39
40my $THREADS;        # number of threads (input from command-line args)
41my $CALIBRATE = 2;  # duration in seconds for initial calibration
42my $DURATION  = 10; # duration in seconds for each pass
43my $NUMPASSES = 4;  # number of passes.  If > 1 then the first pass
44                    # is discarded as a JIT warm-up pass.
45
46my $TABLEATTR = 'BORDER="1" CELLPADDING="4" CELLSPACING="0"';
47
48my $PLUS_MINUS = "±";
49
50if ($NUMPASSES < 3) {
51    die "Need at least 3 passes.  One is discarded (JIT warmup) and need two to have 1 degree of freedom (t distribution).";
52}
53
54
55# run all tests with the specified number of threads from command-line input
56# (if there is no arguments, use $THREADS = 1)
57foreach my $arg ($#ARGV >= 0 ? @ARGV : "1") {
58  $THREADS = $arg;
59  main();
60}
61
62
63#---------------------------------------------------------------------
64sub main {
65
66#-----------DATE FORMAT PERFORMANCE TESTS-----------------------------
67    my $testclass = 'com.ibm.icu.dev.test.perf.DateFormatPerformanceTest';
68    #my $threads = ($THREADS > 1) ? "($THREADS threads)" : "";
69
70    my $doc = XML::LibXML::Document->new('1.0', 'utf-8');
71    my $root = $doc->createElement("perfTestResults");
72
73 #   my $raw = "";
74    my @shortNames = ( "open" , "parse", "fmt");
75    my $index=0;
76
77    for my $methodPair (@METHODS) {
78
79        my $testMethod = $methodPair->[0];
80        my $baselineMethod = $methodPair->[1];
81	my $testname = $shortNames[$index];
82	$index++;
83
84        $OUT = '';
85	my $patternCounter=1;
86
87        for my $pat (@OPTIONS) {
88
89            # measure the test method
90            my $t = measure2($testclass, $testMethod, $pat, -$DURATION);
91	    my $testResult = $t->getMean();
92	    my $jdkElement = $doc->createElement("perfTestResult");
93	    my $testName = "DateFmt-$testname-pat$patternCounter-JDK";
94	    $jdkElement->setAttribute("test" => $testName);
95	    $jdkElement->setAttribute("iterations" => 1);
96	    $jdkElement->setAttribute("time" => $testResult);
97	    $root->appendChild($jdkElement);
98
99            # measure baseline method
100            my $b = measure2($testclass, $baselineMethod, $pat, -$DURATION);
101            my $baseResult = $b->getMean();
102	    my $icuElement = $doc->createElement("perfTestResult");
103	    my $testName = "DateFmt-$testname-pat$patternCounter";
104	    $patternCounter++;
105	    $icuElement->setAttribute("test"=> $testName);
106 	    $icuElement->setAttribute("iterations" => 1);
107	    $icuElement->setAttribute("time" => $baseResult);
108	    $root->appendChild($icuElement);
109
110       }
111    }
112
113#------------------DECIMAL FORMAT TESTS---------------------------------
114
115    my $testclass = 'com.ibm.icu.dev.test.perf.DecimalFormatPerformanceTest';
116    my @OPTIONS = (
117#		locale	    pattern	date string
118		[ "en_US", "#,###.##", "1,234.56"],
119		[ "de_DE", "#,###.##", "1.234,56"],
120		);
121    my $index=0;
122    for my $methodPair (@METHODS) {
123
124        my $testMethod = $methodPair->[0];
125        my $baselineMethod = $methodPair->[1];
126	my $testname = $shortNames[$index];
127	$index++;
128
129
130        for my $pat (@OPTIONS) {
131	       my $patternName = $pat->[0];
132
133            # measure the test method
134            my $t = measure2($testclass, $testMethod, $pat, -$DURATION);
135	    my $testResult = $t->getMean();
136	    my $jdkElement = $doc->createElement("perfTestResult");
137	    my $testName = "NumFmt-$testname-$patternName-JDK";
138	    $jdkElement->setAttribute("test" => $testName);
139	    $jdkElement->setAttribute("iterations"=>1);
140	    $jdkElement->setAttribute("time" => $testResult);
141	    $root->appendChild($jdkElement);
142
143            # measure baseline method
144            my $b = measure2($testclass, $baselineMethod, $pat, -$DURATION);
145            my $baseResult = $b->getMean();
146	    my $icuElement = $doc->createElement("perfTestResult");
147	    my $testName = "NumFmt-$testname-$patternName";
148	    $icuElement->setAttribute("test"=> $testName);
149	    $icuElement->setAttribute("iterations"=>1);
150	    $icuElement->setAttribute("time" => $baseResult);
151	    $root->appendChild($icuElement);
152	}
153    }
154
155#----------------COLLATION PERFORMANCE TESTS--------------------------_
156
157    %dataFiles = (
158   	   "en_US",         "TestNames_Latin.txt",
159	   "da_DK",         "TestNames_Latin.txt",
160	   "de_DE",         "TestNames_Latin.txt",
161	   "de__PHONEBOOK", "TestNames_Latin.txt",
162	   "fr_FR",         "TestNames_Latin.txt",
163	   "ja_JP",         "TestNames_Latin.txt TestNames_Japanese_h.txt TestNames_Japanese_k.txt TestNames_Asian.txt",
164	   "zh_CN",         "TestNames_Latin.txt TestNames_Chinese.txt",
165	   "zh_TW",         "TestNames_Latin.txt TestNames_Chinese.txt",
166	   "zh__PINYIN",    "TestNames_Latin.txt TestNames_Chinese.txt",
167	   "ru_RU", 	    "TestNames_Latin.txt TestNames_Russian.txt",
168	   "th",            "TestNames_Latin.txt TestNames_Thai.txt",
169	   "ko_KR",         "TestNames_Latin.txt TestNames_Korean.txt",
170	   );
171
172    #  Outer loop runs through the locales to test
173    #     (Edit this list dirctly to make changes)
174    #
175    foreach  $locale (
176	   "en_US",
177	   "da_DK",
178	   "de_DE",
179	   "de__PHONEBOOK",
180	   "fr_FR",
181	   "ja_JP",
182           "zh_CN",
183	   "zh_TW",
184	   "zh__PINYIN",
185           "ko_KR",
186	   "ru_RU",
187	   "th",
188                   )
189       {
190
191
192       #
193       # Inner loop runs over the set of data files specified for each locale.
194       #    (Edit the %datafiles initialization, above, to make changes.
195       #
196        $ff = $dataFiles{$locale};
197        @ff = split(/[\s]+/, $ff);
198        $counter = 1;
199        foreach  $data (@ff) {
200          #
201          # Run ICU Test for this (locale, data file) pair.
202          #
203           $iStrCol = `java -classpath $CLASSPATH com.ibm.icu.dev.test.perf.CollationPerformanceTest -terse -file data/collation/$data -locale $locale -loop 1000 -binsearch`;
204print "java -classpath $CLASSPATH com.ibm.icu.dev.test.perf.CollationPerformanceTest -terse -file data/collation/$data -locale $locale -loop 1000 -binsearch\n";
205  $iStrCol =~s/[,\s]*//g;  # whack off the leading "  ," in the returned result.
206          doKeyTimes("java -classpath $CLASSPATH com.ibm.icu.dev.test.perf.CollationPerformanceTest -terse -file data/collation/$data -locale $locale -loop 1000 -keygen",
207                    my $iKeyGen, my $iKeyLen);
208
209          #
210          # Run Windows test for this (locale, data file) pair.  Only do if
211          #    we are not on Windows 98/ME and we hava a windows langID
212          #    for the locale.
213          #
214           $wStrCol =  $wKeyGen =  $wKeyLen = 0;
215          my $wStrCol = `java -classpath $CLASSPATH com.ibm.icu.dev.test.perf.CollationPerformanceTest -terse -file data/collation/$data -locale $locale -loop 1000 -binsearch -java`;
216          $wStrCol =~s/[,\s]*//g;  # whack off the leading "  ," in the returned result.
217          doKeyTimes("java -classpath $CLASSPATH com.ibm.icu.dev.test.perf.CollationPerformanceTest -terse -file data/collation/$data -locale $locale -loop 1000 -keygen -java",
218                     $wKeyGen, $wKeyLen);
219
220           $collDiff =  $keyGenDiff =  $keyLenDiff = 0;
221          if ($wKeyLen > 0) {
222               $collDiff   = (($wStrCol - $iStrCol) / $iStrCol) * 100;
223               $keyGenDiff = (($wKeyGen - $iKeyGen) / $iKeyGen) * 100;
224               $keyLenDiff = (($wKeyLen - $iKeyLen) / $iKeyLen) * 100;
225          }
226
227	my $ICU = $doc->createElement("perfTestResult");
228	my $testname = "Coll-$locale-data$counter-StrCol";
229	#write the results corresponding to this local,data pair
230	$ICU->setAttribute("test"=> $testname);
231	$ICU->setAttribute("iterations"=>1000);
232	$ICU->setAttribute("time"=> $iStrCol);
233	$root->appendChild($ICU);
234
235	my $Key = $doc->createElement("perfTestResult");
236	my $testname = "Coll-$locale-data$counter-keyGen";
237	$Key->setAttribute("test"=> $testname);
238	$Key->setAttribute("iterations"=>1000);
239	$Key->setAttribute("time"=>$iKeyGen);
240	$root->appendChild($Key);
241
242	my $JDK = $doc->createElement("perfTestResult");
243	my $testname = "Coll-$locale-data$counter-StrCol-JDK";
244	$JDK->setAttribute("test"=>$testname);
245	$JDK->setAttribute("iterations"=>1000);
246	$JDK->setAttribute("time"=>$wStrCol);
247	$root->appendChild($JDK);
248
249	my $Key = $doc->createElement("perfTestResult");
250	my $testname = "Coll-$locale-data$counter-keyGen-JDK";
251	$Key->setAttribute("test"=>$testname);
252	$Key->setAttribute("iterations"=>1000);
253	$Key->setAttribute("time"=>$wKeyGen);
254	$root->appendChild($Key);
255	$counter++;
256     }
257   }
258
259
260
261#----------WRITE RESULTS TO perf.xml-----------------------
262    $doc->setDocumentElement($root);
263    open my $out_fh, '>', "perf.xml";
264    print {$out_fh} $doc->toString;
265}
266
267
268#---------------------------------------------------------------------
269# Append text to the global variable $OUT
270sub out {
271   $OUT .= join('', @_);
272}
273
274
275#---------------------------------------------------------------------
276# Measure a given test method with a give test pattern using the
277# global run parameters.
278#
279# @param the method to run
280# @param the pattern defining characters to test
281# @param if >0 then the number of iterations per pass.  If <0 then
282#        (negative of) the number of seconds per pass.
283#
284# @return a Dataset object, scaled by iterations per pass and
285#         events per iteration, to give time per event
286#
287sub measure2 {
288    my @data = measure1(@_);
289    my $iterPerPass = shift(@data);
290    my $eventPerIter = shift(@data);
291
292    shift(@data) if (@data > 1); # discard first run
293
294    my $ds = Dataset->new(@data);
295    $ds->setScale(1.0e-3 / ($iterPerPass * $eventPerIter));
296    $ds;
297}
298
299#---------------------------------------------------------------------
300# Measure a given test method with a give test pattern using the
301# global run parameters.
302#
303# @param the method to run
304# @param the pattern defining characters to test
305# @param if >0 then the number of iterations per pass.  If <0 then
306#        (negative of) the number of seconds per pass.
307#
308# @return array of:
309#         [0] iterations per pass
310#         [1] events per iteration
311#         [2..] ms reported for each pass, in order
312#
313sub measure1 {
314    my $testclass = shift;
315    my $method = shift;
316    my $pat = shift;
317    my $iterCount = shift; # actually might be -seconds/pass
318
319    # is $iterCount actually -seconds/pass?
320    if ($iterCount < 0) {
321
322        # calibrate: estimate ms/iteration
323        print "Calibrating...";
324        my @t = callJava($testclass, $method, $pat, -$CALIBRATE, 1);
325        print "done.\n";
326
327        my @data = split(/\s+/, $t[0]->[2]);
328        $data[0] *= 1.0e+3;
329
330        my $timePerIter = 1.0e-3 * $data[0] / $data[1];
331
332        # determine iterations/pass
333        $iterCount = int(-$iterCount / $timePerIter + 0.5);
334   }
335
336    # run passes
337    print "Measuring $iterCount iterations x $NUMPASSES passes...";
338    my @t = callJava($testclass, $method, $pat, $iterCount, $NUMPASSES);
339    print "done.\n";
340    my @ms = ();
341    my @b; # scratch
342    for my $a (@t) {
343        # $a->[0]: method name, corresponds to $method
344        # $a->[1]: 'begin' data, == $iterCount
345        # $a->[2]: 'end' data, of the form <ms> <loops> <eventsPerIter>
346        # $a->[3...]: gc messages from JVM during pass
347        @b = split(/\s+/, $a->[2]);
348        push(@ms, $b[0] * 1.0e+3);
349    }
350    my $eventsPerIter = $b[2];
351
352    my @ms_str = @ms;
353    $ms_str[0] .= " (discarded)" if (@ms_str > 1);
354
355    ($iterCount, $eventsPerIter, @ms);
356}
357
358#---------------------------------------------------------------------
359# Invoke java to run $TESTCLASS, passing it the given parameters.
360#
361# @param the method to run
362# @param the number of iterations, or if negative, the duration
363#        in seconds.  If more than on pass is desired, pass in
364#        a string, e.g., "100 100 100".
365# @param the pattern defining characters to test
366#
367# @return an array of results.  Each result is an array REF
368#         describing one pass.  The array REF contains:
369#         ->[0]: The method name as reported
370#         ->[1]: The params on the '= <meth> begin ...' line
371#         ->[2]: The params on the '= <meth> end ...' line
372#         ->[3..]: GC messages from the JVM, if any
373#
374sub callJava {
375    my $testclass = shift;
376    my $method = shift;
377    my $pat = shift;
378    my $n = shift;
379    my $passes = shift;
380
381    my $n = ($n < 0) ? "-t ".(-$n) : "-i ".$n;
382
383    my $cmd = "java -classpath $CLASSPATH $testclass $method $n -p $passes -L @$pat[0] \"@$pat[1]\" \"@$pat[2]\" -r $THREADS";
384    print "[$cmd]\n"; # for debugging
385    open(PIPE, "$cmd|") or die "Can't run \"$cmd\"";
386    my @out;
387    while (<PIPE>) {
388        push(@out, $_);
389    }
390    close(PIPE) or die "Java failed: \"$cmd\"";
391
392    @out = grep(!/^\#/, @out);  # filter out comments
393
394    #print "[", join("\n", @out), "]\n";
395
396    my @results;
397    my $method = '';
398    my $data = [];
399    foreach (@out) {
400        next unless (/\S/);
401
402        if (/^=\s*(\w+)\s*(\w+)\s*(.*)/) {
403            my ($m, $state, $d) = ($1, $2, $3);
404            #print "$_ => [[$m $state $data]]\n";
405            if ($state eq 'begin') {
406                die "$method was begun but not finished" if ($method);
407                $method = $m;
408                push(@$data, $d);
409                push(@$data, ''); # placeholder for end data
410            } elsif ($state eq 'end') {
411                if ($m ne $method) {
412                    die "$method end does not match: $_";
413                }
414                $data->[1] = $d; # insert end data at [1]
415                #print "#$method:", join(";",@$data), "\n";
416                unshift(@$data, $method); # add method to start
417
418                push(@results, $data);
419                $method = '';
420                $data = [];
421            } else {
422                die "Can't parse: $_";
423           }
424        }
425       elsif (/^\[/) {
426            if ($method) {
427                push(@$data, $_);
428            } else {
429                # ignore extraneous GC notices
430            }
431        }
432        else {
433            die "Can't parse: $_";
434        }
435    }
436
437    die "$method was begun but not finished" if ($method);
438
439    @results;
440}
441
442#-----------------------------------------------------------------------------------
443#  doKeyGenTimes($Command_to_run, $time, $key_length)
444#       Do a key-generation test and return the time and key length/char values.
445#
446sub doKeyTimes($$$) {
447   # print "$_[0]\n";
448   local($x) = `$_[0]`;                  # execute the collperf command.
449   ($_[1], $_[2]) = split(/\,/, $x);     # collperf returns "time, keylength" string.
450}
451
452
453#eof
454