Skip to content

Commit 1f4c238

Browse files
committed
refactor each solver in separate function
1 parent 32ea807 commit 1f4c238

File tree

4 files changed

+291
-261
lines changed

4 files changed

+291
-261
lines changed

benchmarks/CMakeLists.txt

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,13 @@ if (NOT WIN32)
2626
endif()
2727

2828
if (NOT CYGWIN)
29-
target_link_libraries(benchmark PUBLIC absl::strings)
29+
target_link_libraries(benchmark PUBLIC absl::strings absl::str_format)
30+
target_compile_definitions(benchmark PUBLIC ABSEIL_SUPPORTED=1)
31+
endif()
32+
33+
if(TARGET errol)
34+
target_link_libraries(benchmark PUBLIC errol)
35+
target_compile_definitions(benchmark PUBLIC ERROL_SUPPORTED=1)
3036
endif()
3137

3238
target_link_libraries(benchmark PUBLIC fmt)
@@ -41,7 +47,4 @@ if(teju_has_float128)
4147
endif()
4248
target_link_libraries(benchmark PUBLIC dragonbox::dragonbox_to_chars)
4349
target_link_libraries(benchmark PUBLIC dragon_schubfach_lib)
44-
if(TARGET errol)
45-
target_link_libraries(benchmark PUBLIC errol)
46-
endif()
4750
target_include_directories(benchmark PUBLIC ${grisu-exact_SOURCE_DIR})

benchmarks/algorithms.h

