From 4f39181b2f053c0a134e646226547bc05a7353fa Mon Sep 17 00:00:00 2001 From: Tomas Volf <~@wolfsden.cz> Date: Fri, 28 Feb 2025 02:10:18 +0100 Subject: [PATCH] filesys.c: Use scm_sendfile to copy files Use scm_sendfile instead of read-write loop. This moves the work into the kernel, improving performance. This implements Ludovic's suggestion from https://debbugs.gnu.org/68504 * libguile/filesys.c (scm_copy_file2): Use scm_sendfile. [rlb@defaultvalue.org: add NEWS] --- NEWS | 4 ++++ libguile/filesys.c | 24 ++++++++++++++---------- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/NEWS b/NEWS index 2adbdf099..1b9e1877f 100644 --- a/NEWS +++ b/NEWS @@ -45,6 +45,10 @@ for-delimited-from-port and for-line-in-file. Of these, for-line-in-file is helpful in the common situation where you want a procedure applied to every line in a file. +* Performance improvements + +** `copy-file` now relies on `sendfile` rather than a read/write loop + * Changes to the distribution * Build system changes diff --git a/libguile/filesys.c b/libguile/filesys.c index 0e7078cf0..be223a85d 100644 --- a/libguile/filesys.c +++ b/libguile/filesys.c @@ -1,7 +1,7 @@ /* Copyright 1996-2002,2004,2006,2009-2019,2021 Free Software Foundation, Inc. Copyright 2021 Maxime Devos - Copyright 2024 Tomas Volf <~@wolfsden.cz> + Copyright 2024, 2025 Tomas Volf <~@wolfsden.cz> This file is part of Guile. @@ -1306,10 +1306,9 @@ SCM_DEFINE (scm_copy_file2, "copy-file", 2, 0, 1, { char *c_oldfile, *c_newfile; int oldfd, newfd; - int n, rv; + int rv; SCM cow = sym_auto; int clone_res; - char buf[BUFSIZ]; struct stat_or_stat64 oldstat; scm_dynwind_begin (0); @@ -1354,13 +1353,18 @@ SCM_DEFINE (scm_copy_file2, "copy-file", 2, 0, 1, scm_syserror ("copy-file: copy-on-write failed"); if (clone_res) - while ((n = read (oldfd, buf, sizeof buf)) > 0) - if (write (newfd, buf, n) != n) - { - close (oldfd); - close (newfd); - SCM_SYSERROR; - } + { + off_t end; + if ((end = lseek_or_lseek64 (oldfd, 0, SEEK_END)) < 0) + SCM_SYSERROR; + if (lseek_or_lseek64 (oldfd, 0, SEEK_SET) < 0) + SCM_SYSERROR; + + scm_sendfile (scm_from_int (newfd), + scm_from_int (oldfd), + scm_from_off_t (end), + SCM_UNDEFINED); + } close (oldfd); if (close (newfd) == -1) SCM_SYSERROR;