From 306f9d5e1076ff936ef35942bca546ce188fba81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jannik=20Gl=C3=BCckert?= Date: Mon, 6 Mar 2023 20:52:08 +0100 Subject: [PATCH 1/2] libstdc++: also use sendfile for big files we were previously only using sendfile for files smaller than 2GB, as sendfile needs to be called repeatedly for files bigger than that. some quick numbers, copying a 16GB file, average of 10 repetitions: old: real: 13.4s user: 0.14s sys : 7.43s new: real: 8.90s user: 0.00s sys : 3.68s libstdc++-v3/ChangeLog: * src/filesystem/ops-common.h: enable sendfile for files >2GB in std::filesystem::copy_file --- libstdc++-v3/src/filesystem/ops-common.h | 77 ++++++++++++------------ 1 file changed, 40 insertions(+), 37 deletions(-) diff --git a/libstdc++-v3/src/filesystem/ops-common.h b/libstdc++-v3/src/filesystem/ops-common.h index abbfca43e5c..d8afc6a4d64 100644 --- a/libstdc++-v3/src/filesystem/ops-common.h +++ b/libstdc++-v3/src/filesystem/ops-common.h @@ -358,6 +358,24 @@ _GLIBCXX_BEGIN_NAMESPACE_FILESYSTEM } #ifdef NEED_DO_COPY_FILE +#if defined _GLIBCXX_USE_SENDFILE && ! defined _GLIBCXX_FILESYSTEM_IS_WINDOWS + bool + copy_file_sendfile(int fd_in, int fd_out, size_t length) noexcept + { + size_t bytes_left = length; + off_t offset = 0; + ssize_t bytes_copied; + do { + bytes_copied = ::sendfile(fd_out, fd_in, &offset, bytes_left); + if (bytes_copied < 0) + { + return false; + } + bytes_left -= bytes_copied; + } while (bytes_left > 0 && bytes_copied > 0); + return true; + } +#endif bool do_copy_file(const char_type* from, const char_type* to, std::filesystem::copy_options_existing_file options, @@ -498,28 +516,30 @@ _GLIBCXX_BEGIN_NAMESPACE_FILESYSTEM return false; } - size_t count = from_st->st_size; + bool has_copied = false; + #if defined _GLIBCXX_USE_SENDFILE && ! defined _GLIBCXX_FILESYSTEM_IS_WINDOWS - off_t offset = 0; - ssize_t n = ::sendfile(out.fd, in.fd, &offset, count); - if (n < 0 && errno != ENOSYS && errno != EINVAL) + if (!has_copied) + has_copied = copy_file_sendfile(in.fd, out.fd, from_st->st_size); + if (!has_copied) { - ec.assign(errno, std::generic_category()); - return false; + if (errno != ENOSYS && errno != EINVAL) + { + ec.assign(errno, std::generic_category()); + return false; + } } - if ((size_t)n == count) +#endif + + if (has_copied) { - if (!out.close() || !in.close()) - { - ec.assign(errno, std::generic_category()); - return false; - } - ec.clear(); - return true; + if (!out.close() || !in.close()) + { + ec.assign(errno, std::generic_category()); + return false; + } + return true; } - else if (n > 0) - count -= n; -#endif // _GLIBCXX_USE_SENDFILE using std::ios; __gnu_cxx::stdio_filebuf sbin(in.fd, ios::in|ios::binary); @@ -530,29 +550,12 @@ _GLIBCXX_BEGIN_NAMESPACE_FILESYSTEM if (sbout.is_open()) out.fd = -1; -#ifdef _GLIBCXX_USE_SENDFILE - if (n != 0) + if (!(std::ostream(&sbout) << &sbin)) { - if (n < 0) - n = 0; - - const auto p1 = sbin.pubseekoff(n, ios::beg, ios::in); - const auto p2 = sbout.pubseekoff(n, ios::beg, ios::out); - - const std::streampos errpos(std::streamoff(-1)); - if (p1 == errpos || p2 == errpos) - { - ec = std::make_error_code(std::errc::io_error); - return false; - } + ec = std::make_error_code(std::errc::io_error); + return false; } -#endif - if (count && !(std::ostream(&sbout) << &sbin)) - { - ec = std::make_error_code(std::errc::io_error); - return false; - } if (!sbout.close() || !sbin.close()) { ec.assign(errno, std::generic_category()); -- 2.39.2