Lines changed: 216 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,216 @@
1+
#ifndef ALGORITHMS_H
2+
#define ALGORITHMS_H
3+
4+
#ifndef __CYGWIN__
5+
#include "absl/strings/str_cat.h"
6+
#endif
7+
8+
#if ERROL_SUPPORTED
9+
#include "errol.h"
10+
#endif
11+
12+
#if FROM_CHARS_SUPPORTED
13+
#include <charconv>
14+
#endif
15+
16+
#include <fmt/format.h>
17+
#include <gdtoa.h> // Netlib
18+
19+
#include <span>
20+
21+
#include "cpp/common/traits.hpp" // Teju Jagua
22+
#include "double-conversion/double-conversion.h"
23+
#include "dragon4.h"
24+
#include "dragonbox/dragonbox_to_chars.h"
25+
#include "grisu2.h"
26+
#include "grisu_exact.h"
27+
#include "ieeeToString.h"
28+
#include "ryu/ryu.h"
29+
#include "schubfach_32.h"
30+
#include "schubfach_64.h"
31+
32+
namespace Benchmarks {
33+
34+
enum Algorithm {
35+
DRAGON4 = 0,
36+
ERROL3 = 1,
37+
TO_STRING = 2,
38+
FMT_FORMAT = 3,
39+
NETLIB = 4,
40+
SNPRINTF = 5,
41+
GRISU2 = 6,
42+
GRISU_EXACT = 7,
43+
SCHUBFACH = 8,
44+
DRAGONBOX = 9,
45+
RYU = 10,
46+
TEJU_JAGUA = 11,
47+
DOUBLE_CONVERSION = 12,
48+
ABSEIL = 13,
49+
STD_TO_CHARS = 14,
50+
COUNT = 15
51+
};
52+
53+
template<typename T>
54+
concept arithmetic_float
55+
= std::is_same_v<T, float> || std::is_same_v<T, double>;
56+
57+
template<arithmetic_float T>
58+
struct BenchArgs {
59+
using Type = T;
60+
61+
BenchArgs(const std::string& name = {}, int (*func)(T, std::span<char>&) = {},
62+
bool used = true, unsigned char testRepeat = 100)
63+
: name(name), func(func), used(used), testRepeat(testRepeat) {}
64+
65+
std::string name{};
66+
int (*func)(T, std::span<char>&){};
67+
bool used{};
68+
unsigned char testRepeat{100};
69+
};
70+
71+
// No dragon4 implementation optimized for float instead of double ?
72+
template<arithmetic_float T>
73+
int dragon4(T d, std::span<char>& buffer) {
74+
using IEEE754Type
75+
= std::conditional_t<std::is_same_v<T, float>, IEEE754f, IEEE754d>;
76+
const IEEE754Type fields = decode_ieee754(d);
77+
78+
uint64_t dm;
79+
int dexp;
80+
dragon4::Dragon4(dm, dexp, fields.mantissa, fields.exponent, true, true);
81+
return to_chars(dm, dexp, fields.sign, buffer.data());
82+
}
83+
84+
// No errol3 implementation optimized for float instead of double ?
85+
template<arithmetic_float T>
86+
int errol3(T d, std::span<char>& buffer) {
87+
#if ERROL_SUPPORTED
88+
errol3_dtoa(d, buffer.data()); // returns the exponent
89+
return std::strlen(buffer.data());
90+
#else
91+
std::abort();
92+
#endif
93+
}
94+
95+
template<arithmetic_float T>
96+
int to_string(T d, std::span<char>& _) {
97+
const std::string s = std::to_string(d);
98+
return s.size();
99+
}
100+
101+
template<arithmetic_float T>
102+
int fmt_format(T d, std::span<char>& _) {
103+
const std::string s = fmt::format("{}", d);
104+
return s.size();
105+
}
106+
107+
// There's no "ftoa", only "dtoa", so not optimized for float.
108+
template<arithmetic_float T>
109+
int netlib(T d, std::span<char>& _) {
110+
#if NETLIB_SUPPORTED
111+
int decpt, sign;
112+
char* rve;
113+
char* result = dtoa(d, 0, 0, &decpt, &sign, &rve);
114+
if (result) {
115+
const int volume = rve - result;
116+
freedtoa(result);
117+
return volume;
118+
} else {
119+
std::abort();
120+
}
121+
#else
122+
std::abort();
123+
#endif
124+
}
125+
126+
template<arithmetic_float T>
127+
int snprintf(T d, std::span<char>& buffer) {
128+
return std::snprintf(buffer.data(), buffer.size(), "%g", d);
129+
}
130+
131+
// grisu2::dtoa_impl::grisu2 can take a template type
132+
// However, grisu2::to_chars is hardcoded for double.
133+
template<arithmetic_float T>
134+
int grisu2(T d, std::span<char>& buffer) {
135+
const char* newp = grisu2::to_chars(buffer.data(), nullptr, d);
136+
return newp - buffer.data();
137+
}
138+
139+
template<arithmetic_float T>
140+
int grisu_exact(T d, std::span<char>& buffer) {
141+
const auto v = jkj::grisu_exact(d);
142+
return to_chars(v.significand, v.exponent, v.is_negative, buffer.data());
143+
}
144+
145+
template<arithmetic_float T>
146+
int schubfach(T d, std::span<char>& buffer) {
147+
if constexpr (std::is_same_v<T, float>)
148+
return schubfach::Ftoa(buffer.data(), d) - buffer.data();
149+
else
150+
return schubfach::Dtoa(buffer.data(), d) - buffer.data();
151+
}
152+
153+
template<arithmetic_float T>
154+
int dragonbox(T d, std::span<char>& buffer) {
155+
const char* end_ptr = jkj::dragonbox::to_chars(d, buffer.data());
156+
return end_ptr - buffer.data();
157+
}
158+
159+
template<arithmetic_float T>
160+
int ryu(T d, std::span<char>& buffer) {
161+
if constexpr (std::is_same_v<T, float>)
162+
return f2s_buffered_n(d, buffer.data());
163+
else
164+
return d2s_buffered_n(d, buffer.data());
165+
}
166+
167+
template<arithmetic_float T>
168+
int teju_jagua(T d, std::span<char>& buffer) {
169+
const auto fields = teju::traits_t<T>::teju(d);
170+
const bool sign = std::signbit(d);
171+
return to_chars(fields.mantissa, fields.exponent, sign, buffer.data());
172+
}
173+
174+
template<arithmetic_float T>
175+
int double_conversion(T d, std::span<char>& buffer) {
176+
const static double_conversion::DoubleToStringConverter converter(
177+
double_conversion::DoubleToStringConverter::NO_FLAGS, "inf", "nan", 'e',
178+
-4, 6, 0, 0);
179+
double_conversion::StringBuilder builder(buffer.data(), buffer.size());
180+
const bool valid = std::is_same_v<T, float>
181+
? converter.ToShortestSingle(d, &builder)
182+
: converter.ToShortest(d, &builder);
183+
if (!valid) {
184+
std::cerr << "problem with " << d << std::endl;
185+
std::abort();
186+
}
187+
return strlen(builder.Finalize());
188+
}
189+
190+
template<arithmetic_float T>
191+
int abseil(T d, std::span<char>& buffer) {
192+
std::string s;
193+
absl::StrAppend(&s, d);
194+
std::copy(s.begin(), s.end(), buffer.begin());
195+
return size(s);
196+
// return absl::SNPrintF(buffer.data(), buffer.size(), "%g", d);
197+
}
198+
199+
template<arithmetic_float T>
200+
int std_to_chars(T d, std::span<char>& buffer) {
201+
#if FROM_CHARS_SUPPORTED
202+
const auto [p, ec]
203+
= std::to_chars(buffer.data(), buffer.data() + buffer.size(), d);
204+
if (ec != std::errc()) {
205+
std::cerr << "problem with " << d << std::endl;
206+
std::abort();
207+
}
208+
return p - buffer.data();
209+
#else
210+
std::abort();
211+
#endif
212+
}
213+
214+
} // namespace Benchmarks
215+
216+
#endif

0 commit comments

Comments
 (0)