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