From b385ac7a171120a078785fd979b7dfc25f75660a Mon Sep 17 00:00:00 2001 From: MacroModel Date: Mon, 8 Dec 2025 22:15:35 +0800 Subject: [PATCH 1/4] update factory --- include/fast_io_hosted/platforms/posix.h | 40 +++++++++++++++--- .../platforms/win32_network/socket_file.h | 42 ++++++++++++++++--- 2 files changed, 71 insertions(+), 11 deletions(-) diff --git a/include/fast_io_hosted/platforms/posix.h b/include/fast_io_hosted/platforms/posix.h index 67f3794e..7d7a7d9a 100644 --- a/include/fast_io_hosted/platforms/posix.h +++ b/include/fast_io_hosted/platforms/posix.h @@ -1211,17 +1211,47 @@ struct posix_file_factory FAST_IO_TRIVIALLY_RELOCATABLE_IF_ELIGIBLE { using native_handle_type = int; int fd{-1}; - inline explicit constexpr posix_file_factory(int v) noexcept - : fd(v) {}; - inline posix_file_factory(posix_file_factory const &) = delete; - inline posix_file_factory &operator=(posix_file_factory const &) = delete; - inline ~posix_file_factory() + inline constexpr posix_file_factory() noexcept = default; + inline explicit constexpr posix_file_factory(int v) noexcept : fd(v) + {} + inline constexpr posix_file_factory(posix_file_factory const &) = delete; + inline constexpr posix_file_factory &operator=(posix_file_factory const &) = delete; + inline constexpr posix_file_factory(posix_file_factory &&other) noexcept + : fd(other.fd) + { + other.fd = -1; + } + inline constexpr posix_file_factory &operator=(posix_file_factory &&other) noexcept + { + if (__builtin_addressof(other) == this) [[unlikely]] + { + return *this; + } + if (this->fd != -1) [[likely]] + { + ::fast_io::details::sys_close(this->fd); + } + this->fd = other.fd; + other.fd = -1; + return *this; + } + inline constexpr ~posix_file_factory() { if (fd != -1) [[likely]] { ::fast_io::details::sys_close(fd); } } + inline constexpr native_handle_type native_handle() const noexcept + { + return this->fd; + } + inline constexpr native_handle_type release() noexcept + { + auto fd{this->fd}; + this->fd = -1; + return fd; + } }; template <::fast_io::posix_family family, ::std::integral ch_type> diff --git a/include/fast_io_hosted/platforms/win32_network/socket_file.h b/include/fast_io_hosted/platforms/win32_network/socket_file.h index 998c6610..99cd9bd3 100644 --- a/include/fast_io_hosted/platforms/win32_network/socket_file.h +++ b/include/fast_io_hosted/platforms/win32_network/socket_file.h @@ -29,7 +29,7 @@ struct win32_socket_event_guard_t return *this; } - if(curr_handle) [[likely]] + if (curr_handle) [[likely]] { ::fast_io::win32::WSACloseEvent(curr_handle); } @@ -534,19 +534,49 @@ inline ::std::size_t open_win32_socket_impl(sock_family d, sock_type t, open_mod struct win32_socket_factory FAST_IO_TRIVIALLY_RELOCATABLE_IF_ELIGIBLE { using native_handle_type = ::std::size_t; - ::std::size_t hsocket{}; + ::std::size_t hsocket{SIZE_MAX}; + inline constexpr win32_socket_factory() noexcept = default; inline explicit constexpr win32_socket_factory(::std::size_t v) noexcept : hsocket(v) {} - inline win32_socket_factory(win32_socket_factory const &) = delete; - inline win32_socket_factory &operator=(win32_socket_factory const &) = delete; - inline ~win32_socket_factory() + inline constexpr win32_socket_factory(win32_socket_factory const &) = delete; + inline constexpr win32_socket_factory &operator=(win32_socket_factory const &) = delete; + inline constexpr win32_socket_factory(win32_socket_factory &&other) noexcept + : hsocket(other.hsocket) + { + other.hsocket = SIZE_MAX; + } + inline constexpr win32_socket_factory &operator=(win32_socket_factory &&other) noexcept + { + if (__builtin_addressof(other) == this) [[unlikely]] + { + return *this; + } + if (hsocket != SIZE_MAX) [[likely]] + { + ::fast_io::win32::closesocket(hsocket); + } + hsocket = other.hsocket; + other.hsocket = SIZE_MAX; + return *this; + } + inline constexpr ~win32_socket_factory() { - if (hsocket) [[likely]] + if (hsocket != SIZE_MAX) [[likely]] { ::fast_io::win32::closesocket(hsocket); } } + inline constexpr native_handle_type native_handle() const noexcept + { + return hsocket; + } + inline constexpr native_handle_type release() noexcept + { + native_handle_type temp{hsocket}; + hsocket = SIZE_MAX; + return temp; + } }; template From 7f74bc6a609f3e0b09ffabb38ceb22dc3a50309e Mon Sep 17 00:00:00 2001 From: MacroModel Date: Mon, 8 Dec 2025 22:19:08 +0800 Subject: [PATCH 2/4] fix --- include/fast_io_hosted/platforms/posix.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/fast_io_hosted/platforms/posix.h b/include/fast_io_hosted/platforms/posix.h index 7d7a7d9a..a1836191 100644 --- a/include/fast_io_hosted/platforms/posix.h +++ b/include/fast_io_hosted/platforms/posix.h @@ -1248,9 +1248,9 @@ struct posix_file_factory FAST_IO_TRIVIALLY_RELOCATABLE_IF_ELIGIBLE } inline constexpr native_handle_type release() noexcept { - auto fd{this->fd}; + auto curr_fd{this->fd}; this->fd = -1; - return fd; + return curr_fd; } }; From 52a56e9371f216807cba24f7f77ea5b73117ef1c Mon Sep 17 00:00:00 2001 From: MacroModel Date: Tue, 9 Dec 2025 07:56:33 +0100 Subject: [PATCH 3/4] Add benchmark for hex number parsing performance comparisons - Introduced a new benchmark file `atoi_vs_from_chars_hex.cc` to compare the performance of different methods for parsing hexadecimal numbers, including `strtoul`, `std::from_chars`, `fast_io::char_digit_to_literal`, and `fast_float::from_chars`. - Updated `CMakeLists.txt` to include the new benchmark executable and ensure proper linking with the `fast_float` library. --- benchmark/0022.from_chars/CMakeLists.txt | 14 +- .../0022.from_chars/atoi_vs_from_chars_hex.cc | 133 ++++++++++++++++++ 2 files changed, 146 insertions(+), 1 deletion(-) create mode 100644 benchmark/0022.from_chars/atoi_vs_from_chars_hex.cc diff --git a/benchmark/0022.from_chars/CMakeLists.txt b/benchmark/0022.from_chars/CMakeLists.txt index ca3b4791..9b3cb037 100644 --- a/benchmark/0022.from_chars/CMakeLists.txt +++ b/benchmark/0022.from_chars/CMakeLists.txt @@ -10,7 +10,6 @@ FetchContent_MakeAvailable(fast_float) add_executable(benchmark.0022.from_chars ${CMAKE_CURRENT_LIST_DIR}/atoi_vs_from_chars.cc) target_include_directories(benchmark.0022.from_chars PRIVATE ${CMAKE_SOURCE_DIR}/include) - target_compile_features(benchmark.0022.from_chars PRIVATE cxx_std_20) # Prefer official target if provided by fast_float; otherwise include headers directly @@ -22,3 +21,16 @@ else() target_include_directories(benchmark.0022.from_chars PRIVATE ${fast_float_SOURCE_DIR}/include) endif() endif() + +add_executable(benchmark.0022.from_chars_hex ${CMAKE_CURRENT_LIST_DIR}/atoi_vs_from_chars_hex.cc) +target_include_directories(benchmark.0022.from_chars_hex PRIVATE ${CMAKE_SOURCE_DIR}/include) +target_compile_features(benchmark.0022.from_chars_hex PRIVATE cxx_std_20) + +if (TARGET fast_float::fast_float) + target_link_libraries(benchmark.0022.from_chars_hex PRIVATE fast_float::fast_float) +else() + FetchContent_GetProperties(fast_float) + if (fast_float_SOURCE_DIR) + target_include_directories(benchmark.0022.from_chars_hex PRIVATE ${fast_float_SOURCE_DIR}/include) + endif() +endif() diff --git a/benchmark/0022.from_chars/atoi_vs_from_chars_hex.cc b/benchmark/0022.from_chars/atoi_vs_from_chars_hex.cc new file mode 100644 index 00000000..8ba96d8b --- /dev/null +++ b/benchmark/0022.from_chars/atoi_vs_from_chars_hex.cc @@ -0,0 +1,133 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace fast_io::io; + +static std::string make_hex_numbers_buffer(std::size_t n) +{ + std::string s; + s.reserve(n * 8); + for (std::size_t i{}; i != n; ++i) + { + auto old = s.size(); + s.resize(old + 32); + auto *first = s.data() + old; + auto *last = s.data() + s.size(); + auto res = std::to_chars(first, last - 1, i, 16); + *res.ptr = '\n'; + s.resize(static_cast(res.ptr - s.data() + 1)); + } + return s; +} + +int main() +{ + constexpr std::size_t N = 10'000'000; + auto buf = make_hex_numbers_buffer(N); + char const *begin = buf.data(); + char const *end = buf.data() + buf.size(); + + { + std::size_t lines{}; + for (char const *p = begin; p < end; ++p) + { + lines += (*p == '\n'); + } + fast_io::println("lines=", lines); + } + + // strtoul (hex) + { + fast_io::timer t(u8"strtoul_hex"); + std::uint64_t sum{}; + char const *p = begin; + while (p < end) + { + char *endptr{}; + auto v = std::strtoul(p, &endptr, 16); + sum += static_cast(v); + p = endptr; + if (p < end && *p == '\n') + { + ++p; + } + } + std::uint64_t volatile sink = sum; + (void)sink; + } + + // std::from_chars (hex) + { + fast_io::timer t(u8"std_from_chars_hex"); + std::uint64_t sum{}; + char const *p = begin; + while (p < end) + { + std::uint64_t v{}; + auto res = std::from_chars(p, end, v, 16); + sum += v; + p = res.ptr; + if (p < end && *p == '\n') + { + ++p; + } + } + std::uint64_t volatile sink = sum; + (void)sink; + } + + // fast_io char_digit_to_literal (hex) + { + fast_io::timer t(u8"fastio_char_digit_to_literal_hex"); + std::uint64_t sum{}; + char const *p = begin; + while (p < end) + { + using UCh = std::make_unsigned_t; + std::uint64_t v{}; + char const *q = p; + while (q < end && *q != '\n') + { + UCh ch = static_cast(*q); + if (fast_io::details::char_digit_to_literal<16, char>(ch)) + { + break; + } + v = (v << 4) + static_cast(ch); + ++q; + } + sum += v; + p = (q < end ? q + 1 : q); + } + std::uint64_t volatile sink = sum; + (void)sink; + } + + // fast_float (hex) + { + fast_io::timer t(u8"fast_float_from_chars_hex"); + std::uint64_t sum{}; + char const *p = begin; + while (p < end) + { + std::uint64_t v{}; + auto res = fast_float::from_chars(p, end, v, 16); + sum += v; + p = res.ptr; + if (p < end && *p == '\n') + { + ++p; + } + } + std::uint64_t volatile sink = sum; + (void)sink; + } +} + From 2090641030e92206d27ad9bfcb237f0e403a5cca Mon Sep 17 00:00:00 2001 From: MacroModel Date: Tue, 9 Dec 2025 15:23:46 +0800 Subject: [PATCH 4/4] Enhance hex number buffer generation in benchmark - Modified the `make_hex_numbers_buffer` function to mix lowercase and uppercase hex digits in the generated buffer, improving the representation of hexadecimal numbers for benchmarking purposes. --- benchmark/0022.from_chars/atoi_vs_from_chars_hex.cc | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/benchmark/0022.from_chars/atoi_vs_from_chars_hex.cc b/benchmark/0022.from_chars/atoi_vs_from_chars_hex.cc index 8ba96d8b..166d484e 100644 --- a/benchmark/0022.from_chars/atoi_vs_from_chars_hex.cc +++ b/benchmark/0022.from_chars/atoi_vs_from_chars_hex.cc @@ -21,6 +21,17 @@ static std::string make_hex_numbers_buffer(std::size_t n) auto *first = s.data() + old; auto *last = s.data() + s.size(); auto res = std::to_chars(first, last - 1, i, 16); + // mix lowercase/uppercase hex digits in the buffer + if ((i & 1u) != 0u) + { + for (auto p = first; p != res.ptr; ++p) + { + if (*p >= 'a' && *p <= 'f') + { + *p = static_cast(*p - 'a' + 'A'); + } + } + } *res.ptr = '\n'; s.resize(static_cast(res.ptr - s.data() + 1)); }