88 */
99
1010#include " algorithms.h"
11+ #include < vector>
1112#define IEEE_8087
1213#include " benchutil.h"
1314#include " cxxopts.hpp"
2223#include < string>
2324#include < variant>
2425#include < fast_float/fast_float.h>
26+ #include < fmt/core.h>
2527
2628using Benchmarks::arithmetic_float;
2729using Benchmarks::BenchArgs;
2830
31+ bool is_matched (const std::string &str, const std::span<std::string> filter) {
32+ if (filter.empty ()) {
33+ return true ;
34+ }
35+ for (const auto &f : filter) {
36+ if (str.find (f) != std::string::npos) {
37+ return true ;
38+ }
39+ }
40+ return false ;
41+ }
42+
2943template <arithmetic_float T>
3044void evaluateProperties (const std::vector<T> &lines,
31- const std::array<BenchArgs<T>, Benchmarks::COUNT> &args, const std::string& filter = " " ) {
45+ const std::array<BenchArgs<T>, Benchmarks::COUNT> &args, const std::span<std:: string> filter = {} ) {
3246 constexpr auto precision = std::numeric_limits<T>::digits10;
3347 fmt::println (" {:20} {:20}" , " Algorithm" , " Valid round-trip" );
3448
3549 for (const auto &algo : args) {
3650 if (!algo.used ) {
37- std::cout << " # skipping " << algo.name << std::endl ;
51+ fmt::println ( " # skipping {} " , algo.name ) ;
3852 continue ;
3953 }
4054 // Apply filter if provided
41- if (!filter. empty () && std::string (filter). find ( algo.name ) == std::string::npos ) {
42- std::cout << " # filtered out " << algo.name << std::endl ;
55+ if (!is_matched ( algo.name , filter) ) {
56+ fmt::println ( " # filtered out {} " , algo.name ) ;
4357 continue ;
4458 }
4559 char buf1[100 ], buf2[100 ];
@@ -71,17 +85,42 @@ void evaluateProperties(const std::vector<T> &lines,
7185 }
7286}
7387
88+ struct diy_float_t {
89+ uint64_t significand;
90+ int exponent;
91+ bool is_negative;
92+ };
93+
7494template <arithmetic_float T>
7595void process (const std::vector<T> &lines,
76- const std::array<BenchArgs<T>, Benchmarks::COUNT> &args, const std::string& filter = " " ) {
96+ const std::array<BenchArgs<T>, Benchmarks::COUNT> &args, const std::span<std::string> filter = {}) {
97+ // We have a special algorithm for the string generation:
98+ std::string just_string = " just_string" ;
99+ if (is_matched (just_string, filter)) {
100+ std::vector<diy_float_t > parsed;
101+ for (auto d : lines) {
102+ auto v = jkj::grisu_exact (d);
103+ parsed.emplace_back (v.significand , v.exponent , v.is_negative );
104+ }
105+ pretty_print (parsed, just_string, [](const std::vector<diy_float_t >& parsed) -> int {
106+ int volume = 0 ;
107+ char buf[100 ];
108+ std::span<char > bufspan (buf, sizeof (buf));
109+ for (const auto v : parsed)
110+ volume += to_chars (v.significand , v.exponent , v.is_negative , bufspan.data ());
111+ return volume;
112+ }, 100 );
113+ } else {
114+ fmt::println (" # skipping {}" , just_string);
115+ }
77116 for (const auto &algo : args) {
78117 if (!algo.used ) {
79- std::cout << " # skipping " << algo.name << std::endl ;
118+ fmt::println ( " # skipping {} " , algo.name ) ;
80119 continue ;
81120 }
82121 // Apply filter if provided
83- if (!filter. empty () && std::string (filter). find ( algo.name ) == std::string::npos ) {
84- std::cout << " # filtered out " << algo.name << std::endl ;
122+ if (!is_matched ( algo.name , filter) ) {
123+ fmt::println ( " # filtered out {} " , algo.name ) ;
85124 continue ;
86125 }
87126 pretty_print (lines, algo.name , [&algo](const std::vector<T> &lines) -> int {
@@ -93,13 +132,14 @@ void process(const std::vector<T> &lines,
93132 return volume;
94133 }, algo.testRepeat );
95134 }
135+
96136}
97137
98138template <typename T>
99139std::vector<T> fileload (const std::string &filename) {
100140 std::ifstream inputfile (filename);
101141 if (!inputfile) {
102- std::cerr << " can't open " << filename << std::endl ;
142+ fmt::print (stderr, " can't open {} \n " , filename) ;
103143 return {};
104144 }
105145
@@ -110,24 +150,21 @@ std::vector<T> fileload(const std::string &filename) {
110150 lines.push_back (std::is_same_v<T, float > ? std::stof (line)
111151 : std::stod (line));
112152 } catch (...) {
113- std::cerr << " problem with " << line << " \n "
114- << " We expect floating-point numbers (one per line)."
115- << std::endl;
153+ fmt::print (stderr, " problem with {}\n We expect floating-point numbers (one per line).\n " , line);
116154 std::abort ();
117155 }
118156 }
119- std::cout << " # read " << lines.size () << " lines " << std::endl ;
157+ fmt::println ( " # read {} lines " , lines.size ()) ;
120158 return lines;
121159}
122160
123161template <typename T>
124162std::vector<T> get_random_numbers (size_t howmany,
125163 const std::string &random_model) {
126- std::cout << " # parsing random numbers" << std::endl ;
164+ fmt::println ( " # parsing random numbers" ) ;
127165 std::vector<T> lines;
128166 auto g = get_generator_by_name<T>(random_model);
129- std::cout << " model: " << g->describe () << " \n "
130- << " volume: " << howmany << " floats" << std::endl;
167+ fmt::print (" model: {}\n volume: {} floats\n " , g->describe (), howmany);
131168 lines.reserve (howmany); // let us reserve plenty of memory.
132169 for (size_t i = 0 ; i < howmany; i++) {
133170 const T line = g->new_float ();
@@ -155,20 +192,21 @@ int main(int argc, char **argv) {
155192 cxxopts::value<bool >()->default_value (" false" ))
156193 (" e,errol" , " Enable errol3 (current impl. returns invalid values, e.g., for 0)." ,
157194 cxxopts::value<bool >()->default_value (" false" ))
158- (" a,algo-filter" , " Filter algorithms by name substring." ,
159- cxxopts::value<std::string>()->default_value (" " ))
195+ (" a,algo-filter" , " Filter algorithms by name substring: you can use multiple filters separated by commas." ,
196+ cxxopts::value<std::vector<std::string>>()->default_value (" " ))
197+ (" r,repeat" , " Force a number of repetitions." ,
198+ cxxopts::value<size_t >()->default_value (" 0" ))
160199 (" h,help" , " Print usage." );
161200 const auto result = options.parse (argc, argv);
162201
163202 if (result[" help" ].as <bool >()) {
164- std::cout << options.help () << std::endl ;
203+ fmt::print ( " {} \n " , options.help ()) ;
165204 return EXIT_SUCCESS;
166205 }
167-
206+ const size_t repeat = result[ " repeat " ]. as < size_t >();
168207 const bool single = result[" single" ].as <bool >();
169- const std::string filter = result[" algo-filter" ].as <std::string>();
170- std::cout << " number type: binary"
171- << (single ? " 32 (float)" : " 64 (double)" ) << std::endl;
208+ std::vector<std::string> filter = result[" algo-filter" ].as <std::vector<std::string>>();
209+ fmt::println (" number type: binary{}" , (single ? " 32 (float)" : " 64 (double)" ));
172210
173211 std::variant<std::vector<float >, std::vector<double >> numbers;
174212 const auto filename = result[" file" ].as <std::string>();
@@ -179,9 +217,7 @@ int main(int argc, char **argv) {
179217 numbers = get_random_numbers<float >(volume, model);
180218 else
181219 numbers = get_random_numbers<double >(volume, model);
182- std::cout << " # You can also provide a filename (with the -f flag): "
183- " it should contain one string per line corresponding to a number"
184- << std::endl;
220+ fmt::println (" # You can also provide a filename (with the -f flag): it should contain one string per line corresponding to a number" );
185221 }
186222 else {
187223 if (single)
@@ -198,6 +234,14 @@ int main(int argc, char **argv) {
198234 else
199235 algorithms = Benchmarks::initArgs<double >(errol);
200236
237+ if (repeat > 0 ) {
238+ fmt::println (" # forcing repeat count to {}" , repeat);
239+ std::visit ([repeat](auto &args) {
240+ for (auto &arg : args)
241+ arg.testRepeat = repeat;
242+ }, algorithms);
243+ }
244+
201245 const bool test = result[" test" ].as <bool >();
202246 std::visit ([test,&filter](const auto &lines, const auto &args) {
203247 using T1 = typename std::decay_t <decltype (lines)>::value_type;
@@ -210,7 +254,20 @@ int main(int argc, char **argv) {
210254 }
211255 }, numbers, algorithms);
212256 } catch (const std::exception &e) {
213- std::cout << " error parsing options: " << e.what () << std::endl;
257+ fmt::println (" Error parsing options: {}" , e.what ());
258+ fmt::println (" \n USAGE GUIDE:" );
259+ fmt::println (" ./benchmark [OPTIONS]" );
260+ fmt::println (" \n COMMAND SUMMARY:" );
261+ fmt::println (" The benchmark tool evaluates the performance of different floating-point to string" );
262+ fmt::println (" conversion algorithms. It can use either synthetic data or a file containing" );
263+ fmt::println (" floating-point numbers (one per line)." );
264+ fmt::println (" \n EXAMPLES:" );
265+ fmt::println (" ./benchmark --single # Run benchmark with single precision (float)" );
266+ fmt::println (" ./benchmark --file=data/canada.txt # Run benchmark using numbers from a file" );
267+ fmt::println (" ./benchmark --test # Test correctness instead of performance" );
268+ fmt::println (" ./benchmark --volume=1000 --model=uniform # Generate 1000 uniform random numbers" );
269+ fmt::println (" ./benchmark --algo-filter=ryu,grisu # Only test algorithms containing 'ryu' or 'grisu'" );
270+ fmt::println (" \n For full options list, run: ./benchmark --help" );
214271 return EXIT_FAILURE;
215272 }
216273}
0 commit comments