4444#define YY_DOUBLE_SUPPORTED 0
4545#endif
4646
47+
4748template <arithmetic_float T>
4849struct 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
7064namespace 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
328345template <arithmetic_float T>
329346int 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
334373template <arithmetic_float T>
335374int 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
341399template <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 );
0 commit comments