Skip to content

Commit 11575c5

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

File tree

4 files changed

+294
-261
lines changed

4 files changed

+294
-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: 219 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
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>& _) {
100+
const std::string s = std::to_string(d);
101+
return s.size();
102+
}
103+
104+
template<arithmetic_float T>
105+
int fmt_format(T d, std::span<char>& _) {
106+
const std::string s = fmt::format("{}", d);
107+
return s.size();
108+
}
109+
110+
// There's no "ftoa", only "dtoa", so not optimized for float.
111+
template<arithmetic_float T>
112+
int netlib(T d, std::span<char>& _) {
113+
#if NETLIB_SUPPORTED
114+
int decpt, sign;
115+
char* rve;
116+
char* result = dtoa(d, 0, 0, &decpt, &sign, &rve);
117+
if (result) {
118+
const int volume = rve - result;
119+
freedtoa(result);
120+
return volume;
121+
} else {
122+
std::abort();
123+
}
124+
#else
125+
std::abort();
126+
#endif
127+
}
128+
129+
template<arithmetic_float T>
130+
int snprintf(T d, std::span<char>& buffer) {
131+
return std::snprintf(buffer.data(), buffer.size(), "%g", d);
132+
}
133+
134+
// grisu2::dtoa_impl::grisu2 can take a template type
135+
// However, grisu2::to_chars is hardcoded for double.
136+
template<arithmetic_float T>
137+
int grisu2(T d, std::span<char>& buffer) {
138+
const char* newp = grisu2::to_chars(buffer.data(), nullptr, d);
139+
return newp - buffer.data();
140+
}
141+
142+
template<arithmetic_float T>
143+
int grisu_exact(T d, std::span<char>& buffer) {
144+
const auto v = jkj::grisu_exact(d);
145+
return to_chars(v.significand, v.exponent, v.is_negative, buffer.data());
146+
}
147+
148+
template<arithmetic_float T>
149+
int schubfach(T d, std::span<char>& buffer) {
150+
if constexpr (std::is_same_v<T, float>)
151+
return schubfach::Ftoa(buffer.data(), d) - buffer.data();
152+
else
153+
return schubfach::Dtoa(buffer.data(), d) - buffer.data();
154+
}
155+
156+
template<arithmetic_float T>
157+
int dragonbox(T d, std::span<char>& buffer) {
158+
const char* end_ptr = jkj::dragonbox::to_chars(d, buffer.data());
159+
return end_ptr - buffer.data();
160+
}
161+
162+
template<arithmetic_float T>
163+
int ryu(T d, std::span<char>& buffer) {
164+
if constexpr (std::is_same_v<T, float>)
165+
return f2s_buffered_n(d, buffer.data());
166+
else
167+
return d2s_buffered_n(d, buffer.data());
168+
}
169+
170+
template<arithmetic_float T>
171+
int teju_jagua(T d, std::span<char>& buffer) {
172+
const auto fields = teju::traits_t<T>::teju(d);
173+
const bool sign = std::signbit(d);
174+
return to_chars(fields.mantissa, fields.exponent, sign, buffer.data());
175+
}
176+
177+
template<arithmetic_float T>
178+
int double_conversion(T d, std::span<char>& buffer) {
179+
const static double_conversion::DoubleToStringConverter converter(
180+
double_conversion::DoubleToStringConverter::NO_FLAGS, "inf", "nan", 'e',
181+
-4, 6, 0, 0);
182+
double_conversion::StringBuilder builder(buffer.data(), buffer.size());
183+
const bool valid = std::is_same_v<T, float>
184+
? converter.ToShortestSingle(d, &builder)
185+
: converter.ToShortest(d, &builder);
186+
if (!valid) {
187+
std::cerr << "problem with " << d << std::endl;
188+
std::abort();
189+
}
190+
return strlen(builder.Finalize());
191+
}
192+
193+
template<arithmetic_float T>
194+
int abseil(T d, std::span<char>& buffer) {
195+
std::string s;
196+
absl::StrAppend(&s, d);
197+
std::copy(s.begin(), s.end(), buffer.begin());
198+
return size(s);
199+
// return absl::SNPrintF(buffer.data(), buffer.size(), "%g", d);
200+
}
201+
202+
template<arithmetic_float T>
203+
int std_to_chars(T d, std::span<char>& buffer) {
204+
#if FROM_CHARS_SUPPORTED
205+
const auto [p, ec]
206+
= std::to_chars(buffer.data(), buffer.data() + buffer.size(), d);
207+
if (ec != std::errc()) {
208+
std::cerr << "problem with " << d << std::endl;
209+
std::abort();
210+
}
211+
return p - buffer.data();
212+
#else
213+
std::abort();
214+
#endif
215+
}
216+
217+
} // namespace Benchmarks
218+
219+
#endif

0 commit comments

Comments
 (0)