]> gitweb.fperrin.net Git - Dictionary.git/blob - jars/icu4j-4_2_1-src/src/com/ibm/icu/dev/test/perf/normperf.pl
icu4jsrc
[Dictionary.git] / jars / icu4j-4_2_1-src / src / com / ibm / icu / dev / test / perf / normperf.pl
1 #!/usr/local/bin/perl\r
2 # *******************************************************************************\r
3 # * Copyright (C) 2002-2007 International Business Machines Corporation and     *\r
4 # * others. All Rights Reserved.                                                *\r
5 # *******************************************************************************\r
6 \r
7 use strict;\r
8 \r
9 # Assume we are running within the icu4j root directory\r
10 use lib 'src/com/ibm/icu/dev/test/perf';\r
11 use Dataset;\r
12 \r
13 #---------------------------------------------------------------------\r
14 # Test class\r
15 my $TESTCLASS = 'com.ibm.icu.dev.test.perf.NormalizerPerformanceTest'; \r
16 \r
17 # Methods to be tested.  Each pair represents a test method and\r
18 # a baseline method which is used for comparison.\r
19 my @METHODS  = (\r
20                 ['TestJDK_NFD_NFC_Text',  'TestICU_NFD_NFC_Text'],\r
21                 ['TestJDK_NFC_NFC_Text',  'TestICU_NFC_NFC_Text'],\r
22 #               ['TestJDK_NFC_NFD_Text',  'TestICU_NFC_NFD_Text'],\r
23                 ['TestJDK_NFC_Orig_Text', 'TestICU_NFC_Orig_Text'],\r
24                 ['TestJDK_NFD_NFC_Text',  'TestICU_NFD_NFC_Text'],\r
25                 ['TestJDK_NFD_NFD_Text',  'TestICU_NFD_NFD_Text'],\r
26                 ['TestJDK_NFD_Orig_Text', 'TestICU_NFD_Orig_Text'], \r
27                );\r
28 \r
29 # Patterns which define the set of characters used for testing.\r
30 \r
31 my $SOURCEDIR ="src/com/ibm/icu/dev/test/perf/data/collation/";\r
32 \r
33 my @OPTIONS = (\r
34 #                      src text                     src encoding  mode  \r
35                     [ "TestNames_SerbianSH.txt",    "UTF-8", "b"],\r
36 #                   [ "arabic.txt",                 "UTF-8", "b"],\r
37 #                   [ "french.txt",                 "UTF-8", "b"],\r
38 #                   [ "greek.txt",                  "UTF-8", "b"],\r
39 #                   [ "hebrew.txt",                 "UTF-8", "b"],\r
40 #                   [ "hindi.txt" ,                 "UTF-8", "b"],\r
41 #                   [ "japanese.txt",               "UTF-8", "b"],\r
42 #                   [ "korean.txt",                 "UTF-8", "b"],\r
43 #                   [ "s-chinese.txt",              "UTF-8", "b"],\r
44 #                   [ "french.txt",                 "UTF-8", "b"],\r
45 #                   [ "greek.txt",                  "UTF-8", "b"],\r
46 #                   [ "hebrew.txt",                 "UTF-8", "b"],\r
47 #                   [ "hindi.txt" ,                 "UTF-8", "b"],\r
48 #                   [ "japanese.txt",               "UTF-8", "b"],\r
49 #                   [ "korean.txt",                 "UTF-8", "b"],\r
50 #                   [ "s-chinese.txt",              "UTF-8", "b"],\r
51 #                   [ "arabic.html",                "UTF-8", "b"],\r
52 #                   [ "czech.html",                 "UTF-8", "b"],\r
53 #                   [ "danish.html",                "UTF-8", "b"],\r
54 #                   [ "english.html",               "UTF-8", "b"],\r
55 #                   [ "esperanto.html",             "UTF-8", "b"],\r
56 #                   [ "french.html",                "UTF-8", "b"],\r
57 #                   [ "georgian.html",              "UTF-8", "b"],\r
58 #                   [ "german.html",                "UTF-8", "b"],\r
59 #                   [ "greek.html",                 "UTF-8", "b"],\r
60 #                   [ "hebrew.html",                "UTF-8", "b"],\r
61 #                   [ "hindi.html",                 "UTF-8", "b"],\r
62 #                   [ "icelandic.html",             "UTF-8", "b"],\r
63 #                   [ "interlingua.html",           "UTF-8", "b"],\r
64 #                   [ "italian.html",               "UTF-8", "b"],\r
65 #                   [ "japanese.html",              "UTF-8", "b"],\r
66 #                   [ "korean.html",                "UTF-8", "b"],\r
67 #                   [ "lithuanian.html",            "UTF-8", "b"],\r
68 #                   [ "maltese.html",               "UTF-8", "b"],\r
69 #                   [ "persian.html",               "UTF-8", "b"],\r
70 #                   [ "polish.html",                "UTF-8", "b"],\r
71 #                   [ "portuguese.html",            "UTF-8", "b"],\r
72 #                   [ "romanian.html",              "UTF-8", "b"],\r
73 #                   [ "russian.html",               "UTF-8", "b"],\r
74 #                   [ "s-chinese.html",             "UTF-8", "b"],\r
75 #                   [ "spanish.html",               "UTF-8", "b"],\r
76 #                   [ "swedish.html",               "UTF-8", "b"],\r
77 #                   [ "t-chinese.html",             "UTF-8", "b"],\r
78 #                   [ "welsh.html",                 "UTF-8", "b"],\r
79                     [ "TestNames_Asian.txt",        "UTF-8", "l"],\r
80                     [ "TestNames_Chinese.txt",      "UTF-8", "l"],\r
81                     [ "TestNames_Japanese.txt",     "UTF-8", "l"],\r
82                     [ "TestNames_Japanese_h.txt",   "UTF-8", "l"],\r
83                     [ "TestNames_Japanese_k.txt",   "UTF-8", "l"],\r
84                     [ "TestNames_Korean.txt",       "UTF-8", "l"],\r
85                     [ "TestNames_Latin.txt",        "UTF-8", "l"],\r
86                     [ "TestNames_SerbianSH.txt",    "UTF-8", "l"],\r
87                     [ "TestNames_SerbianSR.txt",    "UTF-8", "l"],\r
88                     [ "TestNames_Thai.txt",         "UTF-8", "l"],\r
89                     [ "Testnames_Russian.txt",      "UTF-8", "l"], \r
90               );\r
91 \r
92 my $CALIBRATE = 2;  # duration in seconds for initial calibration\r
93 my $DURATION  = 10; # duration in seconds for each pass\r
94 my $NUMPASSES = 4;  # number of passes.  If > 1 then the first pass\r
95                     # is discarded as a JIT warm-up pass.\r
96 \r
97 my $TABLEATTR = 'BORDER="1" CELLPADDING="4" CELLSPACING="0"';\r
98 \r
99 my $PLUS_MINUS = "±";\r
100 \r
101 if ($NUMPASSES < 3) {\r
102     die "Need at least 3 passes.  One is discarded (JIT warmup) and need two to have 1 degree of freedom (t distribution).";\r
103 }\r
104 \r
105 my $OUT; # see out()\r
106 \r
107 main();\r
108 \r
109 #---------------------------------------------------------------------\r
110 # ...\r
111 sub main {\r
112     my $date = localtime;\r
113     my $title = "ICU4J Performance Test $date";\r
114 \r
115     my $html = $date;\r
116     $html =~ s/://g; # ':' illegal\r
117     $html =~ s/\s*\d+$//; # delete year\r
118     $html =~ s/^\w+\s*//; # delete dow\r
119     $html = "perf $html.html";\r
120 \r
121     open(HTML,">$html") or die "Can't write to $html: $!";\r
122 \r
123     print HTML <<EOF;\r
124 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"\r
125    "http://www.w3.org/TR/html4/strict.dtd">\r
126 <HTML>\r
127    <HEAD>\r
128       <TITLE>$title</TITLE>\r
129    </HEAD>\r
130    <BODY>\r
131 EOF\r
132     print HTML "<H1>$title</H1>\n";\r
133 \r
134     print HTML "<H2>$TESTCLASS</H2>\n";\r
135 \r
136     my $raw = "";\r
137 \r
138     for my $methodPair (@METHODS) {\r
139 \r
140         my $testMethod = $methodPair->[0];\r
141         my $baselineMethod = $methodPair->[1];\r
142 \r
143         print HTML "<P><TABLE $TABLEATTR><TR><TD>\n";\r
144         print HTML "<P><B>$testMethod vs. $baselineMethod</B></P>\n";\r
145         \r
146         print HTML "<P><TABLE $TABLEATTR BGCOLOR=\"#CCFFFF\">\n";\r
147         print HTML "<TR><TD>Options</TD><TD>$testMethod</TD>";\r
148         print HTML "<TD>$baselineMethod</TD><TD>Ratio</TD></TR>\n";\r
149 \r
150         $OUT = '';\r
151 \r
152         for my $pat (@OPTIONS) {\r
153             print HTML "<TR><TD>@$pat[0], @$pat[2]</TD>\n";\r
154 \r
155             out("<P><TABLE $TABLEATTR WIDTH=\"100%\">");\r
156 \r
157             # measure the test method\r
158             out("<TR><TD>");\r
159             print "\n$testMethod [@$pat]\n";\r
160             my $t = measure2($testMethod, $pat, -$DURATION);\r
161             out("</TD></TR>");\r
162             print HTML "<TD>", formatSeconds(4, $t->getMean(), $t->getError);\r
163             print HTML "/event</TD>\n";\r
164 \r
165             # measure baseline method\r
166             out("<TR><TD>");\r
167             print "\n$baselineMethod [@$pat]\n";\r
168             my $b = measure2($baselineMethod, $pat, -$DURATION);\r
169             out("</TD></TR>");\r
170             print HTML "<TD>", formatSeconds(4, $b->getMean(), $t->getError);\r
171             print HTML "/event</TD>\n";\r
172 \r
173             out("</TABLE></P>");\r
174 \r
175             # output ratio\r
176             my $r = $t->divide($b);\r
177             my $mean = $r->getMean() - 1;\r
178             my $color = $mean < 0 ? "RED" : "BLACK";\r
179             print HTML "<TD><B><FONT COLOR=\"$color\">", formatPercent(3, $mean, $r->getError);\r
180             print HTML "</FONT></B></TD></TR>\n";\r
181         }\r
182 \r
183         print HTML "</TABLE></P>\n";\r
184 \r
185         print HTML "<P>Raw data:</P>\n";\r
186         print HTML $OUT;\r
187         print HTML "</TABLE></P>\n";\r
188     }\r
189 \r
190     print HTML <<EOF;\r
191    </BODY>\r
192 </HTML>\r
193 EOF\r
194     close(HTML) or die "Can't close $html: $!";\r
195 }\r
196 \r
197 #---------------------------------------------------------------------\r
198 # Append text to the global variable $OUT\r
199 sub out {\r
200     $OUT .= join('', @_);\r
201 }\r
202 \r
203 #---------------------------------------------------------------------\r
204 # Append text to the global variable $OUT\r
205 sub outln {\r
206     $OUT .= join('', @_) . "\n";\r
207 }\r
208 \r
209 #---------------------------------------------------------------------\r
210 # Measure a given test method with a give test pattern using the\r
211 # global run parameters.\r
212 #\r
213 # @param the method to run\r
214 # @param the pattern defining characters to test\r
215 # @param if >0 then the number of iterations per pass.  If <0 then\r
216 #        (negative of) the number of seconds per pass.\r
217 #\r
218 # @return a Dataset object, scaled by iterations per pass and\r
219 #         events per iteration, to give time per event\r
220 #\r
221 sub measure2 {\r
222     my @data = measure1(@_);\r
223     my $iterPerPass = shift(@data);\r
224     my $eventPerIter = shift(@data);\r
225 \r
226     shift(@data) if (@data > 1); # discard first run\r
227 \r
228     my $ds = Dataset->new(@data);\r
229     $ds->setScale(1.0e-3 / ($iterPerPass * $eventPerIter));\r
230     $ds;\r
231 }\r
232 \r
233 #---------------------------------------------------------------------\r
234 # Measure a given test method with a give test pattern using the\r
235 # global run parameters.\r
236 #\r
237 # @param the method to run\r
238 # @param the pattern defining characters to test\r
239 # @param if >0 then the number of iterations per pass.  If <0 then\r
240 #        (negative of) the number of seconds per pass.\r
241 #\r
242 # @return array of:\r
243 #         [0] iterations per pass\r
244 #         [1] events per iteration\r
245 #         [2..] ms reported for each pass, in order\r
246 #\r
247 sub measure1 {\r
248     my $method = shift;\r
249     my $pat = shift;\r
250     my $iterCount = shift; # actually might be -seconds/pass\r
251 \r
252     out("<P>Measuring $method for input file @$pat[0] in @$pat[2] , ");\r
253     if ($iterCount > 0) {\r
254         out("$iterCount iterations/pass, $NUMPASSES passes</P>\n");\r
255     } else {\r
256         out(-$iterCount, " seconds/pass, $NUMPASSES passes</P>\n");\r
257     }\r
258 \r
259     # is $iterCount actually -seconds/pass?\r
260     if ($iterCount < 0) {\r
261 \r
262         # calibrate: estimate ms/iteration\r
263         print "Calibrating...";\r
264         my @t = callJava($method, $pat, -$CALIBRATE, 1);\r
265         print "done.\n";\r
266 \r
267         my @data = split(/\s+/, $t[0]->[2]);\r
268         $data[0] *= 1.0e+3;\r
269 \r
270         my $timePerIter = 1.0e-3 * $data[0] / $data[1];\r
271     \r
272         # determine iterations/pass\r
273         $iterCount = int(-$iterCount / $timePerIter + 0.5);\r
274 \r
275         out("<P>Calibration pass ($CALIBRATE sec): ");\r
276         out("$data[0] ms, ");\r
277         out("$data[1] iterations = ");\r
278         out(formatSeconds(4, $timePerIter), "/iteration<BR>\n");\r
279     }\r
280     \r
281     # run passes\r
282     print "Measuring $iterCount iterations x $NUMPASSES passes...";\r
283     my @t = callJava($method, $pat, $iterCount, $NUMPASSES);\r
284     print "done.\n";\r
285     my @ms = ();\r
286     my @b; # scratch\r
287     for my $a (@t) {\r
288         # $a->[0]: method name, corresponds to $method\r
289         # $a->[1]: 'begin' data, == $iterCount\r
290         # $a->[2]: 'end' data, of the form <ms> <loops> <eventsPerIter>\r
291         # $a->[3...]: gc messages from JVM during pass\r
292         @b = split(/\s+/, $a->[2]);\r
293         push(@ms, $b[0] * 1.0e+3);\r
294     }\r
295     my $eventsPerIter = $b[2];\r
296 \r
297     out("Iterations per pass: $iterCount<BR>\n");\r
298     out("Events per iteration: $eventsPerIter<BR>\n");\r
299 \r
300     my @ms_str = @ms;\r
301     $ms_str[0] .= " (discarded)" if (@ms_str > 1);\r
302     out("Raw times (ms/pass): ", join(", ", @ms_str), "<BR>\n");\r
303 \r
304     ($iterCount, $eventsPerIter, @ms);\r
305 }\r
306 \r
307 #---------------------------------------------------------------------\r
308 # Invoke java to run $TESTCLASS, passing it the given parameters.\r
309 #\r
310 # @param the method to run\r
311 # @param the number of iterations, or if negative, the duration\r
312 #        in seconds.  If more than on pass is desired, pass in\r
313 #        a string, e.g., "100 100 100".\r
314 # @param the pattern defining characters to test\r
315 #\r
316 # @return an array of results.  Each result is an array REF\r
317 #         describing one pass.  The array REF contains:\r
318 #         ->[0]: The method name as reported\r
319 #         ->[1]: The params on the '= <meth> begin ...' line\r
320 #         ->[2]: The params on the '= <meth> end ...' line\r
321 #         ->[3..]: GC messages from the JVM, if any\r
322 #\r
323 sub callJava {\r
324     my $method = shift;\r
325     my $pat = shift;\r
326     my $n = shift;\r
327     my $passes = shift;\r
328     \r
329     my $fileName = $SOURCEDIR . @$pat[0] ; \r
330     my $n = ($n < 0) ? "-t ".(-$n) : "-i ".$n;\r
331     \r
332     my $cmd = "java -classpath classes $TESTCLASS $method $n -p $passes -f $fileName -e @$pat[1] -@$pat[2]";\r
333     print "[$cmd]\n"; # for debugging\r
334     open(PIPE, "$cmd|") or die "Can't run \"$cmd\"";\r
335     my @out;\r
336     while (<PIPE>) {\r
337         push(@out, $_);\r
338     }\r
339     close(PIPE) or die "Java failed: \"$cmd\"";\r
340 \r
341     @out = grep(!/^\#/, @out);  # filter out comments\r
342 \r
343     #print "[", join("\n", @out), "]\n";\r
344 \r
345     my @results;\r
346     my $method = '';\r
347     my $data = [];\r
348     foreach (@out) {\r
349         next unless (/\S/);\r
350 \r
351         if (/^=\s*(\w+)\s*(\w+)\s*(.*)/) {\r
352             my ($m, $state, $d) = ($1, $2, $3);\r
353             #print "$_ => [[$m $state $data]]\n";\r
354             if ($state eq 'begin') {\r
355                 die "$method was begun but not finished" if ($method);\r
356                 $method = $m;\r
357                 push(@$data, $d);\r
358                 push(@$data, ''); # placeholder for end data\r
359             } elsif ($state eq 'end') {\r
360                 if ($m ne $method) {\r
361                     die "$method end does not match: $_";\r
362                 }\r
363                 $data->[1] = $d; # insert end data at [1]\r
364                 #print "#$method:", join(";",@$data), "\n";\r
365                 unshift(@$data, $method); # add method to start\r
366 \r
367                 push(@results, $data);\r
368                 $method = '';\r
369                 $data = [];\r
370             } else {\r
371                 die "Can't parse: $_";\r
372             }\r
373         }\r
374 \r
375         elsif (/^\[/) {\r
376             if ($method) {\r
377                 push(@$data, $_);\r
378             } else {\r
379                 # ignore extraneous GC notices\r
380             }\r
381         }\r
382 \r
383         else {\r
384             die "Can't parse: $_";\r
385         }\r
386     }\r
387 \r
388     die "$method was begun but not finished" if ($method);\r
389 \r
390     @results;\r
391 }\r
392 \r
393 #|#---------------------------------------------------------------------\r
394 #|# Format a confidence interval, as given by a Dataset.  Output is as\r
395 #|# as follows:\r
396 #|#   241.23 - 241.98 => 241.5 +/- 0.3\r
397 #|#   241.2 - 243.8 => 242 +/- 1\r
398 #|#   211.0 - 241.0 => 226 +/- 15 or? 230 +/- 20\r
399 #|#   220.3 - 234.3 => 227 +/- 7\r
400 #|#   220.3 - 300.3 => 260 +/- 40\r
401 #|#   220.3 - 1000 => 610 +/- 390 or? 600 +/- 400\r
402 #|#   0.022 - 0.024 => 0.023 +/- 0.001\r
403 #|#   0.022 - 0.032 => 0.027 +/- 0.005\r
404 #|#   0.022 - 1.000 => 0.5 +/- 0.5\r
405 #|# In other words, take one significant digit of the error value and\r
406 #|# display the mean to the same precision.\r
407 #|sub formatDataset {\r
408 #|    my $ds = shift;\r
409 #|    my $lower = $ds->getMean() - $ds->getError();\r
410 #|    my $upper = $ds->getMean() + $ds->getError();\r
411 #|    my $scale = 0;\r
412 #|    # Find how many initial digits are the same\r
413 #|    while ($lower < 1 ||\r
414 #|           int($lower) == int($upper)) {\r
415 #|        $lower *= 10;\r
416 #|        $upper *= 10;\r
417 #|        $scale++;\r
418 #|    }\r
419 #|    while ($lower >= 10 &&\r
420 #|           int($lower) == int($upper)) {\r
421 #|        $lower /= 10;\r
422 #|        $upper /= 10;\r
423 #|        $scale--;\r
424 #|    }\r
425 #|}\r
426 \r
427 #---------------------------------------------------------------------\r
428 # Format a number, optionally with a +/- delta, to n significant\r
429 # digits.\r
430 #\r
431 # @param significant digit, a value >= 1\r
432 # @param multiplier\r
433 # @param time in seconds to be formatted\r
434 # @optional delta in seconds\r
435 #\r
436 # @return string of the form "23" or "23 +/- 10".\r
437 #\r
438 sub formatNumber {\r
439     my $sigdig = shift;\r
440     my $mult = shift;\r
441     my $a = shift;\r
442     my $delta = shift; # may be undef\r
443     \r
444     my $result = formatSigDig($sigdig, $a*$mult);\r
445     if (defined($delta)) {\r
446         my $d = formatSigDig($sigdig, $delta*$mult);\r
447         # restrict PRECISION of delta to that of main number\r
448         if ($result =~ /\.(\d+)/) {\r
449             # TODO make this work for values with all significant\r
450             # digits to the left of the decimal, e.g., 1234000.\r
451 \r
452             # TODO the other thing wrong with this is that it\r
453             # isn't rounding the $delta properly.  Have to put\r
454             # this logic into formatSigDig().\r
455             my $x = length($1);\r
456             $d =~ s/\.(\d{$x})\d+/.$1/;\r
457         }\r
458         $result .= " $PLUS_MINUS " . $d;\r
459     }\r
460     $result;\r
461 }\r
462 \r
463 #---------------------------------------------------------------------\r
464 # Format a time, optionally with a +/- delta, to n significant\r
465 # digits.\r
466 #\r
467 # @param significant digit, a value >= 1\r
468 # @param time in seconds to be formatted\r
469 # @optional delta in seconds\r
470 #\r
471 # @return string of the form "23 ms" or "23 +/- 10 ms".\r
472 #\r
473 sub formatSeconds {\r
474     my $sigdig = shift;\r
475     my $a = shift;\r
476     my $delta = shift; # may be undef\r
477 \r
478     my @MULT = (1   , 1e3,  1e6,  1e9);\r
479     my @SUFF = ('s' , 'ms', 'us', 'ns');\r
480 \r
481     # Determine our scale\r
482     my $i = 0;\r
483     ++$i while ($a*$MULT[$i] < 1 && $i < @MULT);\r
484     \r
485     formatNumber($sigdig, $MULT[$i], $a, $delta) . ' ' . $SUFF[$i];\r
486 }\r
487 \r
488 #---------------------------------------------------------------------\r
489 # Format a percentage, optionally with a +/- delta, to n significant\r
490 # digits.\r
491 #\r
492 # @param significant digit, a value >= 1\r
493 # @param value to be formatted, as a fraction, e.g. 0.5 for 50%\r
494 # @optional delta, as a fraction\r
495 #\r
496 # @return string of the form "23 %" or "23 +/- 10 %".\r
497 #\r
498 sub formatPercent {\r
499     my $sigdig = shift;\r
500     my $a = shift;\r
501     my $delta = shift; # may be undef\r
502     \r
503     formatNumber($sigdig, 100, $a, $delta) . ' %';\r
504 }\r
505 \r
506 #---------------------------------------------------------------------\r
507 # Format a number to n significant digits without using exponential\r
508 # notation.\r
509 #\r
510 # @param significant digit, a value >= 1\r
511 # @param number to be formatted\r
512 #\r
513 # @return string of the form "1234" "12.34" or "0.001234".  If\r
514 #         number was negative, prefixed by '-'.\r
515 #\r
516 sub formatSigDig {\r
517     my $n = shift() - 1;\r
518     my $a = shift;\r
519 \r
520     local $_ = sprintf("%.${n}e", $a);\r
521     my $sign = (s/^-//) ? '-' : '';\r
522 \r
523     my $a_e;\r
524     my $result;\r
525     if (/^(\d)\.(\d+)e([-+]\d+)$/) {\r
526         my ($d, $dn, $e) = ($1, $2, $3);\r
527         $a_e = $e;\r
528         $d .= $dn;\r
529         $e++;\r
530         $d .= '0' while ($e > length($d));\r
531         while ($e < 1) {\r
532             $e++;\r
533             $d = '0' . $d;\r
534         }\r
535         if ($e == length($d)) {\r
536             $result = $sign . $d;\r
537         } else {\r
538             $result = $sign . substr($d, 0, $e) . '.' . substr($d, $e);\r
539         }\r
540     } else {\r
541         die "Can't parse $_";\r
542     }\r
543     $result;\r
544 }\r
545 \r
546 #eof\r