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