]> git.saurik.com Git - apple/icu.git/blobdiff - icuSources/test/perf/normperf/simplenormperf.cpp
ICU-64232.0.1.tar.gz
[apple/icu.git] / icuSources / test / perf / normperf / simplenormperf.cpp
diff --git a/icuSources/test/perf/normperf/simplenormperf.cpp b/icuSources/test/perf/normperf/simplenormperf.cpp
new file mode 100644 (file)
index 0000000..b6865ba
--- /dev/null
@@ -0,0 +1,352 @@
+// © 2018 and later: Unicode, Inc. and others.
+// License & terms of use: http://www.unicode.org/copyright.html
+
+// simplenormperf.cpp
+// created: 2018mar15 Markus W. Scherer
+
+#include <stdio.h>
+#include <string>
+
+#include "unicode/utypes.h"
+#include "unicode/bytestream.h"
+#include "unicode/normalizer2.h"
+#include "unicode/stringpiece.h"
+#include "unicode/unistr.h"
+#include "unicode/utf8.h"
+#include "unicode/utimer.h"
+#include "cmemory.h"
+
+using icu::Normalizer2;
+using icu::UnicodeString;
+
+namespace {
+
+// Strings with commonly occurring BMP characters.
+class CommonChars {
+public:
+    static UnicodeString getMixed(int32_t minLength) {
+        return extend(UnicodeString(latin1).append(japanese).append(arabic), minLength);
+    }
+    static UnicodeString getLatin1(int32_t minLength) { return extend(latin1, minLength); }
+    static UnicodeString getLowercaseLatin1(int32_t minLength) { return extend(lowercaseLatin1, minLength); }
+    static UnicodeString getASCII(int32_t minLength) { return extend(ascii, minLength); }
+    static UnicodeString getJapanese(int32_t minLength) { return extend(japanese, minLength); }
+
+    // Returns an array of UTF-8 offsets, one per code point.
+    // Assumes all BMP characters.
+    static int32_t *toUTF8WithOffsets(const UnicodeString &s16, std::string &s8, int32_t &numCodePoints) {
+        s8.clear();
+        s8.reserve(s16.length());
+        s16.toUTF8String(s8);
+        const char *s = s8.data();
+        int32_t length = s8.length();
+        int32_t *offsets = new int32_t[length + 1];
+        int32_t numCP = 0;
+        for (int32_t i = 0; i < length;) {
+            offsets[numCP++] = i;
+            U8_FWD_1(s, i, length);
+        }
+        offsets[numCP] = length;
+        numCodePoints = numCP;
+        return offsets;
+    }
+
+private:
+    static UnicodeString extend(const UnicodeString &s, int32_t minLength) {
+        UnicodeString result(s);
+        while (result.length() < minLength) {
+            UnicodeString twice = result + result;
+            result = std::move(twice);
+        }
+        return result;
+    }
+
+    static const UChar *const latin1;
+    static const UChar *const lowercaseLatin1;
+    static const UChar *const ascii;
+    static const UChar *const japanese;
+    static const UChar *const arabic;
+};
+
+const UChar *const CommonChars::latin1 =
+      // Goethe’s Bergschloß in normal sentence case.
+      u"Da droben auf jenem Berge, da steht ein altes Schloß, "
+      u"wo hinter Toren und Türen sonst lauerten Ritter und Roß.\n"
+      u"Verbrannt sind Türen und Tore, und überall ist es so still; "
+      u"das alte verfallne Gemäuer durchklettr ich, wie ich nur will.\n"
+      u"Hierneben lag ein Keller, so voll von köstlichem Wein; "
+      u"nun steiget nicht mehr mit Krügen die Kellnerin heiter hinein.\n"
+      u"Sie setzt den Gästen im Saale nicht mehr die Becher umher, "
+      u"sie füllt zum Heiligen Mahle dem Pfaffen das Fläschchen nicht mehr.\n"
+      u"Sie reicht dem lüsternen Knappen nicht mehr auf dem Gange den Trank, "
+      u"und nimmt für flüchtige Gabe nicht mehr den flüchtigen Dank.\n"
+      u"Denn alle Balken und Decken, sie sind schon lange verbrannt, "
+      u"und Trepp und Gang und Kapelle in Schutt und Trümmer verwandt.\n"
+      u"Doch als mit Zither und Flasche nach diesen felsigen Höhn "
+      u"ich an dem heitersten Tage mein Liebchen steigen gesehn,\n"
+      u"da drängte sich frohes Behagen hervor aus verödeter Ruh, "
+      u"da gings wie in alten Tagen recht feierlich wieder zu.\n"
+      u"Als wären für stattliche Gäste die weitesten Räume bereit, "
+      u"als käm ein Pärchen gegangen aus jener tüchtigen Zeit.\n"
+      u"Als stünd in seiner Kapelle der würdige Pfaffe schon da "
+      u"und fragte: Wollt ihr einander? Wir aber lächelten: Ja!\n"
+      u"Und tief bewegten Gesänge des Herzens innigsten Grund, "
+      u"Es zeugte, statt der Menge, der Echo schallender Mund.\n"
+      u"Und als sich gegen Abend im stillen alles verlor,"
+      u"da blickte die glühende Sonne zum schroffen Gipfel empor.\n"
+      u"Und Knapp und Kellnerin glänzen als Herren weit und breit; "
+      u"sie nimmt sich zum Kredenzen und er zum Danke sich Zeit.\n";
+
+const UChar *const CommonChars::lowercaseLatin1 =
+      // Goethe’s Bergschloß in all lowercase
+      u"da droben auf jenem berge, da steht ein altes schloß, "
+      u"wo hinter toren und türen sonst lauerten ritter und roß.\n"
+      u"verbrannt sind türen und tore, und überall ist es so still; "
+      u"das alte verfallne gemäuer durchklettr ich, wie ich nur will.\n"
+      u"hierneben lag ein keller, so voll von köstlichem wein; "
+      u"nun steiget nicht mehr mit krügen die kellnerin heiter hinein.\n"
+      u"sie setzt den gästen im saale nicht mehr die becher umher, "
+      u"sie füllt zum heiligen mahle dem pfaffen das fläschchen nicht mehr.\n"
+      u"sie reicht dem lüsternen knappen nicht mehr auf dem gange den trank, "
+      u"und nimmt für flüchtige gabe nicht mehr den flüchtigen dank.\n"
+      u"denn alle balken und decken, sie sind schon lange verbrannt, "
+      u"und trepp und gang und kapelle in schutt und trümmer verwandt.\n"
+      u"doch als mit zither und flasche nach diesen felsigen höhn "
+      u"ich an dem heitersten tage mein liebchen steigen gesehn,\n"
+      u"da drängte sich frohes behagen hervor aus verödeter ruh, "
+      u"da gings wie in alten tagen recht feierlich wieder zu.\n"
+      u"als wären für stattliche gäste die weitesten räume bereit, "
+      u"als käm ein pärchen gegangen aus jener tüchtigen zeit.\n"
+      u"als stünd in seiner kapelle der würdige pfaffe schon da "
+      u"und fragte: wollt ihr einander? wir aber lächelten: ja!\n"
+      u"und tief bewegten gesänge des herzens innigsten grund, "
+      u"es zeugte, statt der menge, der echo schallender mund.\n"
+      u"und als sich gegen abend im stillen alles verlor,"
+      u"da blickte die glühende sonne zum schroffen gipfel empor.\n"
+      u"und knapp und kellnerin glänzen als herren weit und breit; "
+      u"sie nimmt sich zum kredenzen und er zum danke sich zeit.\n";
+
+const UChar *const CommonChars::ascii =
+      // Goethe’s Bergschloß in normal sentence case but ASCII-fied
+      u"Da droben auf jenem Berge, da steht ein altes Schloss, "
+      u"wo hinter Toren und Tueren sonst lauerten Ritter und Ross.\n"
+      u"Verbrannt sind Tueren und Tore, und ueberall ist es so still; "
+      u"das alte verfallne Gemaeuer durchklettr ich, wie ich nur will.\n"
+      u"Hierneben lag ein Keller, so voll von koestlichem Wein; "
+      u"nun steiget nicht mehr mit Kruegen die Kellnerin heiter hinein.\n"
+      u"Sie setzt den Gaesten im Saale nicht mehr die Becher umher, "
+      u"sie fuellt zum Heiligen Mahle dem Pfaffen das Flaeschchen nicht mehr.\n"
+      u"Sie reicht dem luesternen Knappen nicht mehr auf dem Gange den Trank, "
+      u"und nimmt fuer fluechtige Gabe nicht mehr den fluechtigen Dank.\n"
+      u"Denn alle Balken und Decken, sie sind schon lange verbrannt, "
+      u"und Trepp und Gang und Kapelle in Schutt und Truemmer verwandt.\n"
+      u"Doch als mit Zither und Flasche nach diesen felsigen Hoehn "
+      u"ich an dem heitersten Tage mein Liebchen steigen gesehn,\n"
+      u"da draengte sich frohes Behagen hervor aus veroedeter Ruh, "
+      u"da gings wie in alten Tagen recht feierlich wieder zu.\n"
+      u"Als waeren fuer stattliche Gaeste die weitesten Raeume bereit, "
+      u"als kaem ein Paerchen gegangen aus jener tuechtigen Zeit.\n"
+      u"Als stuend in seiner Kapelle der wuerdige Pfaffe schon da "
+      u"und fragte: Wollt ihr einander? Wir aber laechelten: Ja!\n"
+      u"Und tief bewegten Gesaenge des Herzens innigsten Grund, "
+      u"Es zeugte, statt der Menge, der Echo schallender Mund.\n"
+      u"Und als sich gegen Abend im stillen alles verlor,"
+      u"da blickte die gluehende Sonne zum schroffen Gipfel empor.\n"
+      u"Und Knapp und Kellnerin glaenzen als Herren weit und breit; "
+      u"sie nimmt sich zum Kredenzen und er zum Danke sich Zeit.\n";
+
+const UChar *const CommonChars::japanese =
+      // Ame ni mo makezu = Be not Defeated by the Rain, by Kenji Miyazawa.
+      u"雨にもまけず風にもまけず雪にも夏の暑さにもまけぬ"
+      u"丈夫なからだをもち慾はなく決して瞋らず"
+      u"いつもしずかにわらっている一日に玄米四合と"
+      u"味噌と少しの野菜をたべあらゆることを"
+      u"じぶんをかんじょうにいれずによくみききしわかり"
+      u"そしてわすれず野原の松の林の蔭の"
+      u"小さな萱ぶきの小屋にいて東に病気のこどもあれば"
+      u"行って看病してやり西につかれた母あれば"
+      u"行ってその稲の束を負い南に死にそうな人あれば"
+      u"行ってこわがらなくてもいいといい"
+      u"北にけんかやそしょうがあれば"
+      u"つまらないからやめろといいひでりのときはなみだをながし"
+      u"さむさのなつはおろおろあるきみんなにでくのぼうとよばれ"
+      u"ほめられもせずくにもされずそういうものにわたしはなりたい";
+
+const UChar *const CommonChars::arabic =
+      // Some Arabic for variety. "What is Unicode?"
+      // http://www.unicode.org/standard/translations/arabic.html
+      u"تتعامل الحواسيب بالأسام مع الأرقام فقط، "
+      u"و تخزن الحروف و المحارف "
+      u"الأخرى بتخصيص رقم لكل واحد "
+      u"منها. قبل اختراع يونيكود كان هناك ";
+
+// TODO: class BenchmarkPerCodePoint?
+
+class Operation {
+public:
+    Operation() {}
+    virtual ~Operation();
+    virtual double call(int32_t iterations, int32_t pieceLength) = 0;
+
+protected:
+    UTimer startTime;
+};
+
+Operation::~Operation() {}
+
+const int32_t kLengths[] = { 5, 12, 30, 100, 1000, 10000 };
+
+int32_t getMaxLength() { return kLengths[UPRV_LENGTHOF(kLengths) - 1]; }
+
+// Returns seconds per code point.
+double measure(Operation &op, int32_t pieceLength) {
+    // Increase the number of iterations until we use at least one second.
+    int32_t iterations = 1;
+    for (;;) {
+        double seconds = op.call(iterations, pieceLength);
+        if (seconds >= 1) {
+            if (iterations > 1) {
+                return seconds / (iterations * pieceLength);
+            } else {
+                // Run it once more, to avoid measuring only the warm-up.
+                return op.call(1, pieceLength) / (iterations * pieceLength);
+            }
+        }
+        if (seconds < 0.01) {
+            iterations *= 10;
+        } else if (seconds < 0.55) {
+            iterations *= 1.1 / seconds;
+        } else {
+            iterations *= 2;
+        }
+    }
+}
+
+void benchmark(const char *name, Operation &op) {
+    for (int32_t i = 0; i < UPRV_LENGTHOF(kLengths); ++i) {
+        int32_t pieceLength = kLengths[i];
+        double secPerCp = measure(op, pieceLength);
+        printf("%s  %6d  %12f ns/cp\n", name, (int)pieceLength, secPerCp * 1000000000);
+    }
+    puts("");
+}
+
+class NormalizeUTF16 : public Operation {
+public:
+    NormalizeUTF16(const Normalizer2 &n2, const UnicodeString &text) :
+            norm2(n2), src(text), s(src.getBuffer()) {}
+    virtual ~NormalizeUTF16();
+    virtual double call(int32_t iterations, int32_t pieceLength);
+
+private:
+    const Normalizer2 &norm2;
+    UnicodeString src;
+    const UChar *s;
+    UnicodeString dest;
+};
+
+NormalizeUTF16::~NormalizeUTF16() {}
+
+// Assumes all BMP characters.
+double NormalizeUTF16::call(int32_t iterations, int32_t pieceLength) {
+    int32_t start = 0;
+    int32_t limit = src.length() - pieceLength;
+    UnicodeString piece;
+    UErrorCode errorCode = U_ZERO_ERROR;
+    utimer_getTime(&startTime);
+    for (int32_t i = 0; i < iterations; ++i) {
+        piece.setTo(FALSE, s + start, pieceLength);
+        norm2.normalize(piece, dest, errorCode);
+        start = (start + pieceLength) % limit;
+    }
+    return utimer_getElapsedSeconds(&startTime);
+}
+
+class NormalizeUTF8 : public Operation {
+public:
+    NormalizeUTF8(const Normalizer2 &n2, const UnicodeString &text) : norm2(n2), sink(&dest) {
+        offsets = CommonChars::toUTF8WithOffsets(text, src, numCodePoints);
+        s = src.data();
+    }
+    virtual ~NormalizeUTF8();
+    virtual double call(int32_t iterations, int32_t pieceLength);
+
+private:
+    const Normalizer2 &norm2;
+    std::string src;
+    const char *s;
+    int32_t *offsets;
+    int32_t numCodePoints;
+    std::string dest;
+    icu::StringByteSink<std::string> sink;
+};
+
+NormalizeUTF8::~NormalizeUTF8() {
+    delete[] offsets;
+}
+
+double NormalizeUTF8::call(int32_t iterations, int32_t pieceLength) {
+    int32_t start = 0;
+    int32_t limit = numCodePoints - pieceLength;
+    UErrorCode errorCode = U_ZERO_ERROR;
+    utimer_getTime(&startTime);
+    for (int32_t i = 0; i < iterations; ++i) {
+        int32_t start8 = offsets[start];
+        int32_t limit8 = offsets[start + pieceLength];
+        icu::StringPiece piece(s + start8, limit8 - start8);
+        norm2.normalizeUTF8(0, piece, sink, nullptr, errorCode);
+        start = (start + pieceLength) % limit;
+    }
+    return utimer_getElapsedSeconds(&startTime);
+}
+
+}  // namespace
+
+extern int main(int /*argc*/, const char * /*argv*/[]) {
+    // More than the longest piece length so that we read from different parts of the string
+    // for that piece length.
+    int32_t maxLength = getMaxLength() * 10;
+    UErrorCode errorCode = U_ZERO_ERROR;
+    const Normalizer2 *nfc = Normalizer2::getNFCInstance(errorCode);
+    const Normalizer2 *nfkc_cf = Normalizer2::getNFKCCasefoldInstance(errorCode);
+    if (U_FAILURE(errorCode)) {
+        fprintf(stderr,
+                "simplenormperf: failed to get Normalizer2 instances - %s\n",
+                u_errorName(errorCode));
+    }
+    {
+        // Base line: Should remain in the fast loop without trie lookups.
+        NormalizeUTF16 op(*nfc, CommonChars::getLatin1(maxLength));
+        benchmark("NFC/UTF-16/latin1", op);
+    }
+    {
+        // Base line 2: Read UTF-8, trie lookups, but should have nothing to do.
+        NormalizeUTF8 op(*nfc, CommonChars::getJapanese(maxLength));
+        benchmark("NFC/UTF-8/japanese", op);
+    }
+    {
+        NormalizeUTF16 op(*nfkc_cf, CommonChars::getMixed(maxLength));
+        benchmark("NFKC_CF/UTF-16/mixed", op);
+    }
+    {
+        NormalizeUTF16 op(*nfkc_cf, CommonChars::getLowercaseLatin1(maxLength));
+        benchmark("NFKC_CF/UTF-16/lowercaseLatin1", op);
+    }
+    {
+        NormalizeUTF16 op(*nfkc_cf, CommonChars::getJapanese(maxLength));
+        benchmark("NFKC_CF/UTF-16/japanese", op);
+    }
+    {
+        NormalizeUTF8 op(*nfkc_cf, CommonChars::getMixed(maxLength));
+        benchmark("NFKC_CF/UTF-8/mixed", op);
+    }
+    {
+        NormalizeUTF8 op(*nfkc_cf, CommonChars::getLowercaseLatin1(maxLength));
+        benchmark("NFKC_CF/UTF-8/lowercaseLatin1", op);
+    }
+    {
+        NormalizeUTF8 op(*nfkc_cf, CommonChars::getJapanese(maxLength));
+        benchmark("NFKC_CF/UTF-8/japanese", op);
+    }
+    return 0;
+}