Skip to content

Commit e7de3ef

Browse files
committed
Some minor fixes/simplification
1 parent 1c7c126 commit e7de3ef

File tree

2 files changed

+77
-17
lines changed

2 files changed

+77
-17
lines changed

benchmarks/algorithms.h

Lines changed: 73 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
#define YY_DOUBLE_SUPPORTED 0
4545
#endif
4646

47+
4748
template<arithmetic_float T>
4849
struct BenchArgs {
4950
using Type = T;
@@ -57,14 +58,7 @@ struct BenchArgs {
5758
bool used{};
5859
size_t testRepeat{100};
5960

60-
static void initFixedSize(size_t size) {
61-
fixedSize = size;
62-
snprintf(formatStr, sizeof(formatStr), "%%.%zug", fixedSize);
63-
formatStrStr = fmt::format("{{:.{}g}}", fixedSize);
64-
}
6561
static inline size_t fixedSize;
66-
static inline char formatStr[10];
67-
static inline std::string formatStrStr;
6862
};
6963

7064
namespace BenchmarkShortest {
@@ -321,21 +315,85 @@ int abseil(T d, std::span<char>& buffer) {
321315
// absl::StrAppend(&s, d);
322316
// std::copy(s.begin(), s.end(), buffer.begin());
323317
// return size(s);
324-
return absl::SNPrintF(buffer.data(), buffer.size(),
325-
BenchArgs<T>::formatStr, d);
318+
//
319+
// The switch should be very cheap if fixedSize is very predictable.
320+
// Essentially just a predictable jump.
321+
switch (BenchArgs<T>::fixedSize) {
322+
case 0: return absl::SNPrintF(buffer.data(), buffer.size(), "%.0g", d);
323+
case 1: return absl::SNPrintF(buffer.data(), buffer.size(), "%.1g", d);
324+
case 2: return absl::SNPrintF(buffer.data(), buffer.size(), "%.2g", d);
325+
case 3: return absl::SNPrintF(buffer.data(), buffer.size(), "%.3g", d);
326+
case 4: return absl::SNPrintF(buffer.data(), buffer.size(), "%.4g", d);
327+
case 5: return absl::SNPrintF(buffer.data(), buffer.size(), "%.5g", d);
328+
case 6: return absl::SNPrintF(buffer.data(), buffer.size(), "%.6g", d);
329+
case 7: return absl::SNPrintF(buffer.data(), buffer.size(), "%.7g", d);
330+
case 8: return absl::SNPrintF(buffer.data(), buffer.size(), "%.8g", d);
331+
case 9: return absl::SNPrintF(buffer.data(), buffer.size(), "%.9g", d);
332+
case 10: return absl::SNPrintF(buffer.data(), buffer.size(), "%.10g", d);
333+
case 11: return absl::SNPrintF(buffer.data(), buffer.size(), "%.11g", d);
334+
case 12: return absl::SNPrintF(buffer.data(), buffer.size(), "%.12g", d);
335+
case 13: return absl::SNPrintF(buffer.data(), buffer.size(), "%.13g", d);
336+
case 14: return absl::SNPrintF(buffer.data(), buffer.size(), "%.14g", d);
337+
case 15: return absl::SNPrintF(buffer.data(), buffer.size(), "%.15g", d);
338+
case 16: return absl::SNPrintF(buffer.data(), buffer.size(), "%.16g", d);
339+
case 17: return absl::SNPrintF(buffer.data(), buffer.size(), "%.17g", d);
340+
default:
341+
return absl::SNPrintF(buffer.data(), buffer.size(), "%.17g", d);
342+
}
326343
}
327344

328345
template<arithmetic_float T>
329346
int snprintf(T d, std::span<char>& buffer) {
330-
return std::snprintf(buffer.data(), buffer.size(),
331-
BenchArgs<T>::formatStr, d);
347+
// The switch should be very cheap if fixedSize is very predictable.
348+
// Essentially just a predictable jump.
349+
switch (BenchArgs<T>::fixedSize) {
350+
case 0: return std::snprintf(buffer.data(), buffer.size(), "%.0g", d);
351+
case 1: return std::snprintf(buffer.data(), buffer.size(), "%.1g", d);
352+
case 2: return std::snprintf(buffer.data(), buffer.size(), "%.2g", d);
353+
case 3: return std::snprintf(buffer.data(), buffer.size(), "%.3g", d);
354+
case 4: return std::snprintf(buffer.data(), buffer.size(), "%.4g", d);
355+
case 5: return std::snprintf(buffer.data(), buffer.size(), "%.5g", d);
356+
case 6: return std::snprintf(buffer.data(), buffer.size(), "%.6g", d);
357+
case 7: return std::snprintf(buffer.data(), buffer.size(), "%.7g", d);
358+
case 8: return std::snprintf(buffer.data(), buffer.size(), "%.8g", d);
359+
case 9: return std::snprintf(buffer.data(), buffer.size(), "%.9g", d);
360+
case 10: return std::snprintf(buffer.data(), buffer.size(), "%.10g", d);
361+
case 11: return std::snprintf(buffer.data(), buffer.size(), "%.11g", d);
362+
case 12: return std::snprintf(buffer.data(), buffer.size(), "%.12g", d);
363+
case 13: return std::snprintf(buffer.data(), buffer.size(), "%.13g", d);
364+
case 14: return std::snprintf(buffer.data(), buffer.size(), "%.14g", d);
365+
case 15: return std::snprintf(buffer.data(), buffer.size(), "%.15g", d);
366+
case 16: return std::snprintf(buffer.data(), buffer.size(), "%.16g", d);
367+
case 17: return std::snprintf(buffer.data(), buffer.size(), "%.17g", d);
368+
default:
369+
return std::snprintf(buffer.data(), buffer.size(), "%.17g", d);
370+
}
332371
}
333372

334373
template<arithmetic_float T>
335374
int fmt_format(T d, std::span<char>& buffer) {
336-
const auto it = fmt::format_to(buffer.begin(),
337-
fmt::runtime(BenchArgs<T>::formatStrStr), d);
338-
return std::distance(buffer.begin(), it);
375+
switch(BenchArgs<T>::fixedSize) {
376+
case 0: return fmt::format_to(buffer.data(), "{}", d) - buffer.data();
377+
case 1: return fmt::format_to(buffer.data(), "{:.1g}", d) - buffer.data();
378+
case 2: return fmt::format_to(buffer.data(), "{:.2g}", d) - buffer.data();
379+
case 3: return fmt::format_to(buffer.data(), "{:.3g}", d) - buffer.data();
380+
case 4: return fmt::format_to(buffer.data(), "{:.4g}", d) - buffer.data();
381+
case 5: return fmt::format_to(buffer.data(), "{:.5g}", d) - buffer.data();
382+
case 6: return fmt::format_to(buffer.data(), "{:.6g}", d) - buffer.data();
383+
case 7: return fmt::format_to(buffer.data(), "{:.7g}", d) - buffer.data();
384+
case 8: return fmt::format_to(buffer.data(), "{:.8g}", d) - buffer.data();
385+
case 9: return fmt::format_to(buffer.data(), "{:.9g}", d) - buffer.data();
386+
case 10: return fmt::format_to(buffer.data(), "{:.10g}", d) - buffer.data();
387+
case 11: return fmt::format_to(buffer.data(), "{:.11g}", d) - buffer.data();
388+
case 12: return fmt::format_to(buffer.data(), "{:.12g}", d) - buffer.data();
389+
case 13: return fmt::format_to(buffer.data(), "{:.13g}", d) - buffer.data();
390+
case 14: return fmt::format_to(buffer.data(), "{:.14g}", d) - buffer.data();
391+
case 15: return fmt::format_to(buffer.data(), "{:.15g}", d) - buffer.data();
392+
case 16: return fmt::format_to(buffer.data(), "{:.16g}", d) - buffer.data();
393+
case 17: return fmt::format_to(buffer.data(), "{:.17g}", d) - buffer.data();
394+
default:
395+
return fmt::format_to(buffer.data(), "{:.17g}", d) - buffer.data();
396+
}
339397
}
340398

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

412470
namespace f = BenchmarkFixedSize;
413471
args.emplace_back("dragon4" , wrap(f::dragon4<T>) , true , 10);

benchmarks/floatutils.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include <string>
66
#include <sstream>
77
#include <optional>
8+
#include <fast_float/fast_float.h>
89

910
template<typename T>
1011
concept arithmetic_float
@@ -47,8 +48,9 @@ std::optional<T> parse_float(std::string_view sv) {
4748
T result;
4849
const char* begin = sv.data();
4950
const char* end = sv.data() + sv.size();
50-
51-
auto [ptr, ec] = std::from_chars(begin, end, result);
51+
// Using fastfloat for parsing and not std::from_chars, since
52+
// fastfloat is always available and supports both float and double.
53+
auto [ptr, ec] = fast_float::from_chars(begin, end, result);
5254

5355
// Check if parsing succeeded and consumed the entire string
5456
if (ec == std::errc{} && ptr == end) {

0 commit comments

Comments
 (0)