Tcl Source Code

Artifact [05e9c18076]
Login

Artifact 05e9c1807642f95b9ce17b15ccc6e64264a33ebc250959230fde65284e04dda6:

Attachment "sendfile.diff" to ticket [6ad9883ccf] added by chw 2023-12-24 13:43:59.
Index: unix/tclUnixFCmd.c
==================================================================
--- unix/tclUnixFCmd.c
+++ unix/tclUnixFCmd.c
@@ -47,10 +47,13 @@
 #endif
 #endif /* !HAVE_STRUCT_STAT_ST_BLKSIZE */
 #ifdef HAVE_FTS
 #include <fts.h>
 #endif
+#ifdef linux
+#include <sys/sendfile.h>
+#endif
 
 /*
  * The following constants specify the type of callback when
  * TraverseUnixTree() calls the traverseProc()
  */
@@ -545,10 +548,13 @@
 {
     int srcFd, dstFd;
     size_t blockSize;		/* Optimal I/O blocksize for filesystem */
     char *buffer;		/* Data buffer for copy */
     ssize_t nread;
+#ifdef linux
+    size_t count, sfBlockSize;
+#endif
 
 #ifdef DJGPP
 #define BINMODE |O_BINARY
 #else
 #define BINMODE
@@ -598,10 +604,39 @@
      */
 
     if (blockSize <= 0) {
 	blockSize = DEFAULT_COPY_BLOCK_SIZE;
     }
+
+#ifdef linux
+    /*
+     * Try to use sendfile() system call for speed.
+     */
+
+    sfBlockSize = statBufPtr->st_size;
+    if (sfBlockSize > 0x10000000) {
+	sfBlockSize = 0x10000000;	/* Should be < 0x7FFFF000. */
+    } else if (sfBlockSize <= 0) {
+	sfBlockSize = blockSize * 256;
+    }
+    count = 0;
+    do {
+	nread = sendfile(dstFd, srcFd, NULL, sfBlockSize);
+	if (nread == -1) {
+	    if ((count == 0) && ((errno == EINVAL) || (errno == ENOSYS))) {
+		/* Fallback to slow r/w based copy operation. */
+		goto slowCopy;
+	    }
+	    break;
+	}
+	count++;
+    } while (nread > 0);
+    goto done;
+
+  slowCopy:
+#endif
+
     buffer = (char *)Tcl_Alloc(blockSize);
     while (1) {
 	nread = read(srcFd, buffer, blockSize);
 	if ((nread == -1) || (nread == 0)) {
 	    break;
@@ -611,10 +646,14 @@
 	    break;
 	}
     }
 
     Tcl_Free(buffer);
+
+#ifdef linux
+  done:
+#endif
     close(srcFd);
     if ((close(dstFd) != 0) || (nread == -1)) {
 	unlink(dst);					/* INTL: Native. */
 	return TCL_ERROR;
     }