modernc.org/cc@v1.0.1/v2/testdata/_sqlite/test/kvtest.c (about)

     1  /*
     2  ** 2016-12-28
     3  **
     4  ** The author disclaims copyright to this source code.  In place of
     5  ** a legal notice, here is a blessing:
     6  **
     7  **    May you do good and not evil.
     8  **    May you find forgiveness for yourself and forgive others.
     9  **    May you share freely, never taking more than you give.
    10  **
    11  *************************************************************************
    12  **
    13  ** This file implements "key-value" performance test for SQLite.  The
    14  ** purpose is to compare the speed of SQLite for accessing large BLOBs
    15  ** versus reading those same BLOB values out of individual files in the
    16  ** filesystem.
    17  **
    18  ** Run "kvtest" with no arguments for on-line help, or see comments below.
    19  **
    20  ** HOW TO COMPILE:
    21  **
    22  ** (1) Gather this source file and a recent SQLite3 amalgamation with its
    23  **     header into the working directory.  You should have:
    24  **
    25  **          kvtest.c       >--- this file
    26  **          sqlite3.c      \___ SQLite
    27  **          sqlite3.h      /    amlagamation & header
    28  **
    29  ** (2) Run you compiler against the two C source code files.
    30  **
    31  **    (a) On linux or mac:
    32  **
    33  **        OPTS="-DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION"
    34  **        gcc -Os -I. $OPTS kvtest.c sqlite3.c -o kvtest
    35  **
    36  **             The $OPTS options can be omitted.  The $OPTS merely omit
    37  **             the need to link against -ldl and -lpthread, or whatever
    38  **             the equivalent libraries are called on your system.
    39  **
    40  **    (b) Windows with MSVC:
    41  **
    42  **        cl -I. kvtest.c sqlite3.c
    43  **
    44  ** USAGE:
    45  **
    46  ** (1) Create a test database by running "kvtest init" with appropriate
    47  **     options.  See the help message for available options.
    48  **
    49  ** (2) Construct the corresponding pile-of-files database on disk using
    50  **     the "kvtest export" command.
    51  **
    52  ** (3) Run tests using "kvtest run" against either the SQLite database or
    53  **     the pile-of-files database and with appropriate options.
    54  **
    55  ** For example:
    56  **
    57  **       ./kvtest init x1.db --count 100000 --size 10000
    58  **       mkdir x1
    59  **       ./kvtest export x1.db x1
    60  **       ./kvtest run x1.db --count 10000 --max-id 1000000
    61  **       ./kvtest run x1 --count 10000 --max-id 1000000
    62  */
    63  static const char zHelp[] = 
    64  "Usage: kvtest COMMAND ARGS...\n"
    65  "\n"
    66  "   kvtest init DBFILE --count N --size M --pagesize X\n"
    67  "\n"
    68  "        Generate a new test database file named DBFILE containing N\n"
    69  "        BLOBs each of size M bytes.  The page size of the new database\n"
    70  "        file will be X.  Additional options:\n"
    71  "\n"
    72  "           --variance V           Randomly vary M by plus or minus V\n"
    73  "\n"
    74  "   kvtest export DBFILE DIRECTORY [--tree]\n"
    75  "\n"
    76  "        Export all the blobs in the kv table of DBFILE into separate\n"
    77  "        files in DIRECTORY.  DIRECTORY is created if it does not previously\n"
    78  "        exist.  If the --tree option is used, then the blobs are written\n"
    79  "        into a hierarchy of directories, using names like 00/00/00,\n"
    80  "        00/00/01, 00/00/02, and so forth.  Without the --tree option, all\n"
    81  "        files are in the top-level directory with names like 000000, 000001,\n"
    82  "        000002, and so forth.\n"
    83  "\n"
    84  "   kvtest stat DBFILE [options]\n"
    85  "\n"
    86  "        Display summary information about DBFILE.  Options:\n"
    87  "\n"
    88  "           --vacuum               Run VACUUM on the database file\n"
    89  "\n"
    90  "   kvtest run DBFILE [options]\n"
    91  "\n"
    92  "        Run a performance test.  DBFILE can be either the name of a\n"
    93  "        database or a directory containing sample files.  Options:\n"
    94  "\n"
    95  "           --asc                  Read blobs in ascending order\n"
    96  "           --blob-api             Use the BLOB API\n"
    97  "           --cache-size N         Database cache size\n"
    98  "           --count N              Read N blobs\n"
    99  "           --desc                 Read blobs in descending order\n"
   100  "           --fsync                Synchronous file writes\n"
   101  "           --integrity-check      Run \"PRAGMA integrity_check\" after test\n"
   102  "           --max-id N             Maximum blob key to use\n"
   103  "           --mmap N               Mmap as much as N bytes of DBFILE\n"
   104  "           --multitrans           Each read or write in its own transaction\n"
   105  "           --nocheckpoint         Omit the checkpoint on WAL mode writes\n"
   106  "           --nosync               Set \"PRAGMA synchronous=OFF\"\n"
   107  "           --jmode MODE           Set MODE journal mode prior to starting\n"
   108  "           --random               Read blobs in a random order\n"
   109  "           --start N              Start reading with this blob key\n"
   110  "           --stats                Output operating stats before exiting\n"
   111  "           --update               Do an overwrite test\n"
   112  ;
   113  
   114  /* Reference resources used */
   115  #include <stdio.h>
   116  #include <stdlib.h>
   117  #include <sys/types.h>
   118  #include <sys/stat.h>
   119  #include <assert.h>
   120  #include <string.h>
   121  #include "sqlite3.h"
   122  
   123  #ifndef _WIN32
   124  # include <unistd.h>
   125  #else
   126    /* Provide Windows equivalent for the needed parts of unistd.h */
   127  # include <direct.h>
   128  # include <io.h>
   129  # define R_OK 2
   130  # define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
   131  # define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
   132  # define access _access
   133  #endif
   134  
   135  #include <stdint.h>
   136  
   137  /*
   138  ** The following macros are used to cast pointers to integers and
   139  ** integers to pointers.  The way you do this varies from one compiler
   140  ** to the next, so we have developed the following set of #if statements
   141  ** to generate appropriate macros for a wide range of compilers.
   142  **
   143  ** The correct "ANSI" way to do this is to use the intptr_t type.
   144  ** Unfortunately, that typedef is not available on all compilers, or
   145  ** if it is available, it requires an #include of specific headers
   146  ** that vary from one machine to the next.
   147  **
   148  ** Ticket #3860:  The llvm-gcc-4.2 compiler from Apple chokes on
   149  ** the ((void*)&((char*)0)[X]) construct.  But MSVC chokes on ((void*)(X)).
   150  ** So we have to define the macros in different ways depending on the
   151  ** compiler.
   152  */
   153  #if defined(__PTRDIFF_TYPE__)  /* This case should work for GCC */
   154  # define SQLITE_INT_TO_PTR(X)  ((void*)(__PTRDIFF_TYPE__)(X))
   155  # define SQLITE_PTR_TO_INT(X)  ((sqlite3_int64)(__PTRDIFF_TYPE__)(X))
   156  #else
   157  # define SQLITE_INT_TO_PTR(X)  ((void*)(intptr_t)(X))
   158  # define SQLITE_PTR_TO_INT(X)  ((sqlite3_int64)(intptr_t)(X))
   159  #endif
   160  
   161  /*
   162  ** Show thqe help text and quit.
   163  */
   164  static void showHelp(void){
   165    fprintf(stdout, "%s", zHelp);
   166    exit(1);
   167  }
   168  
   169  /*
   170  ** Show an error message an quit.
   171  */
   172  static void fatalError(const char *zFormat, ...){
   173    va_list ap;
   174    fprintf(stdout, "ERROR: ");
   175    va_start(ap, zFormat);
   176    vfprintf(stdout, zFormat, ap);
   177    va_end(ap);
   178    fprintf(stdout, "\n");
   179    exit(1);
   180  }
   181  
   182  /*
   183  ** Return the value of a hexadecimal digit.  Return -1 if the input
   184  ** is not a hex digit.
   185  */
   186  static int hexDigitValue(char c){
   187    if( c>='0' && c<='9' ) return c - '0';
   188    if( c>='a' && c<='f' ) return c - 'a' + 10;
   189    if( c>='A' && c<='F' ) return c - 'A' + 10;
   190    return -1;
   191  }
   192  
   193  /*
   194  ** Interpret zArg as an integer value, possibly with suffixes.
   195  */
   196  static int integerValue(const char *zArg){
   197    int v = 0;
   198    static const struct { char *zSuffix; int iMult; } aMult[] = {
   199      { "KiB", 1024 },
   200      { "MiB", 1024*1024 },
   201      { "GiB", 1024*1024*1024 },
   202      { "KB",  1000 },
   203      { "MB",  1000000 },
   204      { "GB",  1000000000 },
   205      { "K",   1000 },
   206      { "M",   1000000 },
   207      { "G",   1000000000 },
   208    };
   209    int i;
   210    int isNeg = 0;
   211    if( zArg[0]=='-' ){
   212      isNeg = 1;
   213      zArg++;
   214    }else if( zArg[0]=='+' ){
   215      zArg++;
   216    }
   217    if( zArg[0]=='0' && zArg[1]=='x' ){
   218      int x;
   219      zArg += 2;
   220      while( (x = hexDigitValue(zArg[0]))>=0 ){
   221        v = (v<<4) + x;
   222        zArg++;
   223      }
   224    }else{
   225      while( zArg[0]>='0' && zArg[0]<='9' ){
   226        v = v*10 + zArg[0] - '0';
   227        zArg++;
   228      }
   229    }
   230    for(i=0; i<sizeof(aMult)/sizeof(aMult[0]); i++){
   231      if( sqlite3_stricmp(aMult[i].zSuffix, zArg)==0 ){
   232        v *= aMult[i].iMult;
   233        break;
   234      }
   235    }
   236    return isNeg? -v : v;
   237  }
   238  
   239  
   240  /*
   241  ** Check the filesystem object zPath.  Determine what it is:
   242  **
   243  **    PATH_DIR     A single directory holding many files
   244  **    PATH_TREE    A directory hierarchy with files at the leaves
   245  **    PATH_DB      An SQLite database
   246  **    PATH_NEXIST  Does not exist
   247  **    PATH_OTHER   Something else
   248  **
   249  ** PATH_DIR means all of the separate files are grouped together
   250  ** into a single directory with names like 000000, 000001, 000002, and
   251  ** so forth.  PATH_TREE means there is a hierarchy of directories so
   252  ** that no single directory has too many entries.  The files have names
   253  ** like 00/00/00, 00/00/01, 00/00/02 and so forth.  The decision between
   254  ** PATH_DIR and PATH_TREE is determined by the presence of a subdirectory
   255  ** named "00" at the top-level.
   256  */
   257  #define PATH_DIR     1
   258  #define PATH_TREE    2
   259  #define PATH_DB      3
   260  #define PATH_NEXIST  0
   261  #define PATH_OTHER   99
   262  static int pathType(const char *zPath){
   263    struct stat x;
   264    int rc;
   265    if( access(zPath,R_OK) ) return PATH_NEXIST;
   266    memset(&x, 0, sizeof(x));
   267    rc = stat(zPath, &x);
   268    if( rc<0 ) return PATH_OTHER;
   269    if( S_ISDIR(x.st_mode) ){
   270      char *zLayer1 = sqlite3_mprintf("%s/00", zPath);
   271      memset(&x, 0, sizeof(x));
   272      rc = stat(zLayer1, &x);
   273      sqlite3_free(zLayer1);
   274      if( rc<0 ) return PATH_DIR;
   275      if( S_ISDIR(x.st_mode) ) return PATH_TREE;
   276      return PATH_DIR;
   277    }
   278    if( (x.st_size%512)==0 ) return PATH_DB;
   279    return PATH_OTHER;
   280  }
   281  
   282  /*
   283  ** Return the size of a file in bytes.  Or return -1 if the
   284  ** named object is not a regular file or does not exist.
   285  */
   286  static sqlite3_int64 fileSize(const char *zPath){
   287    struct stat x;
   288    int rc;
   289    memset(&x, 0, sizeof(x));
   290    rc = stat(zPath, &x);
   291    if( rc<0 ) return -1;
   292    if( !S_ISREG(x.st_mode) ) return -1;
   293    return x.st_size;
   294  }
   295  
   296  /*
   297  ** A Pseudo-random number generator with a fixed seed.  Use this so
   298  ** that the same sequence of "random" numbers are generated on each
   299  ** run, for repeatability.
   300  */
   301  static unsigned int randInt(void){
   302    static unsigned int x = 0x333a13cd;
   303    static unsigned int y = 0xecb2adea;
   304    x = (x>>1) ^ ((1+~(x&1)) & 0xd0000001);
   305    y = y*1103515245 + 12345;
   306    return x^y;
   307  }
   308  
   309  /*
   310  ** Do database initialization.
   311  */
   312  static int initMain(int argc, char **argv){
   313    char *zDb;
   314    int i, rc;
   315    int nCount = 1000;
   316    int sz = 10000;
   317    int iVariance = 0;
   318    int pgsz = 4096;
   319    sqlite3 *db;
   320    char *zSql;
   321    char *zErrMsg = 0;
   322  
   323    assert( strcmp(argv[1],"init")==0 );
   324    assert( argc>=3 );
   325    zDb = argv[2];
   326    for(i=3; i<argc; i++){
   327      char *z = argv[i];
   328      if( z[0]!='-' ) fatalError("unknown argument: \"%s\"", z);
   329      if( z[1]=='-' ) z++;
   330      if( strcmp(z, "-count")==0 ){
   331        if( i==argc-1 ) fatalError("missing argument on \"%s\"", argv[i]);
   332        nCount = integerValue(argv[++i]);
   333        if( nCount<1 ) fatalError("the --count must be positive");
   334        continue;
   335      }
   336      if( strcmp(z, "-size")==0 ){
   337        if( i==argc-1 ) fatalError("missing argument on \"%s\"", argv[i]);
   338        sz = integerValue(argv[++i]);
   339        if( sz<1 ) fatalError("the --size must be positive");
   340        continue;
   341      }
   342      if( strcmp(z, "-variance")==0 ){
   343        if( i==argc-1 ) fatalError("missing argument on \"%s\"", argv[i]);
   344        iVariance = integerValue(argv[++i]);
   345        continue;
   346      }
   347      if( strcmp(z, "-pagesize")==0 ){
   348        if( i==argc-1 ) fatalError("missing argument on \"%s\"", argv[i]);
   349        pgsz = integerValue(argv[++i]);
   350        if( pgsz<512 || pgsz>65536 || ((pgsz-1)&pgsz)!=0 ){
   351          fatalError("the --pagesize must be power of 2 between 512 and 65536");
   352        }
   353        continue;
   354      }
   355      fatalError("unknown option: \"%s\"", argv[i]);
   356    }
   357    rc = sqlite3_open(zDb, &db);
   358    if( rc ){
   359      fatalError("cannot open database \"%s\": %s", zDb, sqlite3_errmsg(db));
   360    }
   361    zSql = sqlite3_mprintf(
   362      "DROP TABLE IF EXISTS kv;\n"
   363      "PRAGMA page_size=%d;\n"
   364      "VACUUM;\n"
   365      "BEGIN;\n"
   366      "CREATE TABLE kv(k INTEGER PRIMARY KEY, v BLOB);\n"
   367      "WITH RECURSIVE c(x) AS (VALUES(1) UNION ALL SELECT x+1 FROM c WHERE x<%d)"
   368      " INSERT INTO kv(k,v) SELECT x, randomblob(%d+(random()%%(%d))) FROM c;\n"
   369      "COMMIT;\n",
   370      pgsz, nCount, sz, iVariance+1
   371    );
   372    rc = sqlite3_exec(db, zSql, 0, 0, &zErrMsg);
   373    if( rc ) fatalError("database create failed: %s", zErrMsg);
   374    sqlite3_free(zSql);
   375    sqlite3_close(db);
   376    return 0;
   377  }
   378  
   379  /*
   380  ** Analyze an existing database file.  Report its content.
   381  */
   382  static int statMain(int argc, char **argv){
   383    char *zDb;
   384    int i, rc;
   385    sqlite3 *db;
   386    char *zSql;
   387    sqlite3_stmt *pStmt;
   388    int doVacuum = 0;
   389  
   390    assert( strcmp(argv[1],"stat")==0 );
   391    assert( argc>=3 );
   392    zDb = argv[2];
   393    for(i=3; i<argc; i++){
   394      char *z = argv[i];
   395      if( z[0]!='-' ) fatalError("unknown argument: \"%s\"", z);
   396      if( z[1]=='-' ) z++;
   397      if( strcmp(z, "-vacuum")==0 ){
   398        doVacuum = 1;
   399        continue;
   400      }
   401      fatalError("unknown option: \"%s\"", argv[i]);
   402    }
   403    rc = sqlite3_open(zDb, &db);
   404    if( rc ){
   405      fatalError("cannot open database \"%s\": %s", zDb, sqlite3_errmsg(db));
   406    }
   407    if( doVacuum ){
   408      printf("Vacuuming...."); fflush(stdout);
   409      sqlite3_exec(db, "VACUUM", 0, 0, 0);
   410      printf("       done\n");
   411    }
   412    zSql = sqlite3_mprintf(
   413      "SELECT count(*), min(length(v)), max(length(v)), avg(length(v))"
   414      "  FROM kv"
   415    );
   416    rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0);
   417    if( rc ) fatalError("cannot prepare SQL [%s]: %s", zSql, sqlite3_errmsg(db));
   418    sqlite3_free(zSql);
   419    if( sqlite3_step(pStmt)==SQLITE_ROW ){
   420      printf("Number of entries:  %8d\n", sqlite3_column_int(pStmt, 0));
   421      printf("Average value size: %8d\n", sqlite3_column_int(pStmt, 3));
   422      printf("Minimum value size: %8d\n", sqlite3_column_int(pStmt, 1));
   423      printf("Maximum value size: %8d\n", sqlite3_column_int(pStmt, 2));
   424    }else{
   425      printf("No rows\n");
   426    }
   427    sqlite3_finalize(pStmt);
   428    zSql = sqlite3_mprintf("PRAGMA page_size");
   429    rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0);
   430    if( rc ) fatalError("cannot prepare SQL [%s]: %s", zSql, sqlite3_errmsg(db));
   431    sqlite3_free(zSql);
   432    if( sqlite3_step(pStmt)==SQLITE_ROW ){
   433      printf("Page-size:          %8d\n", sqlite3_column_int(pStmt, 0));
   434    }
   435    sqlite3_finalize(pStmt);
   436    zSql = sqlite3_mprintf("PRAGMA page_count");
   437    rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0);
   438    if( rc ) fatalError("cannot prepare SQL [%s]: %s", zSql, sqlite3_errmsg(db));
   439    sqlite3_free(zSql);
   440    if( sqlite3_step(pStmt)==SQLITE_ROW ){
   441      printf("Page-count:         %8d\n", sqlite3_column_int(pStmt, 0));
   442    }
   443    sqlite3_finalize(pStmt);
   444    zSql = sqlite3_mprintf("PRAGMA freelist_count");
   445    rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0);
   446    if( rc ) fatalError("cannot prepare SQL [%s]: %s", zSql, sqlite3_errmsg(db));
   447    sqlite3_free(zSql);
   448    if( sqlite3_step(pStmt)==SQLITE_ROW ){
   449      printf("Freelist-count:     %8d\n", sqlite3_column_int(pStmt, 0));
   450    }
   451    sqlite3_finalize(pStmt);
   452    rc = sqlite3_prepare_v2(db, "PRAGMA integrity_check(10)", -1, &pStmt, 0);
   453    if( rc ) fatalError("cannot prepare integrity check: %s", sqlite3_errmsg(db));
   454    while( sqlite3_step(pStmt)==SQLITE_ROW ){
   455      printf("Integrity-check:    %s\n", sqlite3_column_text(pStmt, 0));
   456    }
   457    sqlite3_finalize(pStmt);
   458    sqlite3_close(db);
   459    return 0;
   460  }
   461  
   462  /*
   463  **      remember(V,PTR)
   464  **
   465  ** Return the integer value V.  Also save the value of V in a
   466  ** C-language variable whose address is PTR.
   467  */
   468  static void rememberFunc(
   469    sqlite3_context *pCtx,
   470    int argc,
   471    sqlite3_value **argv
   472  ){
   473    sqlite3_int64 v;
   474    sqlite3_int64 ptr;
   475    assert( argc==2 );
   476    v = sqlite3_value_int64(argv[0]);
   477    ptr = sqlite3_value_int64(argv[1]);
   478    *(sqlite3_int64*)SQLITE_INT_TO_PTR(ptr) = v;
   479    sqlite3_result_int64(pCtx, v);
   480  }
   481  
   482  /*
   483  ** Make sure a directory named zDir exists.
   484  */
   485  static void kvtest_mkdir(const char *zDir){
   486  #if defined(_WIN32)
   487    (void)mkdir(zDir);
   488  #else
   489    (void)mkdir(zDir, 0755);
   490  #endif
   491  }
   492  
   493  /*
   494  ** Export the kv table to individual files in the filesystem
   495  */
   496  static int exportMain(int argc, char **argv){
   497    char *zDb;
   498    char *zDir;
   499    sqlite3 *db;
   500    sqlite3_stmt *pStmt;
   501    int rc;
   502    int ePathType;
   503    int nFN;
   504    char *zFN;
   505    char *zTail;
   506    size_t nWrote;
   507    int i;
   508  
   509    assert( strcmp(argv[1],"export")==0 );
   510    assert( argc>=3 );
   511    if( argc<4 ) fatalError("Usage: kvtest export DATABASE DIRECTORY [OPTIONS]");
   512    zDb = argv[2];
   513    zDir = argv[3];
   514    kvtest_mkdir(zDir);
   515    for(i=4; i<argc; i++){
   516      const char *z = argv[i];
   517      if( z[0]=='-' && z[1]=='-' ) z++;
   518      if( strcmp(z,"-tree")==0 ){
   519        zFN = sqlite3_mprintf("%s/00", zDir);
   520        kvtest_mkdir(zFN);
   521        sqlite3_free(zFN);
   522        continue;
   523      }
   524      fatalError("unknown argument: \"%s\"\n", argv[i]);
   525    }
   526    ePathType = pathType(zDir);
   527    if( ePathType!=PATH_DIR && ePathType!=PATH_TREE ){
   528      fatalError("object \"%s\" is not a directory", zDir);
   529    }
   530    rc = sqlite3_open(zDb, &db);
   531    if( rc ){
   532      fatalError("cannot open database \"%s\": %s", zDb, sqlite3_errmsg(db));
   533    }
   534    rc = sqlite3_prepare_v2(db, "SELECT k, v FROM kv ORDER BY k", -1, &pStmt, 0);
   535    if( rc ){
   536      fatalError("prepare_v2 failed: %s\n", sqlite3_errmsg(db));
   537    }
   538    nFN = (int)strlen(zDir);
   539    zFN = sqlite3_mprintf("%s/00/00/00.extra---------------------", zDir);
   540    if( zFN==0 ){
   541      fatalError("malloc failed\n");
   542    }
   543    zTail = zFN + nFN + 1;
   544    while( sqlite3_step(pStmt)==SQLITE_ROW ){
   545      int iKey = sqlite3_column_int(pStmt, 0);
   546      sqlite3_int64 nData = sqlite3_column_bytes(pStmt, 1);
   547      const void *pData = sqlite3_column_blob(pStmt, 1);
   548      FILE *out;
   549      if( ePathType==PATH_DIR ){
   550        sqlite3_snprintf(20, zTail, "%06d", iKey);
   551      }else{
   552        sqlite3_snprintf(20, zTail, "%02d", iKey/10000);
   553        kvtest_mkdir(zFN);
   554        sqlite3_snprintf(20, zTail, "%02d/%02d", iKey/10000, (iKey/100)%100);
   555        kvtest_mkdir(zFN);
   556        sqlite3_snprintf(20, zTail, "%02d/%02d/%02d",
   557                         iKey/10000, (iKey/100)%100, iKey%100);
   558      }
   559      out = fopen(zFN, "wb");      
   560      nWrote = fwrite(pData, 1, nData, out);
   561      fclose(out);
   562      printf("\r%s   ", zTail); fflush(stdout);
   563      if( nWrote!=nData ){
   564        fatalError("Wrote only %d of %d bytes to %s\n",
   565                    (int)nWrote, nData, zFN);
   566      }
   567    }
   568    sqlite3_finalize(pStmt);
   569    sqlite3_close(db);
   570    sqlite3_free(zFN);
   571    printf("\n");
   572    return 0;
   573  }
   574  
   575  /*
   576  ** Read the content of file zName into memory obtained from sqlite3_malloc64()
   577  ** and return a pointer to the buffer. The caller is responsible for freeing 
   578  ** the memory. 
   579  **
   580  ** If parameter pnByte is not NULL, (*pnByte) is set to the number of bytes
   581  ** read.
   582  **
   583  ** For convenience, a nul-terminator byte is always appended to the data read
   584  ** from the file before the buffer is returned. This byte is not included in
   585  ** the final value of (*pnByte), if applicable.
   586  **
   587  ** NULL is returned if any error is encountered. The final value of *pnByte
   588  ** is undefined in this case.
   589  */
   590  static unsigned char *readFile(const char *zName, sqlite3_int64 *pnByte){
   591    FILE *in;               /* FILE from which to read content of zName */
   592    sqlite3_int64 nIn;      /* Size of zName in bytes */
   593    size_t nRead;           /* Number of bytes actually read */
   594    unsigned char *pBuf;    /* Content read from disk */
   595  
   596    nIn = fileSize(zName);
   597    if( nIn<0 ) return 0;
   598    in = fopen(zName, "rb");
   599    if( in==0 ) return 0;
   600    pBuf = sqlite3_malloc64( nIn );
   601    if( pBuf==0 ) return 0;
   602    nRead = fread(pBuf, (size_t)nIn, 1, in);
   603    fclose(in);
   604    if( nRead!=1 ){
   605      sqlite3_free(pBuf);
   606      return 0;
   607    }
   608    if( pnByte ) *pnByte = nIn;
   609    return pBuf;
   610  }
   611  
   612  /*
   613  ** Overwrite a file with randomness.  Do not change the size of the
   614  ** file.
   615  */
   616  static void updateFile(const char *zName, sqlite3_int64 *pnByte, int doFsync){
   617    FILE *out;              /* FILE from which to read content of zName */
   618    sqlite3_int64 sz;       /* Size of zName in bytes */
   619    size_t nWritten;        /* Number of bytes actually read */
   620    unsigned char *pBuf;    /* Content to store on disk */
   621    const char *zMode = "wb";   /* Mode for fopen() */
   622  
   623    sz = fileSize(zName);
   624    if( sz<0 ){
   625      fatalError("No such file: \"%s\"", zName);
   626    }
   627    *pnByte = sz;
   628    if( sz==0 ) return;
   629    pBuf = sqlite3_malloc64( sz );
   630    if( pBuf==0 ){
   631      fatalError("Cannot allocate %lld bytes\n", sz);
   632    }
   633    sqlite3_randomness((int)sz, pBuf); 
   634  #if defined(_WIN32)
   635    if( doFsync ) zMode = "wbc";
   636  #endif
   637    out = fopen(zName, zMode);
   638    if( out==0 ){
   639      fatalError("Cannot open \"%s\" for writing\n", zName);
   640    }
   641    nWritten = fwrite(pBuf, 1, (size_t)sz, out);
   642    if( doFsync ){
   643  #if defined(_WIN32)
   644      fflush(out);
   645  #else
   646      fsync(fileno(out));
   647  #endif
   648    }
   649    fclose(out);
   650    if( nWritten!=(size_t)sz ){
   651      fatalError("Wrote only %d of %d bytes to \"%s\"\n",
   652                 (int)nWritten, (int)sz, zName);
   653    }
   654    sqlite3_free(pBuf);
   655  }
   656  
   657  /*
   658  ** Return the current time in milliseconds since the beginning of
   659  ** the Julian epoch.
   660  */
   661  static sqlite3_int64 timeOfDay(void){
   662    static sqlite3_vfs *clockVfs = 0;
   663    sqlite3_int64 t;
   664    if( clockVfs==0 ) clockVfs = sqlite3_vfs_find(0);
   665    if( clockVfs->iVersion>=2 && clockVfs->xCurrentTimeInt64!=0 ){
   666      clockVfs->xCurrentTimeInt64(clockVfs, &t);
   667    }else{
   668      double r;
   669      clockVfs->xCurrentTime(clockVfs, &r);
   670      t = (sqlite3_int64)(r*86400000.0);
   671    }
   672    return t;
   673  }
   674  
   675  #ifdef __linux__
   676  /*
   677  ** Attempt to display I/O stats on Linux using /proc/PID/io
   678  */
   679  static void displayLinuxIoStats(FILE *out){
   680    FILE *in;
   681    char z[200];
   682    sqlite3_snprintf(sizeof(z), z, "/proc/%d/io", getpid());
   683    in = fopen(z, "rb");
   684    if( in==0 ) return;
   685    while( fgets(z, sizeof(z), in)!=0 ){
   686      static const struct {
   687        const char *zPattern;
   688        const char *zDesc;
   689      } aTrans[] = {
   690        { "rchar: ",                  "Bytes received by read():" },
   691        { "wchar: ",                  "Bytes sent to write():"    },
   692        { "syscr: ",                  "Read() system calls:"      },
   693        { "syscw: ",                  "Write() system calls:"     },
   694        { "read_bytes: ",             "Bytes read from storage:"  },
   695        { "write_bytes: ",            "Bytes written to storage:" },
   696        { "cancelled_write_bytes: ",  "Cancelled write bytes:"    },
   697      };
   698      int i;
   699      for(i=0; i<sizeof(aTrans)/sizeof(aTrans[0]); i++){
   700        int n = (int)strlen(aTrans[i].zPattern);
   701        if( strncmp(aTrans[i].zPattern, z, n)==0 ){
   702          fprintf(out, "%-36s %s", aTrans[i].zDesc, &z[n]);
   703          break;
   704        }
   705      }
   706    }
   707    fclose(in);
   708  }
   709  #endif
   710  
   711  /*
   712  ** Display memory stats.
   713  */
   714  static int display_stats(
   715    sqlite3 *db,                    /* Database to query */
   716    int bReset                      /* True to reset SQLite stats */
   717  ){
   718    int iCur;
   719    int iHiwtr;
   720    FILE *out = stdout;
   721  
   722    fprintf(out, "\n");
   723  
   724    iHiwtr = iCur = -1;
   725    sqlite3_status(SQLITE_STATUS_MEMORY_USED, &iCur, &iHiwtr, bReset);
   726    fprintf(out,
   727            "Memory Used:                         %d (max %d) bytes\n",
   728            iCur, iHiwtr);
   729    iHiwtr = iCur = -1;
   730    sqlite3_status(SQLITE_STATUS_MALLOC_COUNT, &iCur, &iHiwtr, bReset);
   731    fprintf(out, "Number of Outstanding Allocations:   %d (max %d)\n",
   732            iCur, iHiwtr);
   733    iHiwtr = iCur = -1;
   734    sqlite3_status(SQLITE_STATUS_PAGECACHE_USED, &iCur, &iHiwtr, bReset);
   735    fprintf(out,
   736        "Number of Pcache Pages Used:         %d (max %d) pages\n",
   737        iCur, iHiwtr);
   738    iHiwtr = iCur = -1;
   739    sqlite3_status(SQLITE_STATUS_PAGECACHE_OVERFLOW, &iCur, &iHiwtr, bReset);
   740    fprintf(out,
   741            "Number of Pcache Overflow Bytes:     %d (max %d) bytes\n",
   742            iCur, iHiwtr);
   743    iHiwtr = iCur = -1;
   744    sqlite3_status(SQLITE_STATUS_MALLOC_SIZE, &iCur, &iHiwtr, bReset);
   745    fprintf(out, "Largest Allocation:                  %d bytes\n",
   746            iHiwtr);
   747    iHiwtr = iCur = -1;
   748    sqlite3_status(SQLITE_STATUS_PAGECACHE_SIZE, &iCur, &iHiwtr, bReset);
   749    fprintf(out, "Largest Pcache Allocation:           %d bytes\n",
   750            iHiwtr);
   751  
   752    iHiwtr = iCur = -1;
   753    sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_USED, &iCur, &iHiwtr, bReset);
   754    fprintf(out, "Pager Heap Usage:                    %d bytes\n",
   755        iCur);
   756    iHiwtr = iCur = -1;
   757    sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_HIT, &iCur, &iHiwtr, 1);
   758    fprintf(out, "Page cache hits:                     %d\n", iCur);
   759    iHiwtr = iCur = -1;
   760    sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_MISS, &iCur, &iHiwtr, 1);
   761    fprintf(out, "Page cache misses:                   %d\n", iCur);
   762    iHiwtr = iCur = -1;
   763    sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_WRITE, &iCur, &iHiwtr, 1);
   764    fprintf(out, "Page cache writes:                   %d\n", iCur);
   765    iHiwtr = iCur = -1;
   766  
   767  #ifdef __linux__
   768    displayLinuxIoStats(out);
   769  #endif
   770  
   771    return 0;
   772  }
   773  
   774  /* Blob access order */
   775  #define ORDER_ASC     1
   776  #define ORDER_DESC    2
   777  #define ORDER_RANDOM  3
   778  
   779  
   780  /*
   781  ** Run a performance test
   782  */
   783  static int runMain(int argc, char **argv){
   784    int eType;                  /* Is zDb a database or a directory? */
   785    char *zDb;                  /* Database or directory name */
   786    int i;                      /* Loop counter */
   787    int rc;                     /* Return code from SQLite calls */
   788    int nCount = 1000;          /* Number of blob fetch operations */
   789    int nExtra = 0;             /* Extra cycles */
   790    int iKey = 1;               /* Next blob key */
   791    int iMax = 0;               /* Largest allowed key */
   792    int iPagesize = 0;          /* Database page size */
   793    int iCache = 1000;          /* Database cache size in kibibytes */
   794    int bBlobApi = 0;           /* Use the incremental blob I/O API */
   795    int bStats = 0;             /* Print stats before exiting */
   796    int eOrder = ORDER_ASC;     /* Access order */
   797    int isUpdateTest = 0;       /* Do in-place updates rather than reads */
   798    int doIntegrityCk = 0;      /* Run PRAGMA integrity_check after the test */
   799    int noSync = 0;             /* Disable synchronous mode */
   800    int doFsync = 0;            /* Update disk files synchronously */
   801    int doMultiTrans = 0;       /* Each operation in its own transaction */
   802    int noCheckpoint = 0;       /* Omit the checkpoint in WAL mode */
   803    sqlite3 *db = 0;            /* Database connection */
   804    sqlite3_stmt *pStmt = 0;    /* Prepared statement for SQL access */
   805    sqlite3_blob *pBlob = 0;    /* Handle for incremental Blob I/O */
   806    sqlite3_int64 tmStart;      /* Start time */
   807    sqlite3_int64 tmElapsed;    /* Elapsed time */
   808    int mmapSize = 0;           /* --mmap N argument */
   809    sqlite3_int64 nData = 0;    /* Bytes of data */
   810    sqlite3_int64 nTotal = 0;   /* Total data read */
   811    unsigned char *pData = 0;   /* Content of the blob */
   812    sqlite3_int64 nAlloc = 0;   /* Space allocated for pData[] */
   813    const char *zJMode = 0;     /* Journal mode */
   814    
   815  
   816    assert( strcmp(argv[1],"run")==0 );
   817    assert( argc>=3 );
   818    zDb = argv[2];
   819    eType = pathType(zDb);
   820    if( eType==PATH_OTHER ) fatalError("unknown object type: \"%s\"", zDb);
   821    if( eType==PATH_NEXIST ) fatalError("object does not exist: \"%s\"", zDb);
   822    for(i=3; i<argc; i++){
   823      char *z = argv[i];
   824      if( z[0]!='-' ) fatalError("unknown argument: \"%s\"", z);
   825      if( z[1]=='-' ) z++;
   826      if( strcmp(z, "-asc")==0 ){
   827        eOrder = ORDER_ASC;
   828        continue;
   829      }
   830      if( strcmp(z, "-blob-api")==0 ){
   831        bBlobApi = 1;
   832        continue;
   833      }
   834      if( strcmp(z, "-cache-size")==0 ){
   835        if( i==argc-1 ) fatalError("missing argument on \"%s\"", argv[i]);
   836        iCache = integerValue(argv[++i]);
   837        continue;
   838      }
   839      if( strcmp(z, "-count")==0 ){
   840        if( i==argc-1 ) fatalError("missing argument on \"%s\"", argv[i]);
   841        nCount = integerValue(argv[++i]);
   842        if( nCount<1 ) fatalError("the --count must be positive");
   843        continue;
   844      }
   845      if( strcmp(z, "-desc")==0 ){
   846        eOrder = ORDER_DESC;
   847        continue;
   848      }
   849      if( strcmp(z, "-fsync")==0 ){
   850        doFsync = 1;
   851        continue;
   852      }
   853      if( strcmp(z, "-integrity-check")==0 ){
   854        doIntegrityCk = 1;
   855        continue;
   856      }
   857      if( strcmp(z, "-jmode")==0 ){
   858        if( i==argc-1 ) fatalError("missing argument on \"%s\"", argv[i]);
   859        zJMode = argv[++i];
   860        continue;
   861      }
   862      if( strcmp(z, "-mmap")==0 ){
   863        if( i==argc-1 ) fatalError("missing argument on \"%s\"", argv[i]);
   864        mmapSize = integerValue(argv[++i]);
   865        if( nCount<0 ) fatalError("the --mmap must be non-negative");
   866        continue;
   867      }
   868      if( strcmp(z, "-max-id")==0 ){
   869        if( i==argc-1 ) fatalError("missing argument on \"%s\"", argv[i]);
   870        iMax = integerValue(argv[++i]);
   871        continue;
   872      }
   873      if( strcmp(z, "-multitrans")==0 ){
   874        doMultiTrans = 1;
   875        continue;
   876      }
   877      if( strcmp(z, "-nocheckpoint")==0 ){
   878        noCheckpoint = 1;
   879        continue;
   880      }
   881      if( strcmp(z, "-nosync")==0 ){
   882        noSync = 1;
   883        continue;
   884      }
   885      if( strcmp(z, "-random")==0 ){
   886        eOrder = ORDER_RANDOM;
   887        continue;
   888      }
   889      if( strcmp(z, "-start")==0 ){
   890        if( i==argc-1 ) fatalError("missing argument on \"%s\"", argv[i]);
   891        iKey = integerValue(argv[++i]);
   892        if( iKey<1 ) fatalError("the --start must be positive");
   893        continue;
   894      }
   895      if( strcmp(z, "-stats")==0 ){
   896        bStats = 1;
   897        continue;
   898      }
   899      if( strcmp(z, "-update")==0 ){
   900        isUpdateTest = 1;
   901        continue;
   902      }
   903      fatalError("unknown option: \"%s\"", argv[i]);
   904    }
   905    if( eType==PATH_DB ){
   906      /* Recover any prior crashes prior to starting the timer */
   907      sqlite3_open(zDb, &db);
   908      sqlite3_exec(db, "SELECT rowid FROM sqlite_master LIMIT 1", 0, 0, 0);
   909      sqlite3_close(db);
   910      db = 0;
   911    }
   912    tmStart = timeOfDay();
   913    if( eType==PATH_DB ){
   914      char *zSql;
   915      rc = sqlite3_open(zDb, &db);
   916      if( rc ){
   917        fatalError("cannot open database \"%s\": %s", zDb, sqlite3_errmsg(db));
   918      }
   919      zSql = sqlite3_mprintf("PRAGMA mmap_size=%d", mmapSize);
   920      sqlite3_exec(db, zSql, 0, 0, 0);
   921      sqlite3_free(zSql);
   922      zSql = sqlite3_mprintf("PRAGMA cache_size=%d", iCache);
   923      sqlite3_exec(db, zSql, 0, 0, 0);
   924      sqlite3_free(zSql);
   925      if( noSync ){
   926        sqlite3_exec(db, "PRAGMA synchronous=OFF", 0, 0, 0);
   927      }
   928      pStmt = 0;
   929      sqlite3_prepare_v2(db, "PRAGMA page_size", -1, &pStmt, 0);
   930      if( sqlite3_step(pStmt)==SQLITE_ROW ){
   931        iPagesize = sqlite3_column_int(pStmt, 0);
   932      }
   933      sqlite3_finalize(pStmt);
   934      sqlite3_prepare_v2(db, "PRAGMA cache_size", -1, &pStmt, 0);
   935      if( sqlite3_step(pStmt)==SQLITE_ROW ){
   936        iCache = sqlite3_column_int(pStmt, 0);
   937      }else{
   938        iCache = 0;
   939      }
   940      sqlite3_finalize(pStmt);
   941      pStmt = 0;
   942      if( zJMode ){
   943        zSql = sqlite3_mprintf("PRAGMA journal_mode=%Q", zJMode);
   944        sqlite3_exec(db, zSql, 0, 0, 0);
   945        sqlite3_free(zSql);
   946        if( noCheckpoint ){
   947          sqlite3_exec(db, "PRAGMA wal_autocheckpoint=0", 0, 0, 0);
   948        }
   949      }
   950      sqlite3_prepare_v2(db, "PRAGMA journal_mode", -1, &pStmt, 0);
   951      if( sqlite3_step(pStmt)==SQLITE_ROW ){
   952        zJMode = sqlite3_mprintf("%s", sqlite3_column_text(pStmt, 0));
   953      }else{
   954        zJMode = "???";
   955      }
   956      sqlite3_finalize(pStmt);
   957      if( iMax<=0 ){
   958        sqlite3_prepare_v2(db, "SELECT max(k) FROM kv", -1, &pStmt, 0);
   959        if( sqlite3_step(pStmt)==SQLITE_ROW ){
   960          iMax = sqlite3_column_int(pStmt, 0);
   961        }
   962        sqlite3_finalize(pStmt);
   963      }
   964      pStmt = 0;
   965      if( !doMultiTrans ) sqlite3_exec(db, "BEGIN", 0, 0, 0);
   966    }
   967    if( iMax<=0 ) iMax = 1000;
   968    for(i=0; i<nCount; i++){
   969      if( eType==PATH_DIR || eType==PATH_TREE ){
   970        /* CASE 1: Reading or writing blobs out of separate files */
   971        char *zKey;
   972        if( eType==PATH_DIR ){
   973          zKey = sqlite3_mprintf("%s/%06d", zDb, iKey);
   974        }else{
   975          zKey = sqlite3_mprintf("%s/%02d/%02d/%02d", zDb, iKey/10000,
   976                                 (iKey/100)%100, iKey%100);
   977        }
   978        nData = 0;
   979        if( isUpdateTest ){
   980          updateFile(zKey, &nData, doFsync);
   981        }else{
   982          pData = readFile(zKey, &nData);
   983          sqlite3_free(pData);
   984        }
   985        sqlite3_free(zKey);
   986      }else if( bBlobApi ){
   987        /* CASE 2: Reading from database using the incremental BLOB I/O API */
   988        if( pBlob==0 ){
   989          rc = sqlite3_blob_open(db, "main", "kv", "v", iKey,
   990                                 isUpdateTest, &pBlob);
   991          if( rc ){
   992            fatalError("could not open sqlite3_blob handle: %s",
   993                       sqlite3_errmsg(db));
   994          }
   995        }else{
   996          rc = sqlite3_blob_reopen(pBlob, iKey);
   997        }
   998        if( rc==SQLITE_OK ){
   999          nData = sqlite3_blob_bytes(pBlob);
  1000          if( nAlloc<nData+1 ){
  1001            nAlloc = nData+100;
  1002            pData = sqlite3_realloc64(pData, nAlloc);
  1003          }
  1004          if( pData==0 ) fatalError("cannot allocate %d bytes", nData+1);
  1005          if( isUpdateTest ){
  1006            sqlite3_randomness((int)nData, pData);
  1007            rc = sqlite3_blob_write(pBlob, pData, (int)nData, 0);
  1008            if( rc!=SQLITE_OK ){
  1009              fatalError("could not write the blob at %d: %s", iKey,
  1010                        sqlite3_errmsg(db));
  1011            }
  1012          }else{
  1013            rc = sqlite3_blob_read(pBlob, pData, (int)nData, 0);
  1014            if( rc!=SQLITE_OK ){
  1015              fatalError("could not read the blob at %d: %s", iKey,
  1016                        sqlite3_errmsg(db));
  1017            }
  1018          }
  1019        }
  1020      }else{
  1021        /* CASE 3: Reading from database using SQL */
  1022        if( pStmt==0 ){
  1023          if( isUpdateTest ){
  1024            sqlite3_create_function(db, "remember", 2, SQLITE_UTF8, 0,
  1025                                    rememberFunc, 0, 0);
  1026  
  1027            rc = sqlite3_prepare_v2(db, 
  1028              "UPDATE kv SET v=randomblob(remember(length(v),?2))"
  1029              " WHERE k=?1", -1, &pStmt, 0);
  1030            sqlite3_bind_int64(pStmt, 2, SQLITE_PTR_TO_INT(&nData));
  1031          }else{
  1032            rc = sqlite3_prepare_v2(db, 
  1033                   "SELECT v FROM kv WHERE k=?1", -1, &pStmt, 0);
  1034          }
  1035          if( rc ){
  1036            fatalError("cannot prepare query: %s", sqlite3_errmsg(db));
  1037          }
  1038        }else{
  1039          sqlite3_reset(pStmt);
  1040        }
  1041        sqlite3_bind_int(pStmt, 1, iKey);
  1042        nData = 0;
  1043        rc = sqlite3_step(pStmt);
  1044        if( rc==SQLITE_ROW ){
  1045          nData = sqlite3_column_bytes(pStmt, 0);
  1046          pData = (unsigned char*)sqlite3_column_blob(pStmt, 0);
  1047        }
  1048      }
  1049      if( eOrder==ORDER_ASC ){
  1050        iKey++;
  1051        if( iKey>iMax ) iKey = 1;
  1052      }else if( eOrder==ORDER_DESC ){
  1053        iKey--;
  1054        if( iKey<=0 ) iKey = iMax;
  1055      }else{
  1056        iKey = (randInt()%iMax)+1;
  1057      }
  1058      nTotal += nData;
  1059      if( nData==0 ){ nCount++; nExtra++; }
  1060    }
  1061    if( nAlloc ) sqlite3_free(pData);
  1062    if( pStmt ) sqlite3_finalize(pStmt);
  1063    if( pBlob ) sqlite3_blob_close(pBlob);
  1064    if( bStats ){
  1065      display_stats(db, 0);
  1066    }
  1067    if( db ){
  1068      if( !doMultiTrans ) sqlite3_exec(db, "COMMIT", 0, 0, 0);
  1069      if( !noCheckpoint ){
  1070        sqlite3_close(db);
  1071        db = 0;
  1072      }
  1073    }
  1074    tmElapsed = timeOfDay() - tmStart;
  1075    if( db && noCheckpoint ){
  1076      sqlite3_close(db);
  1077      db = 0;
  1078    }
  1079    if( nExtra ){
  1080      printf("%d cycles due to %d misses\n", nCount, nExtra);
  1081    }
  1082    if( eType==PATH_DB ){
  1083      printf("SQLite version: %s\n", sqlite3_libversion());
  1084      if( doIntegrityCk ){
  1085        sqlite3_open(zDb, &db);
  1086        sqlite3_prepare_v2(db, "PRAGMA integrity_check", -1, &pStmt, 0);
  1087        while( sqlite3_step(pStmt)==SQLITE_ROW ){
  1088          printf("integrity-check: %s\n", sqlite3_column_text(pStmt, 0));
  1089        }
  1090        sqlite3_finalize(pStmt);
  1091        sqlite3_close(db);
  1092        db = 0;
  1093      }
  1094    }
  1095    printf("--count %d --max-id %d", nCount-nExtra, iMax);
  1096    switch( eOrder ){
  1097      case ORDER_RANDOM:  printf(" --random\n");  break;
  1098      case ORDER_DESC:    printf(" --desc\n");    break;
  1099      default:            printf(" --asc\n");     break;
  1100    }
  1101    if( eType==PATH_DB ){
  1102      printf("--cache-size %d --jmode %s\n", iCache, zJMode);
  1103      printf("--mmap %d%s\n", mmapSize, bBlobApi ? " --blob-api" : "");
  1104      if( noSync ) printf("--nosync\n");
  1105    }
  1106    if( iPagesize ) printf("Database page size: %d\n", iPagesize);
  1107    printf("Total elapsed time: %.3f\n", tmElapsed/1000.0);
  1108    if( isUpdateTest ){
  1109      printf("Microseconds per BLOB write: %.3f\n", tmElapsed*1000.0/nCount);
  1110      printf("Content write rate: %.1f MB/s\n", nTotal/(1000.0*tmElapsed));
  1111    }else{
  1112      printf("Microseconds per BLOB read: %.3f\n", tmElapsed*1000.0/nCount);
  1113      printf("Content read rate: %.1f MB/s\n", nTotal/(1000.0*tmElapsed));
  1114    }
  1115    return 0;
  1116  }
  1117  
  1118  
  1119  int main(int argc, char **argv){
  1120    if( argc<3 ) showHelp();
  1121    if( strcmp(argv[1],"init")==0 ){
  1122      return initMain(argc, argv);
  1123    }
  1124    if( strcmp(argv[1],"export")==0 ){
  1125      return exportMain(argc, argv);
  1126    }
  1127    if( strcmp(argv[1],"run")==0 ){
  1128      return runMain(argc, argv);
  1129    }
  1130    if( strcmp(argv[1],"stat")==0 ){
  1131      return statMain(argc, argv);
  1132    }
  1133    showHelp();
  1134    return 0;
  1135  }