Skip to content

Commit 48d28a6

Browse files
committed
concepts for Range and unify code further
1 parent 20f9eb7 commit 48d28a6

File tree

5 files changed

+56
-75
lines changed

5 files changed

+56
-75
lines changed

benchmarks/algorithms.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ struct BenchArgs {
7373
using Type = T;
7474

7575
BenchArgs(const std::string& name = {}, int (*func)(T, std::span<char>&) = {},
76-
bool used = true, unsigned char testRepeat = 100)
76+
bool used = true, size_t testRepeat = 100)
7777
: name(name), func(func), used(used), testRepeat(testRepeat) {}
7878

7979
std::string name{};

benchmarks/benchmark.cpp

Lines changed: 26 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -28,48 +28,10 @@
2828
using Benchmarks::BenchArgs;
2929

3030
template <arithmetic_float T>
31-
void evaluateProperties(const std::vector<T> &lines,
31+
void evaluateProperties(const std::vector<TestCase<T>> &lines,
3232
const std::array<BenchArgs<T>, Benchmarks::COUNT> &args,
3333
const std::vector<std::string> &algo_filter) {
34-
fmt::println("{:20} {:20}", "Algorithm", "Valid round-trip");
35-
36-
for (const auto &algo : args) {
37-
if (!algo.used) {
38-
fmt::println("# skipping {}", algo.name);
39-
continue;
40-
}
41-
if (algo_filtered_out(algo.name, algo_filter)) {
42-
fmt::println("# filtered out {}", algo.name);
43-
continue;
44-
}
45-
46-
char buf1[100], buf2[100];
47-
std::span<char> bufRef(buf1, sizeof(buf1)), bufAlgo(buf2, sizeof(buf2));
48-
int incorrect = 0;
49-
for (const auto d : lines) {
50-
// Reference output
51-
const int vRef = Benchmarks::std_to_chars(d, bufRef);
52-
bufRef[vRef] = '\0';
53-
T dRef;
54-
// We prefer fast_float::from_chars over std::from_chars because it is more
55-
// likely to be available.
56-
auto [ptr, ec] = fast_float::from_chars(bufRef.data(), bufRef.data() + vRef, dRef);
57-
assert(ptr == bufRef.data() + vRef);
58-
assert(ec == std::errc());
59-
assert(d == dRef);
60-
// Tested algorithm output
61-
const int vAlgo = algo.func(d, bufAlgo);
62-
bufAlgo[vAlgo] = '\0';
63-
T dAlgo;
64-
auto [ptrAlgo, ecAlgo] = fast_float::from_chars(bufAlgo.data(), bufAlgo.data() + vAlgo, dAlgo);
65-
assert(ptrAlgo == bufAlgo.data() + vAlgo);
66-
assert(ecAlgo == std::errc());
67-
if ((incorrect += (d != dAlgo)) == 1)
68-
fmt::println("#\t{:20} mismatch: d = {:.17f}, bufRef = {}, bufAlgo = {}, dAlgo = {:.17f}",
69-
algo.name, d, bufRef.data(), bufAlgo.data(), dAlgo);
70-
}
71-
fmt::println("{:20} {:20}", algo.name, incorrect == 0 ? "yes" : "no");
72-
}
34+
evaluate_properties_helper<T>(lines, algo_filter, args);
7335
}
7436

7537
struct diy_float_t {
@@ -81,23 +43,23 @@ struct diy_float_t {
8143
};
8244

8345
template <arithmetic_float T>
84-
void process(const std::vector<T> &lines,
46+
void process(const std::vector<TestCase<T>> &lines,
8547
const std::array<BenchArgs<T>, Benchmarks::COUNT> &args,
8648
const std::vector<std::string> &algo_filter) {
8749
// We have a special algorithm for the string generation:
8850
if (const std::string just_string = "just_string";
8951
!algo_filtered_out(just_string, algo_filter)) {
9052
std::vector<diy_float_t> parsed;
91-
for(auto d : lines) {
92-
auto v = jkj::grisu_exact(d);
53+
for(const auto d : lines) {
54+
const auto v = jkj::grisu_exact(d.value);
9355
parsed.emplace_back(v.significand, v.exponent, v.is_negative);
9456
}
9557
pretty_print(parsed, just_string, [](const std::vector<diy_float_t>& parsed) -> int {
9658
int volume = 0;
9759
char buf[100];
9860
std::span<char> bufspan(buf, sizeof(buf));
9961
for (const auto v : parsed)
100-
volume += to_chars(v.significand, v.exponent, v.is_negative, bufspan.data());
62+
volume += to_chars(v.significand, v.exponent, v.is_negative, bufspan.data());
10163
return volume;
10264
}, 100);
10365
} else {
@@ -114,31 +76,30 @@ void process(const std::vector<T> &lines,
11476
continue;
11577
}
11678

117-
pretty_print(lines, algo.name, [&algo](const std::vector<T> &lines) -> int {
79+
pretty_print(lines, algo.name, [&algo](const std::vector<TestCase<T>> &lines) -> int {
11880
int volume = 0;
11981
char buf[100];
12082
std::span<char> bufspan(buf, sizeof(buf));
12183
for (const auto d : lines)
122-
volume += algo.func(d, bufspan);
84+
volume += algo.func(d.value, bufspan);
12385
return volume;
12486
}, algo.testRepeat);
12587
}
12688
}
12789

128-
template <typename T>
129-
std::vector<T> fileload(const std::string &filename) {
90+
template <arithmetic_float T>
91+
std::vector<TestCase<T>> fileload(const std::string &filename) {
13092
std::ifstream inputfile(filename);
13193
if (!inputfile) {
13294
fmt::println(stderr, "can't open {}", filename);
13395
return {};
13496
}
13597

136-
std::vector<T> lines;
98+
std::vector<TestCase<T>> lines;
13799
lines.reserve(10000); // let us reserve plenty of memory.
138100
for (std::string line; getline(inputfile, line);) {
139101
try {
140-
lines.push_back(std::is_same_v<T, float> ? std::stof(line)
141-
: std::stod(line));
102+
lines.emplace_back(std::is_same_v<T, float> ? std::stof(line) : std::stod(line), line);
142103
} catch (...) {
143104
fmt::println(stderr, "problem with {}\nWe expect floating-point numbers (one per line).", line);
144105
std::abort();
@@ -148,17 +109,17 @@ std::vector<T> fileload(const std::string &filename) {
148109
return lines;
149110
}
150111

151-
template <typename T>
152-
std::vector<T> get_random_numbers(size_t howmany,
153-
const std::string &random_model) {
112+
template <arithmetic_float T>
113+
std::vector<TestCase<T>> get_random_numbers(size_t howmany,
114+
const std::string &random_model) {
154115
fmt::println("# parsing random numbers");
155-
std::vector<T> lines;
116+
std::vector<TestCase<T>> lines;
156117
auto g = get_generator_by_name<T>(random_model);
157118
fmt::println("model: {}\nvolume: {} floats", g->describe(), howmany);
158119
lines.reserve(howmany); // let us reserve plenty of memory.
159120
for (size_t i = 0; i < howmany; i++) {
160121
const T line = g->new_float();
161-
lines.push_back(line);
122+
lines.emplace_back(line, std::nullopt);
162123
}
163124
return lines;
164125
}
@@ -183,7 +144,7 @@ int main(int argc, char **argv) {
183144
("e,errol", "Enable errol3 (current impl. returns invalid values, e.g., for 0).",
184145
cxxopts::value<bool>()->default_value("false"))
185146
("a,algo-filter", "Filter algorithms by name substring: you can use multiple filters separated by commas.",
186-
cxxopts::value<std::vector<std::string>>()->default_value(""))
147+
cxxopts::value<std::vector<std::string>>())
187148
("r,repeat", "Force a number of repetitions.",
188149
cxxopts::value<size_t>()->default_value("0"))
189150
("h,help", "Print usage.");
@@ -195,10 +156,13 @@ int main(int argc, char **argv) {
195156
}
196157
const size_t repeat = result["repeat"].as<size_t>();
197158
const bool single = result["single"].as<bool>();
198-
std::vector<std::string> filter = result["algo-filter"].as<std::vector<std::string>>();
159+
const auto filter = result.count("algo-filter")
160+
? result["algo-filter"].as<std::vector<std::string>>()
161+
: std::vector<std::string>{};
199162
fmt::println("number type: binary{}", (single ? "32 (float)" : "64 (double)"));
200163

201-
std::variant<std::vector<float>, std::vector<double>> numbers;
164+
std::variant<std::vector<TestCase<float>>,
165+
std::vector<TestCase<double>>> numbers;
202166
const auto filename = result["file"].as<std::string>();
203167
if (filename.empty()) {
204168
const auto volume = result["volume"].as<size_t>();
@@ -207,7 +171,7 @@ int main(int argc, char **argv) {
207171
numbers = get_random_numbers<float>(volume, model);
208172
else
209173
numbers = get_random_numbers<double>(volume, model);
210-
fmt::println("# You can also provide a filename (with the -f flag):"
174+
fmt::println("# You can also provide a filename (with the -f flag): "
211175
"it should contain one string per line corresponding to a number");
212176
}
213177
else {
@@ -234,8 +198,8 @@ int main(int argc, char **argv) {
234198
}
235199

236200
const bool test = result["test"].as<bool>();
237-
std::visit([test,&filter](const auto &lines, const auto &args) {
238-
using T1 = typename std::decay_t<decltype(lines)>::value_type;
201+
std::visit([test, &filter](const auto &lines, const auto &args) {
202+
using T1 = typename std::decay_t<decltype(lines)>::value_type::Type;
239203
using T2 = typename std::decay_t<decltype(args)>::value_type::Type;
240204
if constexpr (std::is_same_v<T1, T2>) {
241205
if (test)

benchmarks/benchutil.h

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,14 @@
66
#include <atomic>
77
#include <cfloat>
88
#include <cstdio>
9+
#include <ranges>
910

1011
#include "algorithms.h"
1112
#include "counters/event_counter.h"
1213

13-
event_collector collector;
14+
using Benchmarks::BenchArgs;
1415

15-
template <arithmetic_float T>
16-
struct TestCase {
17-
T value;
18-
std::optional<std::string> str_value;
19-
};
16+
event_collector collector;
2017

2118
bool algo_filtered_out(const std::string &algo_name,
2219
const std::vector<std::string> &algo_filter) {
@@ -30,12 +27,32 @@ bool algo_filtered_out(const std::string &algo_name,
3027
return true;
3128
}
3229

33-
template<arithmetic_float T, std::ranges::input_range Range>
34-
void evaluate_properties_helper(bool errol,
30+
template <arithmetic_float T>
31+
struct TestCase {
32+
using Type = T;
33+
T value;
34+
std::optional<std::string> str_value;
35+
};
36+
37+
template<typename E, typename T>
38+
concept TestCaseConcept = arithmetic_float<T> && requires(E e) {
39+
{ e.value } -> std::convertible_to<T>;
40+
{ e.str_value } -> std::convertible_to<std::optional<std::string>>;
41+
};
42+
43+
template<typename R, typename T>
44+
concept TestCaseRange
45+
= std::ranges::input_range<R>
46+
&& TestCaseConcept<std::ranges::range_reference_t<R>, T>;
47+
48+
template<arithmetic_float T, typename Range> requires TestCaseRange<Range, T>
49+
void evaluate_properties_helper(Range&& cases,
3550
const std::vector<std::string> &algo_filter,
36-
Range&& cases) {
51+
std::variant<std::array<BenchArgs<T>, Benchmarks::COUNT>, bool> argsOpt) {
3752
fmt::println("{:20} {:20}", "Algorithm", "Valid shortest serialization");
38-
const auto args = Benchmarks::initArgs<T>(errol);
53+
const auto args = std::holds_alternative<bool>(argsOpt)
54+
? Benchmarks::initArgs<T>(std::get<bool>(argsOpt))
55+
: std::get<std::array<BenchArgs<T>, Benchmarks::COUNT>>(argsOpt);
3956

4057
// Get number of cases for progress display
4158
uint64_t total = 0;

benchmarks/exhaustivefloat32.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ void run_exhaustive32(bool errol, const std::vector<std::string>& algo_filter =
2020
return TestCase<float>{ d, std::nullopt };
2121
});
2222

23-
evaluate_properties_helper<float>(errol, algo_filter, floats_view);
23+
evaluate_properties_helper<float>(floats_view, algo_filter, errol);
2424
}
2525

2626
cxxopts::Options

benchmarks/thoroughfloat64.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ void run_file_test(const std::string& filename, bool errol, const std::vector<st
3838
return;
3939
}
4040

41-
evaluate_properties_helper<double>(errol, algo_filter, test_values);
41+
evaluate_properties_helper<double>(test_values, algo_filter, errol);
4242
}
4343

4444
cxxopts::Options

0 commit comments

Comments
 (0)