/* *********************************************************************** * © 2016 and later: Unicode, Inc. and others. * License & terms of use: http://www.unicode.org/copyright.html#License *********************************************************************** *********************************************************************** * Copyright (C) 2010-2014, International Business Machines * Corporation and others. All Rights Reserved. *********************************************************************** * file name: dicttrieperf.cpp * encoding: UTF-8 * tab size: 8 (not used) * indentation:4 * * created on: 2010dec09 * created by: Markus W. Scherer * * Performance test program for dictionary-type tries. * * Usage from within /test/perf/dicttrieperf/ : * (Linux) * make * export LD_LIBRARY_PATH=../../../lib:../../../stubdata:../../../tools/ctestfw * ./dicttrieperf --sourcedir /data/out/tmp --passes 3 --iterations 1000 * or * ./dicttrieperf -f /source/data/brkitr/thaidict.txt --passes 3 --iterations 250 */ #include #include #include "unicode/bytestrie.h" #include "unicode/bytestriebuilder.h" #include "unicode/localpointer.h" #include "unicode/ucharstrie.h" #include "unicode/ucharstriebuilder.h" #include "unicode/uperf.h" #include "unicode/utext.h" #include "charstr.h" #include "package.h" #include "toolutil.h" #include "ucbuf.h" // struct ULine #include "uoptions.h" #include "uvectr32.h" #include "cmemory.h" // for UPRV_LENGTHOF // Test object. class DictionaryTriePerfTest : public UPerfTest { public: DictionaryTriePerfTest(int32_t argc, const char *argv[], UErrorCode &status) : UPerfTest(argc, argv, NULL, 0, "", status), numTextLines(0) { if(hasFile()) { getLines(status); for(int32_t i=0; i=0x41) { ++numTextLines; // Remove trailing CR LF. int32_t len=lines[i].len; UChar c; while(len>0 && ((c=lines[i].name[len-1])==0xa || c==0xd)) { --len; } lines[i].len=len; } } } } virtual UPerfFunction *runIndexedTest(int32_t index, UBool exec, const char *&name, char *par=NULL); const char *getSourceDir() const { return sourceDir; } UBool hasFile() const { return ucharBuf!=NULL; } const ULine *getCachedLines() const { return lines; } int32_t getNumLines() const { return numLines; } int32_t numTextLines; // excluding comment lines }; // Performance test function object. // Loads icudt46l.dat (or whatever its current versioned filename) // from the -s or --sourcedir path. class PackageLookup : public UPerfFunction { protected: PackageLookup(const DictionaryTriePerfTest &perf) { IcuToolErrorCode errorCode("PackageLookup()"); CharString filename(perf.getSourceDir(), errorCode); int32_t filenameLength=filename.length(); if(filenameLength>0 && filename[filenameLength-1]!=U_FILE_SEP_CHAR && filename[filenameLength-1]!=U_FILE_ALT_SEP_CHAR) { filename.append(U_FILE_SEP_CHAR, errorCode); } filename.append(U_ICUDATA_NAME, errorCode); filename.append(".dat", errorCode); pkg.readPackage(filename.data()); } public: virtual ~PackageLookup() {} // virtual void call(UErrorCode* pErrorCode) { ... } virtual long getOperationsPerIteration() { return pkg.getItemCount(); } // virtual long getEventsPerIteration(); protected: Package pkg; }; struct TOCEntry { int32_t nameOffset, dataOffset; }; // Similar to ICU 4.6 offsetTOCLookupFn() (in ucmndata.c). static int32_t simpleBinarySearch(const char *s, const char *names, const TOCEntry *toc, int32_t count) { int32_t start=0; int32_t limit=count; int32_t lastNumber=limit; for(;;) { int32_t number=(start+limit)/2; if(lastNumber==number) { // have we moved? return -1; // not found } lastNumber=number; int32_t cmp=strcmp(s, names+toc[number].nameOffset); if(cmp<0) { limit=number; } else if(cmp>0) { start=number; } else { // found s return number; } } } class BinarySearchPackageLookup : public PackageLookup { public: BinarySearchPackageLookup(const DictionaryTriePerfTest &perf) : PackageLookup(perf) { IcuToolErrorCode errorCode("BinarySearchPackageLookup()"); int32_t count=pkg.getItemCount(); toc=new TOCEntry[count]; for(int32_t i=0; iname; itemNames.append("icudt46l/", errorCode); itemNames.append(name, strlen(name)+1, errorCode); } printf("size of item names: %6ld\n", (long)itemNames.length()); printf("size of TOC: %6ld\n", (long)(count*8)); printf("total index size: %6ld\n", (long)(itemNames.length()+count*8)); } virtual ~BinarySearchPackageLookup() { delete[] toc; } virtual void call(UErrorCode * /*pErrorCode*/) { int32_t count=pkg.getItemCount(); const char *itemNameChars=itemNames.data(); const char *name=itemNameChars; for(int32_t i=0; iname; int32_t offset=itemNames.length(); itemNames.append("icudt46l/", errorCode); itemNames.append(name, -1, errorCode); // As value, set the data item index. // In a real implementation, we would use that to get the // start and limit offset of the data item. StringPiece fullName(itemNames.toStringPiece()); fullName.remove_prefix(offset); builder->add(fullName, i, errorCode); // NUL-terminate the name for call() to find the next one. itemNames.append(0, errorCode); } int32_t length=builder->buildStringPiece(USTRINGTRIE_BUILD_SMALL, errorCode).length(); printf("size of BytesTrie: %6ld\n", (long)length); // count+1: +1 for the last-item limit offset which we should have always had printf("size of dataOffsets:%6ld\n", (long)((count+1)*4)); printf("total index size: %6ld\n", (long)(length+(count+1)*4)); } virtual ~BytesTriePackageLookup() { delete builder; } virtual void call(UErrorCode *pErrorCode) { int32_t count=pkg.getItemCount(); const char *nameTrieBytes=builder->buildStringPiece(USTRINGTRIE_BUILD_SMALL, *pErrorCode).data(); const char *name=itemNames.data(); for(int32_t i=0; i/source/data/brkitr/thaidict.txt. class DictLookup : public UPerfFunction { public: DictLookup(const DictionaryTriePerfTest &perfTest) : perf(perfTest) {} virtual long getOperationsPerIteration() { return perf.numTextLines; } protected: const DictionaryTriePerfTest &perf; }; // Closely imitate CompactTrieDictionary::matches(). // Note: CompactTrieDictionary::matches() is part of its trie implementation, // and while it loops over the text, it knows the current state. // By contrast, this implementation uses UCharsTrie API functions that have to // check the trie state each time and load/store state in the object. // (Whether it hasNext() and whether it is in the middle of a linear-match node.) static int32_t ucharsTrieMatches(UCharsTrie &trie, UText *text, int32_t textLimit, int32_t *lengths, int &count, int limit ) { UChar32 c=utext_next32(text); // Notes: // a) CompactTrieDictionary::matches() does not check for U_SENTINEL. // b) It also ignores non-BMP code points by casting to UChar! if(c<0) { return 0; } // Should be firstForCodePoint() but CompactTrieDictionary // handles only code units. UStringTrieResult result=trie.first(c); int32_t numChars=1; count=0; for(;;) { if(USTRINGTRIE_HAS_VALUE(result)) { if(count=textLimit) { // Note: Why do we have both a text limit and a UText that knows its length? break; } UChar32 c=utext_next32(text); // Notes: // a) CompactTrieDictionary::matches() does not check for U_SENTINEL. // b) It also ignores non-BMP code points by casting to UChar! if(c<0) { break; } ++numChars; // Should be nextForCodePoint() but CompactTrieDictionary // handles only code units. result=trie.next(c); } #if 0 // Note: CompactTrieDictionary::matches() comments say that it leaves the UText // after the longest prefix match and returns the number of characters // that were matched. if(index!=lastMatch) { utext_setNativeIndex(text, lastMatch); } return lastMatch-start; // However, it does not do either of these, so I am not trying to // imitate it (or its docs) 100%. #endif return numChars; } class UCharsTrieDictLookup : public DictLookup { public: UCharsTrieDictLookup(const DictionaryTriePerfTest &perfTest) : DictLookup(perfTest), trie(NULL) { IcuToolErrorCode errorCode("UCharsTrieDictLookup()"); builder=new UCharsTrieBuilder(errorCode); const ULine *lines=perf.getCachedLines(); int32_t numLines=perf.getNumLines(); for(int32_t i=0; iadd(UnicodeString(FALSE, lines[i].name, lines[i].len), 0, errorCode); } UnicodeString trieUChars; int32_t length=builder->buildUnicodeString(USTRINGTRIE_BUILD_SMALL, trieUChars, errorCode).length(); printf("size of UCharsTrie: %6ld bytes\n", (long)length*2); trie=builder->build(USTRINGTRIE_BUILD_SMALL, errorCode); } virtual ~UCharsTrieDictLookup() { delete builder; delete trie; } protected: UCharsTrieBuilder *builder; UCharsTrie *trie; }; class UCharsTrieDictMatches : public UCharsTrieDictLookup { public: UCharsTrieDictMatches(const DictionaryTriePerfTest &perfTest) : UCharsTrieDictLookup(perfTest) {} virtual void call(UErrorCode *pErrorCode) { UText text=UTEXT_INITIALIZER; int32_t lengths[20]; const ULine *lines=perf.getCachedLines(); int32_t numLines=perf.getNumLines(); for(int32_t i=0; ireset().next(lines[i].name, lines[i].len))) { fprintf(stderr, "word %ld (0-based) not found\n", (long)i); } } } }; static inline int32_t thaiCharToByte(UChar32 c) { if(0xe00<=c && c<=0xefe) { return c&0xff; } else if(c==0x2e) { return 0xff; } else { return -1; } } static UBool thaiWordToBytes(const UChar *s, int32_t length, CharString &str, UErrorCode &errorCode) { for(int32_t i=0; i=0) { str.append((char)b, errorCode); } else { fprintf(stderr, "thaiWordToBytes(): unable to encode U+%04X as a byte\n", c); return FALSE; } } return TRUE; } class BytesTrieDictLookup : public DictLookup { public: BytesTrieDictLookup(const DictionaryTriePerfTest &perfTest) : DictLookup(perfTest), trie(NULL), noDict(FALSE) { IcuToolErrorCode errorCode("BytesTrieDictLookup()"); builder=new BytesTrieBuilder(errorCode); CharString str; const ULine *lines=perf.getCachedLines(); int32_t numLines=perf.getNumLines(); for(int32_t i=0; iadd(str.toStringPiece(), 0, errorCode); } if(!noDict) { int32_t length=builder->buildStringPiece(USTRINGTRIE_BUILD_SMALL, errorCode).length(); printf("size of BytesTrie: %6ld bytes\n", (long)length); trie=builder->build(USTRINGTRIE_BUILD_SMALL, errorCode); } } virtual ~BytesTrieDictLookup() { delete builder; delete trie; } protected: BytesTrieBuilder *builder; BytesTrie *trie; UBool noDict; }; static int32_t bytesTrieMatches(BytesTrie &trie, UText *text, int32_t textLimit, int32_t *lengths, int &count, int limit ) { UChar32 c=utext_next32(text); if(c<0) { return 0; } UStringTrieResult result=trie.first(thaiCharToByte(c)); int32_t numChars=1; count=0; for(;;) { if(USTRINGTRIE_HAS_VALUE(result)) { if(count=textLimit) { break; } UChar32 c=utext_next32(text); if(c<0) { break; } ++numChars; result=trie.next(thaiCharToByte(c)); } return numChars; } class BytesTrieDictMatches : public BytesTrieDictLookup { public: BytesTrieDictMatches(const DictionaryTriePerfTest &perfTest) : BytesTrieDictLookup(perfTest) {} virtual void call(UErrorCode *pErrorCode) { if(noDict) { return; } UText text=UTEXT_INITIALIZER; int32_t lengths[20]; const ULine *lines=perf.getCachedLines(); int32_t numLines=perf.getNumLines(); for(int32_t i=0; ifirst(thaiCharToByte(line[0])); int32_t lineLength=lines[i].len; for(int32_t j=1; jnext(thaiCharToByte(line[j])); } if(!USTRINGTRIE_HAS_VALUE(result)) { fprintf(stderr, "word %ld (0-based) not found\n", (long)i); } } } }; UPerfFunction *DictionaryTriePerfTest::runIndexedTest(int32_t index, UBool exec, const char *&name, char * /*par*/) { if(hasFile()) { switch(index) { case 0: name="ucharstriematches"; if(exec) { return new UCharsTrieDictMatches(*this); } break; case 1: name="ucharstriecontains"; if(exec) { return new UCharsTrieDictContains(*this); } break; case 2: name="bytestriematches"; if(exec) { return new BytesTrieDictMatches(*this); } break; case 3: name="bytestriecontains"; if(exec) { return new BytesTrieDictContains(*this); } break; default: name=""; break; } } else { if(index==0 && exec) { puts("Running BytesTrie perf tests on the .dat package file from the --sourcedir.\n" "For UCharsTrie perf tests on a dictionary text file, specify the -f or --file-name.\n"); } switch(index) { case 0: name="simplebinarysearch"; if(exec) { return new BinarySearchPackageLookup(*this); } break; case 1: name="prefixbinarysearch"; if(exec) { return new PrefixBinarySearchPackageLookup(*this); } break; case 2: name="bytestrie"; if(exec) { return new BytesTriePackageLookup(*this); } break; default: name=""; break; } } return NULL; } int main(int argc, const char *argv[]) { IcuToolErrorCode errorCode("dicttrieperf main()"); DictionaryTriePerfTest test(argc, argv, errorCode); if(errorCode.isFailure()) { fprintf(stderr, "DictionaryTriePerfTest() failed: %s\n", errorCode.errorName()); test.usage(); return errorCode.reset(); } if(!test.run()) { fprintf(stderr, "FAILED: Tests could not be run, please check the arguments.\n"); return -1; } return 0; }