]>
git.saurik.com Git - apple/icu.git/blob - icuSources/test/perf/normperf/simplenormperf.cpp
1 // © 2018 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
5 // created: 2018mar15 Markus W. Scherer
10 #include "unicode/utypes.h"
11 #include "unicode/bytestream.h"
12 #include "unicode/normalizer2.h"
13 #include "unicode/stringpiece.h"
14 #include "unicode/unistr.h"
15 #include "unicode/utf8.h"
16 #include "unicode/utimer.h"
19 using icu::Normalizer2
;
20 using icu::UnicodeString
;
24 // Strings with commonly occurring BMP characters.
27 static UnicodeString
getMixed(int32_t minLength
) {
28 return extend(UnicodeString(latin1
).append(japanese
).append(arabic
), minLength
);
30 static UnicodeString
getLatin1(int32_t minLength
) { return extend(latin1
, minLength
); }
31 static UnicodeString
getLowercaseLatin1(int32_t minLength
) { return extend(lowercaseLatin1
, minLength
); }
32 static UnicodeString
getASCII(int32_t minLength
) { return extend(ascii
, minLength
); }
33 static UnicodeString
getJapanese(int32_t minLength
) { return extend(japanese
, minLength
); }
35 // Returns an array of UTF-8 offsets, one per code point.
36 // Assumes all BMP characters.
37 static int32_t *toUTF8WithOffsets(const UnicodeString
&s16
, std::string
&s8
, int32_t &numCodePoints
) {
39 s8
.reserve(s16
.length());
41 const char *s
= s8
.data();
42 int32_t length
= s8
.length();
43 int32_t *offsets
= new int32_t[length
+ 1];
45 for (int32_t i
= 0; i
< length
;) {
47 U8_FWD_1(s
, i
, length
);
49 offsets
[numCP
] = length
;
50 numCodePoints
= numCP
;
55 static UnicodeString
extend(const UnicodeString
&s
, int32_t minLength
) {
56 UnicodeString
result(s
);
57 while (result
.length() < minLength
) {
58 UnicodeString twice
= result
+ result
;
59 result
= std::move(twice
);
64 static const UChar
*const latin1
;
65 static const UChar
*const lowercaseLatin1
;
66 static const UChar
*const ascii
;
67 static const UChar
*const japanese
;
68 static const UChar
*const arabic
;
71 const UChar
*const CommonChars::latin1
=
72 // Goethe’s Bergschloß in normal sentence case.
73 u
"Da droben auf jenem Berge, da steht ein altes Schloß, "
74 u
"wo hinter Toren und Türen sonst lauerten Ritter und Roß.\n"
75 u
"Verbrannt sind Türen und Tore, und überall ist es so still; "
76 u
"das alte verfallne Gemäuer durchklettr ich, wie ich nur will.\n"
77 u
"Hierneben lag ein Keller, so voll von köstlichem Wein; "
78 u
"nun steiget nicht mehr mit Krügen die Kellnerin heiter hinein.\n"
79 u
"Sie setzt den Gästen im Saale nicht mehr die Becher umher, "
80 u
"sie füllt zum Heiligen Mahle dem Pfaffen das Fläschchen nicht mehr.\n"
81 u
"Sie reicht dem lüsternen Knappen nicht mehr auf dem Gange den Trank, "
82 u
"und nimmt für flüchtige Gabe nicht mehr den flüchtigen Dank.\n"
83 u
"Denn alle Balken und Decken, sie sind schon lange verbrannt, "
84 u
"und Trepp und Gang und Kapelle in Schutt und Trümmer verwandt.\n"
85 u
"Doch als mit Zither und Flasche nach diesen felsigen Höhn "
86 u
"ich an dem heitersten Tage mein Liebchen steigen gesehn,\n"
87 u
"da drängte sich frohes Behagen hervor aus verödeter Ruh, "
88 u
"da gings wie in alten Tagen recht feierlich wieder zu.\n"
89 u
"Als wären für stattliche Gäste die weitesten Räume bereit, "
90 u
"als käm ein Pärchen gegangen aus jener tüchtigen Zeit.\n"
91 u
"Als stünd in seiner Kapelle der würdige Pfaffe schon da "
92 u
"und fragte: Wollt ihr einander? Wir aber lächelten: Ja!\n"
93 u
"Und tief bewegten Gesänge des Herzens innigsten Grund, "
94 u
"Es zeugte, statt der Menge, der Echo schallender Mund.\n"
95 u
"Und als sich gegen Abend im stillen alles verlor,"
96 u
"da blickte die glühende Sonne zum schroffen Gipfel empor.\n"
97 u
"Und Knapp und Kellnerin glänzen als Herren weit und breit; "
98 u
"sie nimmt sich zum Kredenzen und er zum Danke sich Zeit.\n";
100 const UChar
*const CommonChars::lowercaseLatin1
=
101 // Goethe’s Bergschloß in all lowercase
102 u
"da droben auf jenem berge, da steht ein altes schloß, "
103 u
"wo hinter toren und türen sonst lauerten ritter und roß.\n"
104 u
"verbrannt sind türen und tore, und überall ist es so still; "
105 u
"das alte verfallne gemäuer durchklettr ich, wie ich nur will.\n"
106 u
"hierneben lag ein keller, so voll von köstlichem wein; "
107 u
"nun steiget nicht mehr mit krügen die kellnerin heiter hinein.\n"
108 u
"sie setzt den gästen im saale nicht mehr die becher umher, "
109 u
"sie füllt zum heiligen mahle dem pfaffen das fläschchen nicht mehr.\n"
110 u
"sie reicht dem lüsternen knappen nicht mehr auf dem gange den trank, "
111 u
"und nimmt für flüchtige gabe nicht mehr den flüchtigen dank.\n"
112 u
"denn alle balken und decken, sie sind schon lange verbrannt, "
113 u
"und trepp und gang und kapelle in schutt und trümmer verwandt.\n"
114 u
"doch als mit zither und flasche nach diesen felsigen höhn "
115 u
"ich an dem heitersten tage mein liebchen steigen gesehn,\n"
116 u
"da drängte sich frohes behagen hervor aus verödeter ruh, "
117 u
"da gings wie in alten tagen recht feierlich wieder zu.\n"
118 u
"als wären für stattliche gäste die weitesten räume bereit, "
119 u
"als käm ein pärchen gegangen aus jener tüchtigen zeit.\n"
120 u
"als stünd in seiner kapelle der würdige pfaffe schon da "
121 u
"und fragte: wollt ihr einander? wir aber lächelten: ja!\n"
122 u
"und tief bewegten gesänge des herzens innigsten grund, "
123 u
"es zeugte, statt der menge, der echo schallender mund.\n"
124 u
"und als sich gegen abend im stillen alles verlor,"
125 u
"da blickte die glühende sonne zum schroffen gipfel empor.\n"
126 u
"und knapp und kellnerin glänzen als herren weit und breit; "
127 u
"sie nimmt sich zum kredenzen und er zum danke sich zeit.\n";
129 const UChar
*const CommonChars::ascii
=
130 // Goethe’s Bergschloß in normal sentence case but ASCII-fied
131 u
"Da droben auf jenem Berge, da steht ein altes Schloss, "
132 u
"wo hinter Toren und Tueren sonst lauerten Ritter und Ross.\n"
133 u
"Verbrannt sind Tueren und Tore, und ueberall ist es so still; "
134 u
"das alte verfallne Gemaeuer durchklettr ich, wie ich nur will.\n"
135 u
"Hierneben lag ein Keller, so voll von koestlichem Wein; "
136 u
"nun steiget nicht mehr mit Kruegen die Kellnerin heiter hinein.\n"
137 u
"Sie setzt den Gaesten im Saale nicht mehr die Becher umher, "
138 u
"sie fuellt zum Heiligen Mahle dem Pfaffen das Flaeschchen nicht mehr.\n"
139 u
"Sie reicht dem luesternen Knappen nicht mehr auf dem Gange den Trank, "
140 u
"und nimmt fuer fluechtige Gabe nicht mehr den fluechtigen Dank.\n"
141 u
"Denn alle Balken und Decken, sie sind schon lange verbrannt, "
142 u
"und Trepp und Gang und Kapelle in Schutt und Truemmer verwandt.\n"
143 u
"Doch als mit Zither und Flasche nach diesen felsigen Hoehn "
144 u
"ich an dem heitersten Tage mein Liebchen steigen gesehn,\n"
145 u
"da draengte sich frohes Behagen hervor aus veroedeter Ruh, "
146 u
"da gings wie in alten Tagen recht feierlich wieder zu.\n"
147 u
"Als waeren fuer stattliche Gaeste die weitesten Raeume bereit, "
148 u
"als kaem ein Paerchen gegangen aus jener tuechtigen Zeit.\n"
149 u
"Als stuend in seiner Kapelle der wuerdige Pfaffe schon da "
150 u
"und fragte: Wollt ihr einander? Wir aber laechelten: Ja!\n"
151 u
"Und tief bewegten Gesaenge des Herzens innigsten Grund, "
152 u
"Es zeugte, statt der Menge, der Echo schallender Mund.\n"
153 u
"Und als sich gegen Abend im stillen alles verlor,"
154 u
"da blickte die gluehende Sonne zum schroffen Gipfel empor.\n"
155 u
"Und Knapp und Kellnerin glaenzen als Herren weit und breit; "
156 u
"sie nimmt sich zum Kredenzen und er zum Danke sich Zeit.\n";
158 const UChar
*const CommonChars::japanese
=
159 // Ame ni mo makezu = Be not Defeated by the Rain, by Kenji Miyazawa.
160 u
"雨にもまけず風にもまけず雪にも夏の暑さにもまけぬ"
161 u
"丈夫なからだをもち慾はなく決して瞋らず"
162 u
"いつもしずかにわらっている一日に玄米四合と"
163 u
"味噌と少しの野菜をたべあらゆることを"
164 u
"じぶんをかんじょうにいれずによくみききしわかり"
166 u
"小さな萱ぶきの小屋にいて東に病気のこどもあれば"
167 u
"行って看病してやり西につかれた母あれば"
168 u
"行ってその稲の束を負い南に死にそうな人あれば"
171 u
"つまらないからやめろといいひでりのときはなみだをながし"
172 u
"さむさのなつはおろおろあるきみんなにでくのぼうとよばれ"
173 u
"ほめられもせずくにもされずそういうものにわたしはなりたい";
175 const UChar
*const CommonChars::arabic
=
176 // Some Arabic for variety. "What is Unicode?"
177 // http://www.unicode.org/standard/translations/arabic.html
178 u
"تتعامل الحواسيب بالأسام مع الأرقام فقط، "
179 u
"و تخزن الحروف و المحارف "
180 u
"الأخرى بتخصيص رقم لكل واحد "
181 u
"منها. قبل اختراع يونيكود كان هناك ";
183 // TODO: class BenchmarkPerCodePoint?
188 virtual ~Operation();
189 virtual double call(int32_t iterations
, int32_t pieceLength
) = 0;
195 Operation::~Operation() {}
197 const int32_t kLengths
[] = { 5, 12, 30, 100, 1000, 10000 };
199 int32_t getMaxLength() { return kLengths
[UPRV_LENGTHOF(kLengths
) - 1]; }
201 // Returns seconds per code point.
202 double measure(Operation
&op
, int32_t pieceLength
) {
203 // Increase the number of iterations until we use at least one second.
204 int32_t iterations
= 1;
206 double seconds
= op
.call(iterations
, pieceLength
);
208 if (iterations
> 1) {
209 return seconds
/ (iterations
* pieceLength
);
211 // Run it once more, to avoid measuring only the warm-up.
212 return op
.call(1, pieceLength
) / (iterations
* pieceLength
);
215 if (seconds
< 0.01) {
217 } else if (seconds
< 0.55) {
218 iterations
*= 1.1 / seconds
;
225 void benchmark(const char *name
, Operation
&op
) {
226 for (int32_t i
= 0; i
< UPRV_LENGTHOF(kLengths
); ++i
) {
227 int32_t pieceLength
= kLengths
[i
];
228 double secPerCp
= measure(op
, pieceLength
);
229 printf("%s %6d %12f ns/cp\n", name
, (int)pieceLength
, secPerCp
* 1000000000);
234 class NormalizeUTF16
: public Operation
{
236 NormalizeUTF16(const Normalizer2
&n2
, const UnicodeString
&text
) :
237 norm2(n2
), src(text
), s(src
.getBuffer()) {}
238 virtual ~NormalizeUTF16();
239 virtual double call(int32_t iterations
, int32_t pieceLength
);
242 const Normalizer2
&norm2
;
248 NormalizeUTF16::~NormalizeUTF16() {}
250 // Assumes all BMP characters.
251 double NormalizeUTF16::call(int32_t iterations
, int32_t pieceLength
) {
253 int32_t limit
= src
.length() - pieceLength
;
255 UErrorCode errorCode
= U_ZERO_ERROR
;
256 utimer_getTime(&startTime
);
257 for (int32_t i
= 0; i
< iterations
; ++i
) {
258 piece
.setTo(FALSE
, s
+ start
, pieceLength
);
259 norm2
.normalize(piece
, dest
, errorCode
);
260 start
= (start
+ pieceLength
) % limit
;
262 return utimer_getElapsedSeconds(&startTime
);
265 class NormalizeUTF8
: public Operation
{
267 NormalizeUTF8(const Normalizer2
&n2
, const UnicodeString
&text
) : norm2(n2
), sink(&dest
) {
268 offsets
= CommonChars::toUTF8WithOffsets(text
, src
, numCodePoints
);
271 virtual ~NormalizeUTF8();
272 virtual double call(int32_t iterations
, int32_t pieceLength
);
275 const Normalizer2
&norm2
;
279 int32_t numCodePoints
;
281 icu::StringByteSink
<std::string
> sink
;
284 NormalizeUTF8::~NormalizeUTF8() {
288 double NormalizeUTF8::call(int32_t iterations
, int32_t pieceLength
) {
290 int32_t limit
= numCodePoints
- pieceLength
;
291 UErrorCode errorCode
= U_ZERO_ERROR
;
292 utimer_getTime(&startTime
);
293 for (int32_t i
= 0; i
< iterations
; ++i
) {
294 int32_t start8
= offsets
[start
];
295 int32_t limit8
= offsets
[start
+ pieceLength
];
296 icu::StringPiece
piece(s
+ start8
, limit8
- start8
);
297 norm2
.normalizeUTF8(0, piece
, sink
, nullptr, errorCode
);
298 start
= (start
+ pieceLength
) % limit
;
300 return utimer_getElapsedSeconds(&startTime
);
305 extern int main(int /*argc*/, const char * /*argv*/[]) {
306 // More than the longest piece length so that we read from different parts of the string
307 // for that piece length.
308 int32_t maxLength
= getMaxLength() * 10;
309 UErrorCode errorCode
= U_ZERO_ERROR
;
310 const Normalizer2
*nfc
= Normalizer2::getNFCInstance(errorCode
);
311 const Normalizer2
*nfkc_cf
= Normalizer2::getNFKCCasefoldInstance(errorCode
);
312 if (U_FAILURE(errorCode
)) {
314 "simplenormperf: failed to get Normalizer2 instances - %s\n",
315 u_errorName(errorCode
));
318 // Base line: Should remain in the fast loop without trie lookups.
319 NormalizeUTF16
op(*nfc
, CommonChars::getLatin1(maxLength
));
320 benchmark("NFC/UTF-16/latin1", op
);
323 // Base line 2: Read UTF-8, trie lookups, but should have nothing to do.
324 NormalizeUTF8
op(*nfc
, CommonChars::getJapanese(maxLength
));
325 benchmark("NFC/UTF-8/japanese", op
);
328 NormalizeUTF16
op(*nfkc_cf
, CommonChars::getMixed(maxLength
));
329 benchmark("NFKC_CF/UTF-16/mixed", op
);
332 NormalizeUTF16
op(*nfkc_cf
, CommonChars::getLowercaseLatin1(maxLength
));
333 benchmark("NFKC_CF/UTF-16/lowercaseLatin1", op
);
336 NormalizeUTF16
op(*nfkc_cf
, CommonChars::getJapanese(maxLength
));
337 benchmark("NFKC_CF/UTF-16/japanese", op
);
340 NormalizeUTF8
op(*nfkc_cf
, CommonChars::getMixed(maxLength
));
341 benchmark("NFKC_CF/UTF-8/mixed", op
);
344 NormalizeUTF8
op(*nfkc_cf
, CommonChars::getLowercaseLatin1(maxLength
));
345 benchmark("NFKC_CF/UTF-8/lowercaseLatin1", op
);
348 NormalizeUTF8
op(*nfkc_cf
, CommonChars::getJapanese(maxLength
));
349 benchmark("NFKC_CF/UTF-8/japanese", op
);