1/*
2 **********************************************************************
3 *   Copyright (C) 2010-2012, International Business Machines
4 *   Corporation and others.  All Rights Reserved.
5 **********************************************************************
6 *  file name:  dicttrieperf.cpp
7 *  encoding:   US-ASCII
8 *  tab size:   8 (not used)
9 *  indentation:4
10 *
11 *  created on: 2010dec09
12 *  created by: Markus W. Scherer
13 *
14 *  Performance test program for dictionary-type tries.
15 *
16 * Usage from within <ICU build tree>/test/perf/dicttrieperf/ :
17 * (Linux)
18 *  make
19 *  export LD_LIBRARY_PATH=../../../lib:../../../stubdata:../../../tools/ctestfw
20 *  ./dicttrieperf --sourcedir <ICU build tree>/data/out/tmp --passes 3 --iterations 1000
21 * or
22 *  ./dicttrieperf -f <ICU source tree>/source/data/brkitr/thaidict.txt --passes 3 --iterations 250
23 */
24
25#include <stdio.h>
26#include <stdlib.h>
27#include "unicode/bytestrie.h"
28#include "unicode/bytestriebuilder.h"
29#include "unicode/localpointer.h"
30#include "unicode/ucharstrie.h"
31#include "unicode/ucharstriebuilder.h"
32#include "unicode/uperf.h"
33#include "unicode/utext.h"
34#include "charstr.h"
35#include "package.h"
36#include "toolutil.h"
37#include "ucbuf.h"  // struct ULine
38#include "uoptions.h"
39#include "uvectr32.h"
40
41#define LENGTHOF(array) (int32_t)(sizeof(array)/sizeof((array)[0]))
42
43// Test object.
44class DictionaryTriePerfTest : public UPerfTest {
45public:
46    DictionaryTriePerfTest(int32_t argc, const char *argv[], UErrorCode &status)
47            : UPerfTest(argc, argv, NULL, 0, "", status), numTextLines(0) {
48        if(hasFile()) {
49            getLines(status);
50            for(int32_t i=0; i<numLines; ++i) {
51                // Skip comment lines (start with a character below 'A').
52                if(lines[i].name[0]>=0x41) {
53                    ++numTextLines;
54                    // Remove trailing CR LF.
55                    int32_t len=lines[i].len;
56                    UChar c;
57                    while(len>0 && ((c=lines[i].name[len-1])==0xa || c==0xd)) {
58                        --len;
59                    }
60                    lines[i].len=len;
61                }
62            }
63        }
64    }
65
66    virtual UPerfFunction *runIndexedTest(int32_t index, UBool exec, const char *&name, char *par=NULL);
67
68    const char *getSourceDir() const { return sourceDir; }
69
70    UBool hasFile() const { return ucharBuf!=NULL; }
71    const ULine *getCachedLines() const { return lines; }
72    int32_t getNumLines() const { return numLines; }
73    int32_t numTextLines;  // excluding comment lines
74};
75
76// Performance test function object.
77// Loads icudt46l.dat (or whatever its current versioned filename)
78// from the -s or --sourcedir path.
79class PackageLookup : public UPerfFunction {
80protected:
81    PackageLookup(const DictionaryTriePerfTest &perf) {
82        IcuToolErrorCode errorCode("PackageLookup()");
83        CharString filename(perf.getSourceDir(), errorCode);
84        int32_t filenameLength=filename.length();
85        if(filenameLength>0 && filename[filenameLength-1]!=U_FILE_SEP_CHAR &&
86                               filename[filenameLength-1]!=U_FILE_ALT_SEP_CHAR) {
87            filename.append(U_FILE_SEP_CHAR, errorCode);
88        }
89        filename.append(U_ICUDATA_NAME, errorCode);
90        filename.append(".dat", errorCode);
91        pkg.readPackage(filename.data());
92    }
93
94public:
95    virtual ~PackageLookup() {}
96
97    // virtual void call(UErrorCode* pErrorCode) { ... }
98
99    virtual long getOperationsPerIteration() {
100        return pkg.getItemCount();
101    }
102
103    // virtual long getEventsPerIteration();
104
105protected:
106    Package pkg;
107};
108
109struct TOCEntry {
110    int32_t nameOffset, dataOffset;
111};
112
113// Similar to ICU 4.6 offsetTOCLookupFn() (in ucmndata.c).
114static int32_t simpleBinarySearch(const char *s, const char *names, const TOCEntry *toc, int32_t count) {
115    int32_t start=0;
116    int32_t limit=count;
117    int32_t lastNumber=limit;
118    for(;;) {
119        int32_t number=(start+limit)/2;
120        if(lastNumber==number) {  // have we moved?
121            return -1;  // not found
122        }
123        lastNumber=number;
124        int32_t cmp=strcmp(s, names+toc[number].nameOffset);
125        if(cmp<0) {
126            limit=number;
127        } else if(cmp>0) {
128            start=number;
129        } else {  // found s
130            return number;
131        }
132    }
133}
134
135class BinarySearchPackageLookup : public PackageLookup {
136public:
137    BinarySearchPackageLookup(const DictionaryTriePerfTest &perf)
138            : PackageLookup(perf) {
139        IcuToolErrorCode errorCode("BinarySearchPackageLookup()");
140        int32_t count=pkg.getItemCount();
141        toc=new TOCEntry[count];
142        for(int32_t i=0; i<count; ++i) {
143            toc[i].nameOffset=itemNames.length();
144            toc[i].dataOffset=i;  // arbitrary value, see toc comment below
145            // The Package class removes the "icudt46l/" prefix.
146            // We restore that here for a fair performance test.
147            const char *name=pkg.getItem(i)->name;
148            itemNames.append("icudt46l/", errorCode);
149            itemNames.append(name, strlen(name)+1, errorCode);
150        }
151        printf("size of item names: %6ld\n", (long)itemNames.length());
152        printf("size of TOC:        %6ld\n", (long)(count*8));
153        printf("total index size:   %6ld\n", (long)(itemNames.length()+count*8));
154    }
155    virtual ~BinarySearchPackageLookup() {
156        delete[] toc;
157    }
158
159    virtual void call(UErrorCode * /*pErrorCode*/) {
160        int32_t count=pkg.getItemCount();
161        const char *itemNameChars=itemNames.data();
162        const char *name=itemNameChars;
163        for(int32_t i=0; i<count; ++i) {
164            if(simpleBinarySearch(name, itemNameChars, toc, count)<0) {
165                fprintf(stderr, "item not found: %s\n", name);
166            }
167            name=strchr(name, 0)+1;
168        }
169    }
170
171protected:
172    CharString itemNames;
173    // toc imitates a .dat file's array of UDataOffsetTOCEntry
174    // with nameOffset and dataOffset.
175    // We don't need the dataOffsets, but we want to imitate the real
176    // memory density, to measure equivalent CPU cache usage.
177    TOCEntry *toc;
178};
179
180#ifndef MIN
181#define MIN(a,b) (((a)<(b)) ? (a) : (b))
182#endif
183
184// Compare strings where we know the shared prefix length,
185// and advance the prefix length as we find that the strings share even more characters.
186static int32_t strcmpAfterPrefix(const char *s1, const char *s2, int32_t &prefixLength) {
187    int32_t pl=prefixLength;
188    s1+=pl;
189    s2+=pl;
190    int32_t cmp=0;
191    for(;;) {
192        int32_t c1=(uint8_t)*s1++;
193        int32_t c2=(uint8_t)*s2++;
194        cmp=c1-c2;
195        if(cmp!=0 || c1==0) {  // different or done
196            break;
197        }
198        ++pl;  // increment shared same-prefix length
199    }
200    prefixLength=pl;
201    return cmp;
202}
203
204static int32_t prefixBinarySearch(const char *s, const char *names, const TOCEntry *toc, int32_t count) {
205    if(count==0) {
206        return -1;
207    }
208    int32_t start=0;
209    int32_t limit=count;
210    // Remember the shared prefix between s, start and limit,
211    // and don't compare that shared prefix again.
212    // The shared prefix should get longer as we narrow the [start, limit[ range.
213    int32_t startPrefixLength=0;
214    int32_t limitPrefixLength=0;
215    // Prime the prefix lengths so that we don't keep prefixLength at 0 until
216    // both the start and limit indexes have moved.
217    // At the same time, we find if s is one of the start and (limit-1) names,
218    // and if not, exclude them from the actual binary search.
219    if(0==strcmpAfterPrefix(s, names+toc[0].nameOffset, startPrefixLength)) {
220        return 0;
221    }
222    ++start;
223    --limit;
224    if(0==strcmpAfterPrefix(s, names+toc[limit].nameOffset, limitPrefixLength)) {
225        return limit;
226    }
227    while(start<limit) {
228        int32_t i=(start+limit)/2;
229        int32_t prefixLength=MIN(startPrefixLength, limitPrefixLength);
230        int32_t cmp=strcmpAfterPrefix(s, names+toc[i].nameOffset, prefixLength);
231        if(cmp<0) {
232            limit=i;
233            limitPrefixLength=prefixLength;
234        } else if(cmp==0) {
235            return i;
236        } else {
237            start=i+1;
238            startPrefixLength=prefixLength;
239        }
240    }
241    return -1;
242}
243
244class PrefixBinarySearchPackageLookup : public BinarySearchPackageLookup {
245public:
246    PrefixBinarySearchPackageLookup(const DictionaryTriePerfTest &perf)
247            : BinarySearchPackageLookup(perf) {}
248
249    virtual void call(UErrorCode * /*pErrorCode*/) {
250        int32_t count=pkg.getItemCount();
251        const char *itemNameChars=itemNames.data();
252        const char *name=itemNameChars;
253        for(int32_t i=0; i<count; ++i) {
254            if(prefixBinarySearch(name, itemNameChars, toc, count)<0) {
255                fprintf(stderr, "item not found: %s\n", name);
256            }
257            name=strchr(name, 0)+1;
258        }
259    }
260};
261
262static int32_t bytesTrieLookup(const char *s, const char *nameTrieBytes) {
263    BytesTrie trie(nameTrieBytes);
264    if(USTRINGTRIE_HAS_VALUE(trie.next(s, -1))) {
265        return trie.getValue();
266    } else {
267        return -1;
268    }
269}
270
271class BytesTriePackageLookup : public PackageLookup {
272public:
273    BytesTriePackageLookup(const DictionaryTriePerfTest &perf)
274            : PackageLookup(perf) {
275        IcuToolErrorCode errorCode("BinarySearchPackageLookup()");
276        builder=new BytesTrieBuilder(errorCode);
277        int32_t count=pkg.getItemCount();
278        for(int32_t i=0; i<count; ++i) {
279            // The Package class removes the "icudt46l/" prefix.
280            // We restore that here for a fair performance test.
281            // We store all full names so that we do not have to reconstruct them
282            // in the call() function.
283            const char *name=pkg.getItem(i)->name;
284            int32_t offset=itemNames.length();
285            itemNames.append("icudt46l/", errorCode);
286            itemNames.append(name, -1, errorCode);
287            // As value, set the data item index.
288            // In a real implementation, we would use that to get the
289            // start and limit offset of the data item.
290            StringPiece fullName(itemNames.toStringPiece());
291            fullName.remove_prefix(offset);
292            builder->add(fullName, i, errorCode);
293            // NUL-terminate the name for call() to find the next one.
294            itemNames.append(0, errorCode);
295        }
296        int32_t length=builder->buildStringPiece(USTRINGTRIE_BUILD_SMALL, errorCode).length();
297        printf("size of BytesTrie:   %6ld\n", (long)length);
298        // count+1: +1 for the last-item limit offset which we should have always had
299        printf("size of dataOffsets:%6ld\n", (long)((count+1)*4));
300        printf("total index size:   %6ld\n", (long)(length+(count+1)*4));
301    }
302    virtual ~BytesTriePackageLookup() {
303        delete builder;
304    }
305
306    virtual void call(UErrorCode *pErrorCode) {
307        int32_t count=pkg.getItemCount();
308        const char *nameTrieBytes=builder->buildStringPiece(USTRINGTRIE_BUILD_SMALL, *pErrorCode).data();
309        const char *name=itemNames.data();
310        for(int32_t i=0; i<count; ++i) {
311            if(bytesTrieLookup(name, nameTrieBytes)<0) {
312                fprintf(stderr, "item not found: %s\n", name);
313            }
314            name=strchr(name, 0)+1;
315        }
316    }
317
318protected:
319    BytesTrieBuilder *builder;
320    CharString itemNames;
321};
322
323// Performance test function object.
324// Each subclass loads a dictionary text file
325// from the -s or --sourcedir path plus -f or --file-name.
326// For example, <ICU source dir>/source/data/brkitr/thaidict.txt.
327class DictLookup : public UPerfFunction {
328public:
329    DictLookup(const DictionaryTriePerfTest &perfTest) : perf(perfTest) {}
330
331    virtual long getOperationsPerIteration() {
332        return perf.numTextLines;
333    }
334
335protected:
336    const DictionaryTriePerfTest &perf;
337};
338
339// Closely imitate CompactTrieDictionary::matches().
340// Note: CompactTrieDictionary::matches() is part of its trie implementation,
341// and while it loops over the text, it knows the current state.
342// By contrast, this implementation uses UCharsTrie API functions that have to
343// check the trie state each time and load/store state in the object.
344// (Whether it hasNext() and whether it is in the middle of a linear-match node.)
345static int32_t
346ucharsTrieMatches(UCharsTrie &trie,
347                  UText *text, int32_t textLimit,
348                  int32_t *lengths, int &count, int limit ) {
349    UChar32 c=utext_next32(text);
350    // Notes:
351    // a) CompactTrieDictionary::matches() does not check for U_SENTINEL.
352    // b) It also ignores non-BMP code points by casting to UChar!
353    if(c<0) {
354        return 0;
355    }
356    // Should be firstForCodePoint() but CompactTrieDictionary
357    // handles only code units.
358    UStringTrieResult result=trie.first(c);
359    int32_t numChars=1;
360    count=0;
361    for(;;) {
362        if(USTRINGTRIE_HAS_VALUE(result)) {
363            if(count<limit) {
364                // lengths[count++]=(int32_t)utext_getNativeIndex(text);
365                lengths[count++]=numChars;  // CompactTrieDictionary just counts chars too.
366            }
367            if(result==USTRINGTRIE_FINAL_VALUE) {
368                break;
369            }
370        } else if(result==USTRINGTRIE_NO_MATCH) {
371            break;
372        }
373        if(numChars>=textLimit) {
374            // Note: Why do we have both a text limit and a UText that knows its length?
375            break;
376        }
377        UChar32 c=utext_next32(text);
378        // Notes:
379        // a) CompactTrieDictionary::matches() does not check for U_SENTINEL.
380        // b) It also ignores non-BMP code points by casting to UChar!
381        if(c<0) {
382            break;
383        }
384        ++numChars;
385        // Should be nextForCodePoint() but CompactTrieDictionary
386        // handles only code units.
387        result=trie.next(c);
388    }
389#if 0
390    // Note: CompactTrieDictionary::matches() comments say that it leaves the UText
391    // after the longest prefix match and returns the number of characters
392    // that were matched.
393    if(index!=lastMatch) {
394        utext_setNativeIndex(text, lastMatch);
395    }
396    return lastMatch-start;
397    // However, it does not do either of these, so I am not trying to
398    // imitate it (or its docs) 100%.
399#endif
400    return numChars;
401}
402
403class UCharsTrieDictLookup : public DictLookup {
404public:
405    UCharsTrieDictLookup(const DictionaryTriePerfTest &perfTest)
406            : DictLookup(perfTest), trie(NULL) {
407        IcuToolErrorCode errorCode("UCharsTrieDictLookup()");
408        builder=new UCharsTrieBuilder(errorCode);
409        const ULine *lines=perf.getCachedLines();
410        int32_t numLines=perf.getNumLines();
411        for(int32_t i=0; i<numLines; ++i) {
412            // Skip comment lines (start with a character below 'A').
413            if(lines[i].name[0]<0x41) {
414                continue;
415            }
416            builder->add(UnicodeString(FALSE, lines[i].name, lines[i].len), 0, errorCode);
417        }
418        UnicodeString trieUChars;
419        int32_t length=builder->buildUnicodeString(USTRINGTRIE_BUILD_SMALL, trieUChars, errorCode).length();
420        printf("size of UCharsTrie:          %6ld bytes\n", (long)length*2);
421        trie=builder->build(USTRINGTRIE_BUILD_SMALL, errorCode);
422    }
423
424    virtual ~UCharsTrieDictLookup() {
425        delete builder;
426        delete trie;
427    }
428
429protected:
430    UCharsTrieBuilder *builder;
431    UCharsTrie *trie;
432};
433
434class UCharsTrieDictMatches : public UCharsTrieDictLookup {
435public:
436    UCharsTrieDictMatches(const DictionaryTriePerfTest &perfTest)
437            : UCharsTrieDictLookup(perfTest) {}
438
439    virtual void call(UErrorCode *pErrorCode) {
440        UText text=UTEXT_INITIALIZER;
441        int32_t lengths[20];
442        const ULine *lines=perf.getCachedLines();
443        int32_t numLines=perf.getNumLines();
444        for(int32_t i=0; i<numLines; ++i) {
445            // Skip comment lines (start with a character below 'A').
446            if(lines[i].name[0]<0x41) {
447                continue;
448            }
449            utext_openUChars(&text, lines[i].name, lines[i].len, pErrorCode);
450            int32_t count=0;
451            ucharsTrieMatches(*trie, &text, lines[i].len,
452                              lengths, count, LENGTHOF(lengths));
453            if(count==0 || lengths[count-1]!=lines[i].len) {
454                fprintf(stderr, "word %ld (0-based) not found\n", (long)i);
455            }
456        }
457    }
458};
459
460class UCharsTrieDictContains : public UCharsTrieDictLookup {
461public:
462    UCharsTrieDictContains(const DictionaryTriePerfTest &perfTest)
463            : UCharsTrieDictLookup(perfTest) {}
464
465    virtual void call(UErrorCode * /*pErrorCode*/) {
466        const ULine *lines=perf.getCachedLines();
467        int32_t numLines=perf.getNumLines();
468        for(int32_t i=0; i<numLines; ++i) {
469            // Skip comment lines (which start with a character below 'A').
470            if(lines[i].name[0]<0x41) {
471                continue;
472            }
473            if(!USTRINGTRIE_HAS_VALUE(trie->reset().next(lines[i].name, lines[i].len))) {
474                fprintf(stderr, "word %ld (0-based) not found\n", (long)i);
475            }
476        }
477    }
478};
479
480static inline int32_t thaiCharToByte(UChar32 c) {
481    if(0xe00<=c && c<=0xefe) {
482        return c&0xff;
483    } else if(c==0x2e) {
484        return 0xff;
485    } else {
486        return -1;
487    }
488}
489
490static UBool thaiWordToBytes(const UChar *s, int32_t length,
491                             CharString &str, UErrorCode &errorCode) {
492    for(int32_t i=0; i<length; ++i) {
493        UChar c=s[i];
494        int32_t b=thaiCharToByte(c);
495        if(b>=0) {
496            str.append((char)b, errorCode);
497        } else {
498            fprintf(stderr, "thaiWordToBytes(): unable to encode U+%04X as a byte\n", c);
499            return FALSE;
500        }
501    }
502    return TRUE;
503}
504
505class BytesTrieDictLookup : public DictLookup {
506public:
507    BytesTrieDictLookup(const DictionaryTriePerfTest &perfTest)
508            : DictLookup(perfTest), trie(NULL), noDict(FALSE) {
509        IcuToolErrorCode errorCode("BytesTrieDictLookup()");
510        builder=new BytesTrieBuilder(errorCode);
511        CharString str;
512        const ULine *lines=perf.getCachedLines();
513        int32_t numLines=perf.getNumLines();
514        for(int32_t i=0; i<numLines; ++i) {
515            // Skip comment lines (start with a character below 'A').
516            if(lines[i].name[0]<0x41) {
517                continue;
518            }
519            if(!thaiWordToBytes(lines[i].name, lines[i].len, str.clear(), errorCode)) {
520                fprintf(stderr, "thaiWordToBytes(): failed for word %ld (0-based)\n", (long)i);
521                noDict=TRUE;
522                break;
523            }
524            builder->add(str.toStringPiece(), 0, errorCode);
525        }
526        if(!noDict) {
527            int32_t length=builder->buildStringPiece(USTRINGTRIE_BUILD_SMALL, errorCode).length();
528            printf("size of BytesTrie:           %6ld bytes\n", (long)length);
529            trie=builder->build(USTRINGTRIE_BUILD_SMALL, errorCode);
530        }
531    }
532
533    virtual ~BytesTrieDictLookup() {
534        delete builder;
535        delete trie;
536    }
537
538protected:
539    BytesTrieBuilder *builder;
540    BytesTrie *trie;
541    UBool noDict;
542};
543
544static int32_t
545bytesTrieMatches(BytesTrie &trie,
546                 UText *text, int32_t textLimit,
547                 int32_t *lengths, int &count, int limit ) {
548    UChar32 c=utext_next32(text);
549    if(c<0) {
550        return 0;
551    }
552    UStringTrieResult result=trie.first(thaiCharToByte(c));
553    int32_t numChars=1;
554    count=0;
555    for(;;) {
556        if(USTRINGTRIE_HAS_VALUE(result)) {
557            if(count<limit) {
558                // lengths[count++]=(int32_t)utext_getNativeIndex(text);
559                lengths[count++]=numChars;  // CompactTrieDictionary just counts chars too.
560            }
561            if(result==USTRINGTRIE_FINAL_VALUE) {
562                break;
563            }
564        } else if(result==USTRINGTRIE_NO_MATCH) {
565            break;
566        }
567        if(numChars>=textLimit) {
568            break;
569        }
570        UChar32 c=utext_next32(text);
571        if(c<0) {
572            break;
573        }
574        ++numChars;
575        result=trie.next(thaiCharToByte(c));
576    }
577    return numChars;
578}
579
580class BytesTrieDictMatches : public BytesTrieDictLookup {
581public:
582    BytesTrieDictMatches(const DictionaryTriePerfTest &perfTest)
583            : BytesTrieDictLookup(perfTest) {}
584
585    virtual void call(UErrorCode *pErrorCode) {
586        if(noDict) {
587            return;
588        }
589        UText text=UTEXT_INITIALIZER;
590        int32_t lengths[20];
591        const ULine *lines=perf.getCachedLines();
592        int32_t numLines=perf.getNumLines();
593        for(int32_t i=0; i<numLines; ++i) {
594            // Skip comment lines (start with a character below 'A').
595            if(lines[i].name[0]<0x41) {
596                continue;
597            }
598            utext_openUChars(&text, lines[i].name, lines[i].len, pErrorCode);
599            int32_t count=0;
600            bytesTrieMatches(*trie, &text, lines[i].len,
601                             lengths, count, LENGTHOF(lengths));
602            if(count==0 || lengths[count-1]!=lines[i].len) {
603                fprintf(stderr, "word %ld (0-based) not found\n", (long)i);
604            }
605        }
606    }
607};
608
609class BytesTrieDictContains : public BytesTrieDictLookup {
610public:
611    BytesTrieDictContains(const DictionaryTriePerfTest &perfTest)
612            : BytesTrieDictLookup(perfTest) {}
613
614    virtual void call(UErrorCode * /*pErrorCode*/) {
615        if(noDict) {
616            return;
617        }
618        const ULine *lines=perf.getCachedLines();
619        int32_t numLines=perf.getNumLines();
620        for(int32_t i=0; i<numLines; ++i) {
621            const UChar *line=lines[i].name;
622            // Skip comment lines (start with a character below 'A').
623            if(line[0]<0x41) {
624                continue;
625            }
626            UStringTrieResult result=trie->first(thaiCharToByte(line[0]));
627            int32_t lineLength=lines[i].len;
628            for(int32_t j=1; j<lineLength; ++j) {
629                if(!USTRINGTRIE_HAS_NEXT(result)) {
630                    fprintf(stderr, "word %ld (0-based) not found\n", (long)i);
631                    break;
632                }
633                result=trie->next(thaiCharToByte(line[j]));
634            }
635            if(!USTRINGTRIE_HAS_VALUE(result)) {
636                fprintf(stderr, "word %ld (0-based) not found\n", (long)i);
637            }
638        }
639    }
640};
641
642UPerfFunction *DictionaryTriePerfTest::runIndexedTest(int32_t index, UBool exec,
643                                                      const char *&name, char * /*par*/) {
644    if(hasFile()) {
645        switch(index) {
646        case 0:
647            name="ucharstriematches";
648            if(exec) {
649                return new UCharsTrieDictMatches(*this);
650            }
651            break;
652        case 1:
653            name="ucharstriecontains";
654            if(exec) {
655                return new UCharsTrieDictContains(*this);
656            }
657            break;
658        case 2:
659            name="bytestriematches";
660            if(exec) {
661                return new BytesTrieDictMatches(*this);
662            }
663            break;
664        case 3:
665            name="bytestriecontains";
666            if(exec) {
667                return new BytesTrieDictContains(*this);
668            }
669            break;
670        default:
671            name="";
672            break;
673        }
674    } else {
675        if(index==0 && exec) {
676            puts("Running BytesTrie perf tests on the .dat package file from the --sourcedir.\n"
677                 "For UCharsTrie perf tests on a dictionary text file, specify the -f or --file-name.\n");
678        }
679        switch(index) {
680        case 0:
681            name="simplebinarysearch";
682            if(exec) {
683                return new BinarySearchPackageLookup(*this);
684            }
685            break;
686        case 1:
687            name="prefixbinarysearch";
688            if(exec) {
689                return new PrefixBinarySearchPackageLookup(*this);
690            }
691            break;
692        case 2:
693            name="bytestrie";
694            if(exec) {
695                return new BytesTriePackageLookup(*this);
696            }
697            break;
698        default:
699            name="";
700            break;
701        }
702    }
703    return NULL;
704}
705
706int main(int argc, const char *argv[]) {
707    IcuToolErrorCode errorCode("dicttrieperf main()");
708    DictionaryTriePerfTest test(argc, argv, errorCode);
709    if(errorCode.isFailure()) {
710        fprintf(stderr, "DictionaryTriePerfTest() failed: %s\n", errorCode.errorName());
711        test.usage();
712        return errorCode.reset();
713    }
714    if(!test.run()) {
715        fprintf(stderr, "FAILED: Tests could not be run, please check the arguments.\n");
716        return -1;
717    }
718    return 0;
719}
720