Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 73 additions & 15 deletions benchmarks/algorithms.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
#define YY_DOUBLE_SUPPORTED 0
#endif


template<arithmetic_float T>
struct BenchArgs {
using Type = T;
Expand All @@ -57,14 +58,7 @@ struct BenchArgs {
bool used{};
size_t testRepeat{100};

static void initFixedSize(size_t size) {
fixedSize = size;
snprintf(formatStr, sizeof(formatStr), "%%.%zug", fixedSize);
formatStrStr = fmt::format("{{:.{}g}}", fixedSize);
}
static inline size_t fixedSize;
static inline char formatStr[10];
static inline std::string formatStrStr;
};

namespace BenchmarkShortest {
Expand Down Expand Up @@ -321,21 +315,85 @@ int abseil(T d, std::span<char>& buffer) {
// absl::StrAppend(&s, d);
// std::copy(s.begin(), s.end(), buffer.begin());
// return size(s);
return absl::SNPrintF(buffer.data(), buffer.size(),
BenchArgs<T>::formatStr, d);
//
// The switch should be very cheap if fixedSize is very predictable.
// Essentially just a predictable jump.
switch (BenchArgs<T>::fixedSize) {
case 0: return absl::SNPrintF(buffer.data(), buffer.size(), "%.0g", d);
case 1: return absl::SNPrintF(buffer.data(), buffer.size(), "%.1g", d);
case 2: return absl::SNPrintF(buffer.data(), buffer.size(), "%.2g", d);
case 3: return absl::SNPrintF(buffer.data(), buffer.size(), "%.3g", d);
case 4: return absl::SNPrintF(buffer.data(), buffer.size(), "%.4g", d);
case 5: return absl::SNPrintF(buffer.data(), buffer.size(), "%.5g", d);
case 6: return absl::SNPrintF(buffer.data(), buffer.size(), "%.6g", d);
case 7: return absl::SNPrintF(buffer.data(), buffer.size(), "%.7g", d);
case 8: return absl::SNPrintF(buffer.data(), buffer.size(), "%.8g", d);
case 9: return absl::SNPrintF(buffer.data(), buffer.size(), "%.9g", d);
case 10: return absl::SNPrintF(buffer.data(), buffer.size(), "%.10g", d);
case 11: return absl::SNPrintF(buffer.data(), buffer.size(), "%.11g", d);
case 12: return absl::SNPrintF(buffer.data(), buffer.size(), "%.12g", d);
case 13: return absl::SNPrintF(buffer.data(), buffer.size(), "%.13g", d);
case 14: return absl::SNPrintF(buffer.data(), buffer.size(), "%.14g", d);
case 15: return absl::SNPrintF(buffer.data(), buffer.size(), "%.15g", d);
case 16: return absl::SNPrintF(buffer.data(), buffer.size(), "%.16g", d);
case 17: return absl::SNPrintF(buffer.data(), buffer.size(), "%.17g", d);
default:
return absl::SNPrintF(buffer.data(), buffer.size(), "%.17g", d);
}
}

template<arithmetic_float T>
int snprintf(T d, std::span<char>& buffer) {
return std::snprintf(buffer.data(), buffer.size(),
BenchArgs<T>::formatStr, d);
// The switch should be very cheap if fixedSize is very predictable.
// Essentially just a predictable jump.
switch (BenchArgs<T>::fixedSize) {
case 0: return std::snprintf(buffer.data(), buffer.size(), "%.0g", d);
case 1: return std::snprintf(buffer.data(), buffer.size(), "%.1g", d);
case 2: return std::snprintf(buffer.data(), buffer.size(), "%.2g", d);
case 3: return std::snprintf(buffer.data(), buffer.size(), "%.3g", d);
case 4: return std::snprintf(buffer.data(), buffer.size(), "%.4g", d);
case 5: return std::snprintf(buffer.data(), buffer.size(), "%.5g", d);
case 6: return std::snprintf(buffer.data(), buffer.size(), "%.6g", d);
case 7: return std::snprintf(buffer.data(), buffer.size(), "%.7g", d);
case 8: return std::snprintf(buffer.data(), buffer.size(), "%.8g", d);
case 9: return std::snprintf(buffer.data(), buffer.size(), "%.9g", d);
case 10: return std::snprintf(buffer.data(), buffer.size(), "%.10g", d);
case 11: return std::snprintf(buffer.data(), buffer.size(), "%.11g", d);
case 12: return std::snprintf(buffer.data(), buffer.size(), "%.12g", d);
case 13: return std::snprintf(buffer.data(), buffer.size(), "%.13g", d);
case 14: return std::snprintf(buffer.data(), buffer.size(), "%.14g", d);
case 15: return std::snprintf(buffer.data(), buffer.size(), "%.15g", d);
case 16: return std::snprintf(buffer.data(), buffer.size(), "%.16g", d);
case 17: return std::snprintf(buffer.data(), buffer.size(), "%.17g", d);
default:
return std::snprintf(buffer.data(), buffer.size(), "%.17g", d);
}
}

template<arithmetic_float T>
int fmt_format(T d, std::span<char>& buffer) {
const auto it = fmt::format_to(buffer.begin(),
fmt::runtime(BenchArgs<T>::formatStrStr), d);
return std::distance(buffer.begin(), it);
switch(BenchArgs<T>::fixedSize) {
case 0: return fmt::format_to(buffer.data(), "{}", d) - buffer.data();
case 1: return fmt::format_to(buffer.data(), "{:.1g}", d) - buffer.data();
case 2: return fmt::format_to(buffer.data(), "{:.2g}", d) - buffer.data();
case 3: return fmt::format_to(buffer.data(), "{:.3g}", d) - buffer.data();
case 4: return fmt::format_to(buffer.data(), "{:.4g}", d) - buffer.data();
case 5: return fmt::format_to(buffer.data(), "{:.5g}", d) - buffer.data();
case 6: return fmt::format_to(buffer.data(), "{:.6g}", d) - buffer.data();
case 7: return fmt::format_to(buffer.data(), "{:.7g}", d) - buffer.data();
case 8: return fmt::format_to(buffer.data(), "{:.8g}", d) - buffer.data();
case 9: return fmt::format_to(buffer.data(), "{:.9g}", d) - buffer.data();
case 10: return fmt::format_to(buffer.data(), "{:.10g}", d) - buffer.data();
case 11: return fmt::format_to(buffer.data(), "{:.11g}", d) - buffer.data();
case 12: return fmt::format_to(buffer.data(), "{:.12g}", d) - buffer.data();
case 13: return fmt::format_to(buffer.data(), "{:.13g}", d) - buffer.data();
case 14: return fmt::format_to(buffer.data(), "{:.14g}", d) - buffer.data();
case 15: return fmt::format_to(buffer.data(), "{:.15g}", d) - buffer.data();
case 16: return fmt::format_to(buffer.data(), "{:.16g}", d) - buffer.data();
case 17: return fmt::format_to(buffer.data(), "{:.17g}", d) - buffer.data();
default:
return fmt::format_to(buffer.data(), "{:.17g}", d) - buffer.data();
}
}

template<arithmetic_float T>
Expand Down Expand Up @@ -407,7 +465,7 @@ std::vector<BenchArgs<T>> initArgs(bool use_errol = false, size_t repeat = 0, si
// grisu2 does not round-trip correctly
} else { // fixed-length representation
fmt::println("# testing fixed-size output to {} digits", fixed_size);
BenchArgs<T>::initFixedSize(fixed_size);
BenchArgs<T>::fixedSize = fixed_size;

namespace f = BenchmarkFixedSize;
args.emplace_back("dragon4" , wrap(f::dragon4<T>) , true , 10);
Expand Down
6 changes: 4 additions & 2 deletions benchmarks/floatutils.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include <string>
#include <sstream>
#include <optional>
#include <fast_float/fast_float.h>

template<typename T>
concept arithmetic_float
Expand Down Expand Up @@ -47,8 +48,9 @@ std::optional<T> parse_float(std::string_view sv) {
T result;
const char* begin = sv.data();
const char* end = sv.data() + sv.size();

auto [ptr, ec] = std::from_chars(begin, end, result);
// Using fastfloat for parsing and not std::from_chars, since
// fastfloat is always available and supports both float and double.
auto [ptr, ec] = fast_float::from_chars(begin, end, result);

// Check if parsing succeeded and consumed the entire string
if (ec == std::errc{} && ptr == end) {
Expand Down
31 changes: 20 additions & 11 deletions dependencies/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ if (NOT CYGWIN)
set(ABSL_USE_GOOGLETEST_HEAD OFF CACHE INTERNAL "")

FetchContent_Declare(abseil
GIT_REPOSITORY https://github.com/abseil/abseil-cpp.git
GIT_TAG "d9e4955") # Abseil LTS branch, January 2025, Patch 1
URL https://github.com/abseil/abseil-cpp/archive/d9e4955.zip
URL_HASH SHA256=b8292600560c7562cd4e96ba397ee205c0450af352061d9303d1e20d15ef8b6b)
FetchContent_GetProperties(abseil)
if(NOT abseil_POPULATED)
set(BUILD_TESTING OFF)
Expand All @@ -30,9 +30,8 @@ set(CMAKE_POLICY_DEFAULT_CMP0077 NEW)

FetchContent_Declare(
doubleconversion
GIT_REPOSITORY https://github.com/google/double-conversion.git
GIT_TAG 101e1ba89dc41ceb75090831da97c43a76cd2906
# GIT_SHALLOW TRUE
URL https://github.com/google/double-conversion/archive/101e1ba89dc41ceb75090831da97c43a76cd2906.zip
URL_HASH SHA256=be99800f12675849023235c77058c3f272cb0222891c9ac17101c2fbeed00c24
)
FetchContent_MakeAvailable(doubleconversion)

Expand Down Expand Up @@ -60,7 +59,9 @@ FetchContent_Declare(
GIT_SHALLOW TRUE
)
FetchContent_MakeAvailable(ryu)

set(ryu_SOURCE_DIR ${ryu_SOURCE_DIR} PARENT_SCOPE)
message(STATUS "ryu source directory: ${ryu_SOURCE_DIR}")

FetchContent_Declare(
grisu-exact
Expand All @@ -70,6 +71,7 @@ FetchContent_Declare(
)
FetchContent_MakeAvailable(grisu-exact)
set(grisu-exact_SOURCE_DIR ${grisu-exact_SOURCE_DIR} PARENT_SCOPE)
message(STATUS "grisu source directory: ${grisu-exact_SOURCE_DIR}")

FetchContent_Declare(
dragonbox
Expand All @@ -78,6 +80,7 @@ FetchContent_Declare(
GIT_SHALLOW TRUE
)
FetchContent_MakeAvailable(dragonbox)
message(STATUS "dragonbox source directory: ${dragonbox_SOURCE_DIR}")

FetchContent_Declare(
dragon4
Expand All @@ -90,12 +93,15 @@ if(NOT dragon4)
FetchContent_Populate(dragon4) # Downloads code to ${dragon4_SOURCE_DIR}
endif()
set(dragon4_SOURCE_DIR ${dragon4_SOURCE_DIR} PARENT_SCOPE)
message(STATUS "dragon4 source directory: ${dragon4_SOURCE_DIR}")

add_library(dragon4_lib STATIC
${dragon4_SOURCE_DIR}/Dragon4.cpp
${dragon4_SOURCE_DIR}/DragonMath.cpp
${dragon4_SOURCE_DIR}/PrintFloat.cpp
)
if(MATH_LIBRARY)
message(STATUS "Using math library")
target_link_libraries(dragon4_lib PUBLIC ${MATH_LIBRARY})
endif()
target_include_directories(dragon4_lib PUBLIC
Expand All @@ -104,17 +110,19 @@ target_include_directories(dragon4_lib PUBLIC

# Swift code may not build under Windows without extra care.
if(NOT WIN32)
message(STATUS "Grabbing Swift code")
FetchContent_Declare(
swift
GIT_REPOSITORY https://github.com/swiftlang/swift.git
GIT_TAG 6a862d2eb7128ff1f317b07e8ad1a6da939775f3
GIT_SHALLOW TRUE
URL https://github.com/swiftlang/swift/archive/6a862d2eb7128ff1f317b07e8ad1a6da939775f3.tar.gz
URL_HASH SHA256=281673f70a1695ce26f5463ac43842ffdb546d6d888338f488f71015564e51a0
)
FetchContent_GetProperties(swift)
if(NOT swift)
FetchContent_Populate(swift) # Downloads code to ${dragon4_SOURCE_DIR}
endif()
set(swift_SOURCE_DIR ${swift_SOURCE_DIR} PARENT_SCOPE)
message(STATUS "Swift directory: ${swift_SOURCE_DIR}")

add_library(swift_lib STATIC
${swift_SOURCE_DIR}/stdlib/public/runtime/SwiftDtoa.cpp
)
Expand All @@ -128,15 +136,16 @@ endif(NOT WIN32)

FetchContent_Declare(
drachennest # for schubfach
GIT_REPOSITORY https://github.com/abolz/Drachennest.git
GIT_TAG e6714a39ad331b4489d0b6aaf3968635bff4eb5e
GIT_SHALLOW TRUE
URL https://github.com/abolz/Drachennest/archive/e6714a39ad331b4489d0b6aaf3968635bff4eb5e.zip
URL_HASH SHA256=6a5526d17b47c8e8c264133d717d99693f01783aa4a50e144d7a0e28363647bb
)
FetchContent_GetProperties(drachennest)
if(NOT drachennest_POPULATED)
FetchContent_Populate(drachennest) # Downloads code to ${drachennest_SOURCE_DIR}
endif()
set(drachennest_SOURCE_DIR/src ${drachennest_SOURCE_DIR}/src PARENT_SCOPE)
message(STATUS "drachennest directory: ${drachennest_SOURCE_DIR}")

add_library(dragon_schubfach_lib STATIC
${drachennest_SOURCE_DIR}/src/dragon4.cc
${drachennest_SOURCE_DIR}/src/schubfach_32.cc
Expand Down