Skip to content

Commit 2ee0310

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

File tree

4 files changed

+305
-265
lines changed

4 files changed

+305
-265
lines changed

benchmarks/CMakeLists.txt

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,18 +15,27 @@ int main(void) {
1515
return 0;
1616
}
1717
" from_chars_ok)
18-
19-
if (from_chars_ok)
20-
target_compile_definitions(benchmark PUBLIC FROM_CHARS_SUPPORTED=1)
21-
endif()
18+
target_compile_definitions(benchmark PUBLIC FROM_CHARS_SUPPORTED=$<IF:${from_chars_ok},1,0>)
2219

2320
if (NOT WIN32)
2421
target_link_libraries(benchmark PUBLIC netlib)
2522
target_compile_definitions(benchmark PUBLIC NETLIB_SUPPORTED=1)
23+
else()
24+
target_compile_definitions(benchmark PUBLIC NETLIB_SUPPORTED=0)
2625
endif()
2726

2827
if (NOT CYGWIN)
29-
target_link_libraries(benchmark PUBLIC absl::strings)
28+
target_link_libraries(benchmark PUBLIC absl::strings absl::str_format)
29+
target_compile_definitions(benchmark PUBLIC ABSEIL_SUPPORTED=1)
30+
else()
31+
target_compile_definitions(benchmark PUBLIC ABSEIL_SUPPORTED=0)
32+
endif()
33+
34+
if(TARGET errol)
35+
target_link_libraries(benchmark PUBLIC errol)
36+
target_compile_definitions(benchmark PUBLIC ERROL_SUPPORTED=1)
37+
else()
38+
target_compile_definitions(benchmark PUBLIC ERROL_SUPPORTED=0)
3039
endif()
3140

3241
target_link_libraries(benchmark PUBLIC fmt)
@@ -41,7 +50,4 @@ if(teju_has_float128)
4150
endif()
4251
target_link_libraries(benchmark PUBLIC dragonbox::dragonbox_to_chars)
4352
target_link_libraries(benchmark PUBLIC dragon_schubfach_lib)
44-
if(TARGET errol)
45-
target_link_libraries(benchmark PUBLIC errol)
46-
endif()
4753
target_include_directories(benchmark PUBLIC ${grisu-exact_SOURCE_DIR})

benchmarks/algorithms.h

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

0 commit comments

Comments
 (0)