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 }