Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Update comments in the R-Tree module in preparation for some big changes. Add an "rtree" performance test to speedtest1. |
---|---|
Downloads: | Tarball | ZIP archive |
Timelines: | family | ancestors | descendants | both | rtree-enhancements |
Files: | files | file ages | folders |
SHA1: |
20a73ec0b2f56609a4504052e198e328 |
User & Date: | drh 2014-04-11 16:14:54.092 |
Context
2014-04-11
| ||
17:41 | Add the --verify option to speedtest1. Add verification test cases to the "rtree" testset and a case that uses a custom geometry callback. (check-in: 9d485c4207 user: drh tags: rtree-enhancements) | |
16:14 | Update comments in the R-Tree module in preparation for some big changes. Add an "rtree" performance test to speedtest1. (check-in: 20a73ec0b2 user: drh tags: rtree-enhancements) | |
2014-04-03
| ||
16:35 | Merge all recent changes from trunk, including the fix for the OP_SCopy-vs-OP_Copy problem. (check-in: 9515c8344a user: drh tags: sessions) | |
Changes
Changes to ext/rtree/rtree.c.
︙ | ︙ | |||
106 107 108 109 110 111 112 113 114 115 116 117 118 119 | #include <string.h> #include <assert.h> #ifndef SQLITE_AMALGAMATION #include "sqlite3rtree.h" typedef sqlite3_int64 i64; typedef unsigned char u8; typedef unsigned int u32; #endif /* The following macro is used to suppress compiler warnings. */ #ifndef UNUSED_PARAMETER # define UNUSED_PARAMETER(x) (void)(x) | > | 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 | #include <string.h> #include <assert.h> #ifndef SQLITE_AMALGAMATION #include "sqlite3rtree.h" typedef sqlite3_int64 i64; typedef unsigned char u8; typedef unsigned short u16; typedef unsigned int u32; #endif /* The following macro is used to suppress compiler warnings. */ #ifndef UNUSED_PARAMETER # define UNUSED_PARAMETER(x) (void)(x) |
︙ | ︙ | |||
131 132 133 134 135 136 137 | /* The rtree may have between 1 and RTREE_MAX_DIMENSIONS dimensions. */ #define RTREE_MAX_DIMENSIONS 5 /* Size of hash table Rtree.aHash. This hash table is not expected to ** ever contain very many entries, so a fixed number of buckets is ** used. */ | | | | > | < | 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 | /* The rtree may have between 1 and RTREE_MAX_DIMENSIONS dimensions. */ #define RTREE_MAX_DIMENSIONS 5 /* Size of hash table Rtree.aHash. This hash table is not expected to ** ever contain very many entries, so a fixed number of buckets is ** used. */ #define HASHSIZE 97 /* The xBestIndex method of this virtual table requires an estimate of ** the number of rows in the virtual table to calculate the costs of ** various strategies. If possible, this estimate is loaded from the ** sqlite_stat1 table (with RTREE_MIN_ROWEST as a hard-coded minimum). ** Otherwise, if no sqlite_stat1 entry is available, use ** RTREE_DEFAULT_ROWEST. */ #define RTREE_DEFAULT_ROWEST 1048576 #define RTREE_MIN_ROWEST 100 /* ** An rtree virtual-table object. */ struct Rtree { sqlite3_vtab base; /* Base class. Must be first */ sqlite3 *db; /* Host database connection */ int iNodeSize; /* Size in bytes of each node in the node table */ u8 nDim; /* Number of dimensions */ u8 eCoordType; /* RTREE_COORD_REAL32 or RTREE_COORD_INT32 */ u8 nBytesPerCell; /* Bytes consumed per cell */ int iDepth; /* Current depth of the r-tree structure */ char *zDb; /* Name of database containing r-tree table */ char *zName; /* Name of r-tree table */ int nBusy; /* Current number of users of this structure */ i64 nRowEst; /* Estimated number of rows in this table */ /* List of nodes removed during a CondenseTree operation. List is ** linked together via the pointer normally used for hash chains - ** RtreeNode.pNext. RtreeNode.iNode stores the depth of the sub-tree ** headed by the node (leaf nodes have RtreeNode.iNode==0). |
︙ | ︙ | |||
182 183 184 185 186 187 188 | sqlite3_stmt *pDeleteRowid; /* Statements to read/write/delete a record from xxx_parent */ sqlite3_stmt *pReadParent; sqlite3_stmt *pWriteParent; sqlite3_stmt *pDeleteParent; | | | | 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 | sqlite3_stmt *pDeleteRowid; /* Statements to read/write/delete a record from xxx_parent */ sqlite3_stmt *pReadParent; sqlite3_stmt *pWriteParent; sqlite3_stmt *pDeleteParent; RtreeNode *aHash[HASHSIZE]; /* Hash table of in-memory nodes. */ }; /* Possible values for Rtree.eCoordType: */ #define RTREE_COORD_REAL32 0 #define RTREE_COORD_INT32 1 /* ** If SQLITE_RTREE_INT_ONLY is defined, then this virtual table will ** only deal with integer coordinates. No floating point operations ** will be done. |
︙ | ︙ | |||
228 229 230 231 232 233 234 | */ #define RTREE_MAX_DEPTH 40 /* ** An rtree cursor object. */ struct RtreeCursor { | | > > > > | | | 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 | */ #define RTREE_MAX_DEPTH 40 /* ** An rtree cursor object. */ struct RtreeCursor { sqlite3_vtab_cursor base; /* Base class. Must be first */ RtreeNode *pNode; /* Node cursor is currently pointing at */ int iCell; /* Index of current cell in pNode */ int iStrategy; /* Copy of idxNum search parameter */ int nConstraint; /* Number of entries in aConstraint */ RtreeConstraint *aConstraint; /* Search constraints. */ }; /* ** A coordinate can be either a floating point number or a integer. All ** coordinates within a single R-Tree are always of the same time. */ union RtreeCoord { RtreeValue f; /* Floating point value */ int i; /* Integer value */ }; /* ** The argument is an RtreeCoord. Return the value stored within the RtreeCoord ** formatted as a RtreeDValue (double or int64). This macro assumes that local ** variable pRtree points to the Rtree structure associated with the ** RtreeCoord. |
︙ | ︙ | |||
280 281 282 283 284 285 286 | #define RTREE_GT 0x45 #define RTREE_MATCH 0x46 /* ** An rtree structure node. */ struct RtreeNode { | | | | | | | > > | | | | 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 | #define RTREE_GT 0x45 #define RTREE_MATCH 0x46 /* ** An rtree structure node. */ struct RtreeNode { RtreeNode *pParent; /* Parent node */ i64 iNode; /* The node number */ int nRef; /* Number of references to this node */ int isDirty; /* True if the node needs to be written to disk */ u8 *zData; /* Content of the node, as should be on disk */ RtreeNode *pNext; /* Next node in this hash collision chain */ }; /* Return the number of cells in a node */ #define NCELL(pNode) readInt16(&(pNode)->zData[2]) /* ** A single cell from a node, deserialized */ struct RtreeCell { i64 iRowid; /* Node or entry ID */ RtreeCoord aCoord[RTREE_MAX_DIMENSIONS*2]; /* Bounding box coordinates */ }; /* ** Value for the first field of every RtreeMatchArg object. The MATCH ** operator tests that the first field of a blob operand matches this ** value to avoid operating on invalid blobs (which could cause a segfault). |
︙ | ︙ | |||
422 423 424 425 426 427 428 | } /* ** Given a node number iNode, return the corresponding key to use ** in the Rtree.aHash table. */ static int nodeHash(i64 iNode){ | < < < | | 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 | } /* ** Given a node number iNode, return the corresponding key to use ** in the Rtree.aHash table. */ static int nodeHash(i64 iNode){ return iNode % HASHSIZE; } /* ** Search the node hash table for node iNode. If found, return a pointer ** to it. Otherwise, return 0. */ static RtreeNode *nodeHashLookup(Rtree *pRtree, i64 iNode){ |
︙ | ︙ | |||
485 486 487 488 489 490 491 | } return pNode; } /* ** Obtain a reference to an r-tree node. */ | | < | 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 | } return pNode; } /* ** Obtain a reference to an r-tree node. */ static int nodeAcquire( Rtree *pRtree, /* R-tree structure */ i64 iNode, /* Node number to load */ RtreeNode *pParent, /* Either the parent node or NULL */ RtreeNode **ppNode /* OUT: Acquired node */ ){ int rc; int rc2 = SQLITE_OK; |
︙ | ︙ | |||
575 576 577 578 579 580 581 | return rc; } /* ** Overwrite cell iCell of node pNode with the contents of pCell. */ static void nodeOverwriteCell( | | | | | | | < | | | < | | 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 | return rc; } /* ** Overwrite cell iCell of node pNode with the contents of pCell. */ static void nodeOverwriteCell( Rtree *pRtree, /* The overall R-Tree */ RtreeNode *pNode, /* The node into which the cell is to be written */ RtreeCell *pCell, /* The cell to write */ int iCell /* Index into pNode into which pCell is written */ ){ int ii; u8 *p = &pNode->zData[4 + pRtree->nBytesPerCell*iCell]; p += writeInt64(p, pCell->iRowid); for(ii=0; ii<(pRtree->nDim*2); ii++){ p += writeCoord(p, &pCell->aCoord[ii]); } pNode->isDirty = 1; } /* ** Remove the cell with index iCell from node pNode. */ static void nodeDeleteCell(Rtree *pRtree, RtreeNode *pNode, int iCell){ u8 *pDst = &pNode->zData[4 + pRtree->nBytesPerCell*iCell]; u8 *pSrc = &pDst[pRtree->nBytesPerCell]; int nByte = (NCELL(pNode) - iCell - 1) * pRtree->nBytesPerCell; memmove(pDst, pSrc, nByte); writeInt16(&pNode->zData[2], NCELL(pNode)-1); pNode->isDirty = 1; } /* ** Insert the contents of cell pCell into node pNode. If the insert ** is successful, return SQLITE_OK. ** ** If there is not enough free space in pNode, return SQLITE_FULL. */ static int nodeInsertCell( Rtree *pRtree, /* The overall R-Tree */ RtreeNode *pNode, /* Write new cell into this node */ RtreeCell *pCell /* The cell to be inserted */ ){ int nCell; /* Current number of cells in pNode */ int nMaxCell; /* Maximum number of cells for pNode */ nMaxCell = (pRtree->iNodeSize-4)/pRtree->nBytesPerCell; nCell = NCELL(pNode); assert( nCell<=nMaxCell ); if( nCell<nMaxCell ){ nodeOverwriteCell(pRtree, pNode, pCell, nCell); writeInt16(&pNode->zData[2], nCell+1); pNode->isDirty = 1; } return (nCell==nMaxCell); } /* ** If the node is dirty, write it out to the database. */ static int nodeWrite(Rtree *pRtree, RtreeNode *pNode){ int rc = SQLITE_OK; if( pNode->isDirty ){ sqlite3_stmt *p = pRtree->pWriteNode; if( pNode->iNode ){ sqlite3_bind_int64(p, 1, pNode->iNode); }else{ sqlite3_bind_null(p, 1); |
︙ | ︙ | |||
658 659 660 661 662 663 664 | return rc; } /* ** Release a reference to a node. If the node is dirty and the reference ** count drops to zero, the node data is written to the database. */ | < | | 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 | return rc; } /* ** Release a reference to a node. If the node is dirty and the reference ** count drops to zero, the node data is written to the database. */ static int nodeRelease(Rtree *pRtree, RtreeNode *pNode){ int rc = SQLITE_OK; if( pNode ){ assert( pNode->nRef>0 ); pNode->nRef--; if( pNode->nRef==0 ){ if( pNode->iNode==1 ){ pRtree->iDepth = -1; |
︙ | ︙ | |||
687 688 689 690 691 692 693 | /* ** Return the 64-bit integer value associated with cell iCell of ** node pNode. If pNode is a leaf node, this is a rowid. If it is ** an internal node, then the 64-bit integer is a child page number. */ static i64 nodeGetRowid( | | | | | | | | | | | | | | 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 | /* ** Return the 64-bit integer value associated with cell iCell of ** node pNode. If pNode is a leaf node, this is a rowid. If it is ** an internal node, then the 64-bit integer is a child page number. */ static i64 nodeGetRowid( Rtree *pRtree, /* The overall R-Tree */ RtreeNode *pNode, /* The node from which to extract the ID */ int iCell /* The cell index from which to extract the ID */ ){ assert( iCell<NCELL(pNode) ); return readInt64(&pNode->zData[4 + pRtree->nBytesPerCell*iCell]); } /* ** Return coordinate iCoord from cell iCell in node pNode. */ static void nodeGetCoord( Rtree *pRtree, /* The overall R-Tree */ RtreeNode *pNode, /* The node from which to extract a coordinate */ int iCell, /* The index of the cell within the node */ int iCoord, /* Which coordinate to extract */ RtreeCoord *pCoord /* OUT: Space to write result to */ ){ readCoord(&pNode->zData[12 + pRtree->nBytesPerCell*iCell + 4*iCoord], pCoord); } /* ** Deserialize cell iCell of node pNode. Populate the structure pointed ** to by pCell with the results. */ static void nodeGetCell( Rtree *pRtree, /* The overall R-Tree */ RtreeNode *pNode, /* The node containing the cell to be read */ int iCell, /* Index of the cell within the node */ RtreeCell *pCell /* OUT: Write the cell contents here */ ){ int ii; pCell->iRowid = nodeGetRowid(pRtree, pNode, iCell); for(ii=0; ii<pRtree->nDim*2; ii++){ nodeGetCoord(pRtree, pNode, iCell, ii, &pCell->aCoord[ii]); } } |
︙ | ︙ |
Changes to test/speedtest1.c.
︙ | ︙ | |||
926 927 928 929 930 931 932 933 934 935 936 937 938 939 | ");", nElem, nElem ); speedtest1_run(); speedtest1_end_test(); } /* ** A testset used for debugging speedtest1 itself. */ void testset_debug1(void){ unsigned i, n; unsigned x1, x2; | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 | ");", nElem, nElem ); speedtest1_run(); speedtest1_end_test(); } /* Generate two numbers between 1 and mx. The first number is less than ** the second. Usually the numbers are near each other but can sometimes ** be far apart. */ static void twoCoords( unsigned mx, /* Range of 1..mx */ unsigned *pX0, unsigned *pX1 /* OUT: write results here */ ){ unsigned d, x0, x1, span; span = mx/100 + 1; if( speedtest1_random()%3==0 ) span *= 3; if( speedtest1_random()%17==0 ) span = mx - 5; d = speedtest1_random()%span + 1; x0 = speedtest1_random()%(mx-d) + 1; x1 = x0 + d; *pX0 = x0; *pX1 = x1; } /* ** A testset for the R-Tree virtual table */ void testset_rtree(void){ unsigned i, n; unsigned mxCoord; unsigned x0, x1, y0, y1, z0, z1; mxCoord = g.szTest*600; n = g.szTest*100; speedtest1_begin_test(100, "%d INSERTs into an r-tree", n); speedtest1_exec("BEGIN"); speedtest1_exec("CREATE VIRTUAL TABLE rt1 USING rtree(id,x0,x1,y0,y1,z0,z1)"); speedtest1_prepare("INSERT INTO rt1(id,x0,x1,y0,y1,z0,z1)" "VALUES(?1,?2,?3,?4,?5,?6,?7)"); for(i=1; i<=n; i++){ twoCoords(mxCoord, &x0, &x1); twoCoords(mxCoord, &y0, &y1); twoCoords(mxCoord, &z0, &z1); sqlite3_bind_int(g.pStmt, 1, i); sqlite3_bind_int(g.pStmt, 2, x0); sqlite3_bind_int(g.pStmt, 3, x1); sqlite3_bind_int(g.pStmt, 4, y0); sqlite3_bind_int(g.pStmt, 5, y1); sqlite3_bind_int(g.pStmt, 6, z0); sqlite3_bind_int(g.pStmt, 7, z1); speedtest1_run(); } speedtest1_exec("COMMIT"); speedtest1_end_test(); n = g.szTest*20; speedtest1_begin_test(110, "%d one-dimensional intersect slice queries", n); speedtest1_prepare("SELECT count(*) FROM rt1 WHERE x0>=?1 AND x1<=?2"); for(i=0; i<mxCoord; i+=(mxCoord/n)){ sqlite3_bind_int(g.pStmt, 1, i); sqlite3_bind_int(g.pStmt, 2, i+mxCoord/n); speedtest1_run(); } speedtest1_end_test(); n = g.szTest*20; speedtest1_begin_test(120, "%d one-dimensional overlap slice queries", n); speedtest1_prepare("SELECT count(*) FROM rt1 WHERE y1>=?1 AND y0<=?2"); for(i=0; i<mxCoord; i+=(mxCoord/n)){ sqlite3_bind_int(g.pStmt, 1, i); sqlite3_bind_int(g.pStmt, 2, i+mxCoord/n); speedtest1_run(); } speedtest1_end_test(); n = g.szTest*80; speedtest1_begin_test(130, "%d three-dimensional intersect box queries", n); speedtest1_prepare("SELECT count(*) FROM rt1 WHERE x1>=?1 AND x0<=?2" " AND y1>=?1 AND y0<=?2 AND z1>=?1 AND z0<=?2"); for(i=0; i<mxCoord; i+=(mxCoord/n)){ sqlite3_bind_int(g.pStmt, 1, i); sqlite3_bind_int(g.pStmt, 2, i+mxCoord/n); speedtest1_run(); } speedtest1_end_test(); n = g.szTest*100; speedtest1_begin_test(140, "%d rowid queries", n); speedtest1_prepare("SELECT * FROM rt1 WHERE id=?1"); for(i=1; i<=n; i++){ sqlite3_bind_int(g.pStmt, 1, i); speedtest1_run(); } speedtest1_end_test(); } /* ** A testset used for debugging speedtest1 itself. */ void testset_debug1(void){ unsigned i, n; unsigned x1, x2; |
︙ | ︙ | |||
1137 1138 1139 1140 1141 1142 1143 1144 | if( g.bExplain ) printf(".explain\n.echo on\n"); if( strcmp(zTSet,"main")==0 ){ testset_main(); }else if( strcmp(zTSet,"debug1")==0 ){ testset_debug1(); }else if( strcmp(zTSet,"cte")==0 ){ testset_cte(); }else{ | > > | > | 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 | if( g.bExplain ) printf(".explain\n.echo on\n"); if( strcmp(zTSet,"main")==0 ){ testset_main(); }else if( strcmp(zTSet,"debug1")==0 ){ testset_debug1(); }else if( strcmp(zTSet,"cte")==0 ){ testset_cte(); }else if( strcmp(zTSet,"rtree")==0 ){ testset_rtree(); }else{ fatal_error("unknown testset: \"%s\"\nChoices: main debug1 cte rtree\n", zTSet); } speedtest1_final(); /* Database connection statistics printed after both prepared statements ** have been finalized */ #if SQLITE_VERSION_NUMBER>=3007009 if( showStats ){ |
︙ | ︙ |