]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
At attempt at improving the performance of unixRead(). Partly successfuly, fast-read
authordrh <>
Fri, 7 Jun 2024 19:48:05 +0000 (19:48 +0000)
committerdrh <>
Fri, 7 Jun 2024 19:48:05 +0000 (19:48 +0000)
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

manifest
manifest.uuid
src/os_unix.c

index f28f11b60c3bb6a87f54a3692b1383210cc07c6b..3e283db9bb4792f43368747ad2ed9f1a091d2760 100644 (file)
--- 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.
index a310670ce9220048d1da1e4803b542edb93d406e..a6ef769dc5703745e2416e7f19b3fc8e6a9f2ddc 100644 (file)
@@ -1 +1 @@
-c4a9dda2809c6e0e3d928e11e5553ead82cd9df551bcd35b11a7d869ef80ab8e
\ No newline at end of file
+aebc790a16a191f7e7002e91b75da0dd16673c6d0831c3ba3e67af88dd7a97d2
\ No newline at end of file
index c61b19060c5f160a5f7d0ae073c48c2be20e25a2..fb4536f361748a6802bfb23b5f1f725806f4f771 100644 (file)
@@ -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( offset<pFile->mmapSize ){
-    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);
     }
   }