From: drh <> Date: Fri, 7 Jun 2024 19:48:05 +0000 (+0000) Subject: At attempt at improving the performance of unixRead(). Partly successfuly, X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=refs%2Fheads%2Ffast-read;p=thirdparty%2Fsqlite.git At attempt at improving the performance of unixRead(). Partly successfuly, but the improvement is not that much and there is question whether or not the improvement is worth the extra complication and code space. FossilOrigin-Name: aebc790a16a191f7e7002e91b75da0dd16673c6d0831c3ba3e67af88dd7a97d2 --- diff --git a/manifest b/manifest index f28f11b60c..3e283db9bb 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Allow\sthe\squery\splanner\saccess\sto\sthe\sargument\sof\sLIMIT\seven\sif\sthat\nargument\sis\sa\sbound\sparameter. -D 2024-06-06T23:56:36.923 +C At\sattempt\sat\simproving\sthe\sperformance\sof\sunixRead().\s\sPartly\ssuccessfuly,\nbut\sthe\simprovement\sis\snot\sthat\smuch\sand\sthere\sis\squestion\swhether\sor\snot\nthe\simprovement\sis\sworth\sthe\sextra\scomplication\sand\scode\sspace. +D 2024-06-07T19:48:05.236 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -739,7 +739,7 @@ F src/os.h 1ff5ae51d339d0e30d8a9d814f4b8f8e448169304d83a7ed9db66a65732f3e63 F src/os_common.h 6c0eb8dd40ef3e12fe585a13e709710267a258e2c8dd1c40b1948a1d14582e06 F src/os_kv.c 4d39e1f1c180b11162c6dc4aa8ad34053873a639bac6baae23272fc03349986a F src/os_setup.h 6011ad7af5db4e05155f385eb3a9b4470688de6f65d6166b8956e58a3d872107 -F src/os_unix.c 08ca53844f4bf8eafb18b0a9076c84afac41da912315a5cfbe9e704d4c10c090 +F src/os_unix.c c4ad2db8265be042eda2ba14ff4f1497b40be0f0f20829cddabf2062bcd843f8 F src/os_win.c 6ff43bac175bd9ed79e7c0f96840b139f2f51d01689a638fd05128becf94908a F src/os_win.h 7b073010f1451abe501be30d12f6bc599824944a F src/pager.c 9beb80f6e330dd63c5d8ba0f7a7f3a55fff22067a68d424949c389bfc6fa0c56 @@ -2195,8 +2195,11 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P f66608bd356efe492d1003663c2e1ccd7cfbf2d40393d256f8720149904ad2d5 e94dfe9928750dd98145d4d9920b298f7b0868703b487f86e0db77a41d53ccf9 -R f3d6aba1cc68a2224dd6f7df935a2c23 +P c4a9dda2809c6e0e3d928e11e5553ead82cd9df551bcd35b11a7d869ef80ab8e +R 1e97a9b24bcfd905372f808facc9498c +T *branch * fast-read +T *sym-fast-read * +T -sym-trunk * U drh -Z b92cbf17475d91065d48b9fdd285839c +Z fcb5924181490f13936092bbea843f28 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index a310670ce9..a6ef769dc5 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -c4a9dda2809c6e0e3d928e11e5553ead82cd9df551bcd35b11a7d869ef80ab8e \ No newline at end of file +aebc790a16a191f7e7002e91b75da0dd16673c6d0831c3ba3e67af88dd7a97d2 \ No newline at end of file diff --git a/src/os_unix.c b/src/os_unix.c index c61b19060c..fb4536f361 100644 --- a/src/os_unix.c +++ b/src/os_unix.c @@ -3317,10 +3317,15 @@ static int nfsUnlock(sqlite3_file *id, int eFileLock){ ** Seek to the offset passed as the second argument, then read cnt ** bytes into pBuf. Return the number of bytes actually read. ** -** To avoid stomping the errno value on a failed read the lastErrno value -** is set before returning. +** To avoid stomping the errno value on a failed read() call, +** the lastErrno value is set before returning. */ -static int seekAndRead(unixFile *id, sqlite3_int64 offset, void *pBuf, int cnt){ +static SQLITE_NOINLINE int seekAndRead( + unixFile *id, + sqlite3_int64 offset, + void *pBuf, + int cnt +){ int got; int prior = 0; #if (!defined(USE_PREAD) && !defined(USE_PREAD64)) @@ -3364,10 +3369,104 @@ static int seekAndRead(unixFile *id, sqlite3_int64 offset, void *pBuf, int cnt){ return got+prior; } +/* Forward reference */ +static int unixRead(sqlite3_file*,void*,int,sqlite3_int64); + +#if SQLITE_MAX_MMAP_SIZE>0 +/* +** Helper subroutine to unixRead(): Handle the case of reading +** from a memory-mapped file. +*/ +static SQLITE_NOINLINE int unixReadMMap( + unixFile *pFile, /* File from which we are reading */ + void *pBuf, /* Store content here */ + int amt, /* Number of bytes to read */ + sqlite3_int64 offset /* Begin reading at this offset into the file */ +){ + assert( offset < pFile->mmapSize ); + if( offset+amt <= pFile->mmapSize ){ + memcpy(pBuf, &((u8 *)(pFile->pMapRegion))[offset], amt); + return SQLITE_OK; + }else{ + int nCopy = pFile->mmapSize - offset; + assert( nCopy>0 ); + testcase( nCopy>amt/2 ); + memcpy(pBuf, &((u8 *)(pFile->pMapRegion))[offset], nCopy); + return unixRead((sqlite3_file*)pFile,&((u8*)pBuf)[nCopy], + amt-nCopy,offset+nCopy); + } +} +#endif /* SQLITE_MAX_MMAP_SIZE>0 */ + +/* +** Helper subroutine to unixRead(): Handle the rare case where +** the number of bytes returned by pread() or read() is different +** from the number of bytes requested. +*/ +static SQLITE_NOINLINE int unixReadDealWithIssue( + unixFile *pFile, /* Tried to read from this file */ + void *pBuf, /* Results written here */ + int amt, /* Bytes requested */ + sqlite3_int64 offset, /* Offset of first byte to read */ + int got /* Result from the first read attempt */ +){ + while( 1 /*exit-by-return-or-break*/ ){ + if( got<0 ){ + if( errno==EINTR ){ + got = seekAndRead(pFile, offset, pBuf, amt); + if( got==amt ) return SQLITE_OK; + continue; + }else{ + storeLastErrno(pFile, errno); + /* + ** Usually we return SQLITE_IOERR_READ here, though for some + ** kinds of errors we return SQLITE_IOERR_CORRUPTFS. The + ** SQLITE_IOERR_CORRUPTFS will be converted into SQLITE_CORRUPT + ** prior to returning to the application by the sqlite3ApiExit() + ** routine. + */ + switch( pFile->lastErrno ){ + case ERANGE: + case EIO: +#ifdef ENXIO + case ENXIO: +#endif +#ifdef EDEVERR + case EDEVERR: +#endif + return SQLITE_IOERR_CORRUPTFS; + } + return SQLITE_IOERR_READ; + } + }else if( got>0 ){ + offset += got; + pBuf = (void*)(got + (char*)pBuf); + amt -= got; + got = seekAndRead(pFile, offset, pBuf, amt); + if( got==amt ) return SQLITE_OK; + continue; + }else{ + storeLastErrno(pFile, 0); /* not a system error */ + /* Unread parts of the buffer must be zero-filled */ + memset(&((char*)pBuf)[got], 0, amt-got); + break; + } + } + return SQLITE_IOERR_SHORT_READ; +} + + /* ** Read data from a file into a buffer. Return SQLITE_OK if all ** bytes were read successfully and SQLITE_IOERR if anything goes ** wrong. +** +** The most common case (by far) is that this routine does a single +** call to pread() to get the content request and then returns. +** This routine is coded so that that is the fast path. If anything +** unusual happens (an I/O error, the read comes back in smaller +** pieces, memory-mapped I/O, etc) then that processing is handled +** by subroutines so as not to burden the fast path. */ static int unixRead( sqlite3_file *id, @@ -3377,64 +3476,41 @@ static int unixRead( ){ unixFile *pFile = (unixFile *)id; int got; +#if (!defined(USE_PREAD) && !defined(USE_PREAD64)) + i64 newOffset; +#endif + assert( id ); assert( offset>=0 ); assert( amt>0 ); - /* If this is a database file (not a journal, super-journal or temp - ** file), the bytes in the locking range should never be read or written. */ -#if 0 - assert( pFile->pPreallocatedUnused==0 - || offset>=PENDING_BYTE+512 - || offset+amt<=PENDING_BYTE - ); -#endif - #if SQLITE_MAX_MMAP_SIZE>0 /* Deal with as much of this read request as possible by transferring ** data from the memory mapping using memcpy(). */ if( offsetmmapSize ){ - if( offset+amt <= pFile->mmapSize ){ - memcpy(pBuf, &((u8 *)(pFile->pMapRegion))[offset], amt); - return SQLITE_OK; - }else{ - int nCopy = pFile->mmapSize - offset; - memcpy(pBuf, &((u8 *)(pFile->pMapRegion))[offset], nCopy); - pBuf = &((u8 *)pBuf)[nCopy]; - amt -= nCopy; - offset += nCopy; - } + return unixReadMMap(pFile, pBuf, amt, offset); } #endif - got = seekAndRead(pFile, offset, pBuf, amt); - if( got==amt ){ - return SQLITE_OK; - }else if( got<0 ){ - /* pFile->lastErrno has been set by seekAndRead(). - ** Usually we return SQLITE_IOERR_READ here, though for some - ** kinds of errors we return SQLITE_IOERR_CORRUPTFS. The - ** SQLITE_IOERR_CORRUPTFS will be converted into SQLITE_CORRUPT - ** prior to returning to the application by the sqlite3ApiExit() - ** routine. - */ - switch( pFile->lastErrno ){ - case ERANGE: - case EIO: -#ifdef ENXIO - case ENXIO: -#endif -#ifdef EDEVERR - case EDEVERR: +#if defined(USE_PREAD) + got = osPread(pFile->h, pBuf, amt, offset); + SimulateIOError( got = -1 ); +#elif defined(USE_PREAD64) + got = osPread64(pFile->h, pBuf, amt, offset); + SimulateIOError( got = -1 ); +#else + newOffset = lseek(pFile->h, offset, SEEK_SET); + SimulateIOError( newOffset = -1 ); + if( newOffset<0 ){ + return unixReadDealWithIssue(pFile, pBuf, amt, offset, -1); + } + got = osRead(id->h, pBuf, amt); #endif - return SQLITE_IOERR_CORRUPTFS; - } - return SQLITE_IOERR_READ; + + if( got!=amt ){ + return unixReadDealWithIssue(pFile, pBuf, amt, offset, got); }else{ - storeLastErrno(pFile, 0); /* not a system error */ - /* Unread parts of the buffer must be zero-filled */ - memset(&((char*)pBuf)[got], 0, amt-got); - return SQLITE_IOERR_SHORT_READ; + return SQLITE_OK; } } @@ -5360,7 +5436,7 @@ static void unixRemapfile( #endif /* The attempt to extend the existing mapping failed. Free it. */ - if( pNew==MAP_FAILED || pNew==0 ){ + if( pNew==MAP_FAILED || NEVER(pNew==0) ){ osMunmap(pOrig, nReuse); } }