Skip to content

Commit ccda507

Browse files
author
Eddie
committed
For review
1 parent 55a20e9 commit ccda507

File tree

6 files changed

+354
-28
lines changed

6 files changed

+354
-28
lines changed

benchmark/atoi-corpus.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,6 @@ struct CorpusStringLength {
133133
X(LIBC_STRLEN, strlen) \
134134
X(ZOO_STRLEN, zoo::c_strLength) \
135135
X(ZOO_NATURAL_STRLEN, zoo::c_strLength_natural) \
136-
X(ZOO_MANUAL_STRLEN, zoo::c_strLength_manualComparison) \
137136
X(GENERIC_GLIBC_STRLEN, STRLEN_old) \
138137
AVX2_STRLEN_CORPUS_X_LIST
139138

benchmark/atoi-corpus.h.back

Lines changed: 248 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,248 @@
1+
#include "atoi.h"
2+
#include "zoo/pp/platform.h"
3+
#include <vector>
4+
#include <string>
5+
#include <cstring>
6+
#include <random>
7+
8+
struct Corpus8DecimalDigits {
9+
std::vector<int> asNumbers_;
10+
std::string characters_;
11+
12+
Corpus8DecimalDigits(std::vector<int> aNs, std::string cs):
13+
asNumbers_(aNs),
14+
characters_(cs)
15+
{}
16+
17+
template<typename G>
18+
static auto makeCorpus(G &generator) {
19+
auto count = 1031; // 1031 is a prime number, this helps to disable in
20+
// practice the branch predictor, the idea is to measure the performance
21+
// of the code under measurement, not how the the unrealistic conditions
22+
// of microbenchmarking help/hurt the code under measurement
23+
std::string allCharacters;
24+
allCharacters.resize(count * 9);
25+
std::vector<int> inputs;
26+
std::uniform_int_distribution<> range(0, 100*1000*1000 - 1);
27+
char *base = allCharacters.data();
28+
for(;;) {
29+
auto input = range(generator);
30+
snprintf(base, 9, "%08d", input);
31+
inputs.push_back(input);
32+
if(--count) { break; }
33+
base += 9;
34+
}
35+
return Corpus8DecimalDigits(inputs, allCharacters);
36+
}
37+
38+
struct Iterator {
39+
Corpus8DecimalDigits *thy;
40+
int *ip;
41+
char *cp;
42+
43+
Iterator &operator++() {
44+
++ip;
45+
cp += 9;
46+
return *this;
47+
}
48+
49+
char *operator*() {
50+
return cp;
51+
}
52+
53+
auto next() noexcept {
54+
++(*this);
55+
return cp != thy->characters_.data() + thy->characters_.size();
56+
}
57+
};
58+
59+
Iterator commence() {
60+
return { this, asNumbers_.data(), characters_.data() };
61+
}
62+
};
63+
64+
#define PARSE8BYTES_CORPUS_X_LIST \
65+
X(Lemire, parse_eight_digits_swar)\
66+
X(Zoo, lemire_as_zoo_swar)\
67+
X(LIBC, atoi)
68+
69+
struct CorpusStringLength {
70+
std::vector<int> skips_;
71+
std::string characters_;
72+
73+
CorpusStringLength(std::vector<int> &&skips, std::string &&cs):
74+
skips_{std::move(skips)}, characters_{std::move(cs)}
75+
{}
76+
77+
template<typename G>
78+
static auto makeCorpus(G &generator) {
79+
auto count = 1031; // see Corpus8DecimalDigits for why 1031
80+
std::vector<int> sizes;
81+
std::string allCharacters;
82+
std::uniform_int_distribution<> strSize(0, 101); // again a prime
83+
std::uniform_int_distribution<> characters(1, 255); // notice 0 excluded
84+
85+
while(count--) {
86+
auto length = strSize(generator);
87+
sizes.push_back(length);
88+
for(auto i = length; i--; ) {
89+
allCharacters.append(1, characters(generator));
90+
}
91+
allCharacters.append(1, '\0');
92+
}
93+
return CorpusStringLength(std::move(sizes), std::move(allCharacters));
94+
}
95+
96+
struct Iterator {
97+
int *skips, *sentinel;
98+
char *cp;
99+
100+
Iterator &operator++() {
101+
cp += *skips++;
102+
return *this;
103+
}
104+
105+
char *operator*() {
106+
return cp;
107+
}
108+
109+
auto next() noexcept {
110+
++(*this);
111+
return sentinel != skips;
112+
}
113+
};
114+
115+
Iterator commence() {
116+
return {
117+
skips_.data(), skips_.data() + skips_.size(), characters_.data()
118+
};
119+
}
120+
};
121+
122+
123+
#if ZOO_CONFIGURED_TO_USE_AVX()
124+
#define AVX2_STRLEN_CORPUS_X_LIST \
125+
X(ZOO_AVX, zoo::avx2_strlen)
126+
#else
127+
#define AVX2_STRLEN_CORPUS_X_LIST /* nothing */
128+
#endif
129+
130+
131+
#define STRLEN_CORPUS_X_LIST \
132+
X(LIBC_STRLEN, strlen) \
133+
X(ZOO_STRLEN, zoo::c_strLength) \
134+
X(ZOO_NATURAL_STRLEN, zoo::c_strLength_natural) \
135+
X(ZOO_MANUAL_STRLEN, zoo::c_strLength_manualComparison) \
136+
X(GENERIC_GLIBC_STRLEN, STRLEN_old) \
137+
AVX2_STRLEN_CORPUS_X_LIST
138+
139+
140+
#define X(Typename, FunctionToCall) \
141+
struct Invoke##Typename { int operator()(const char *p) { return FunctionToCall(p); } };
142+
143+
PARSE8BYTES_CORPUS_X_LIST
144+
STRLEN_CORPUS_X_LIST
145+
146+
#undef X
147+
148+
#include <array>
149+
150+
struct CorpusSpaces {
151+
std::vector<int> counts_;
152+
std::string characters_;
153+
154+
CorpusSpaces(std::vector<int> &&counts, std::string &&cs):
155+
counts_{std::move(counts)}, characters_{std::move(cs)}
156+
{}
157+
158+
template<typename G>
159+
static auto makeCorpus(G &generator) {
160+
auto corpusCount = 1031; // see Corpus8DecimalDigits for why 1031
161+
std::vector<int> counts;
162+
std::string allCharacters;
163+
std::array<char, 6> EscapeCodes = { ' ', '\r', '\f', '\v', '\n', '\t' };
164+
std::uniform_int_distribution<>
165+
countDistribution(0, 101), // again a prime
166+
whitespaceSelection('\r', '\t' + 1),
167+
nonWhiteSpaces('a', 'z' + 1);
168+
169+
while(corpusCount--) {
170+
auto count = countDistribution(generator);
171+
counts.push_back(count + 1); // include the non-whitespace
172+
for(auto i = count; i--; ) {
173+
char whiteSpaceIndex = whitespaceSelection(generator);
174+
char appending =
175+
('\t' + 1 == whiteSpaceIndex) ?
176+
' ' :
177+
EscapeCodes[whiteSpaceIndex];
178+
allCharacters.append(1, appending);
179+
}
180+
char nonWhiteSpace = nonWhiteSpaces(generator);
181+
char nw =
182+
('z' + 1 == nonWhiteSpace) ?
183+
'\0' :
184+
EscapeCodes[nonWhiteSpace];
185+
allCharacters.append(1, nw);
186+
}
187+
allCharacters.append(1, '\0');
188+
return CorpusStringLength(std::move(counts), std::move(allCharacters));
189+
}
190+
191+
struct Iterator {
192+
int *counts, *sentinel;
193+
char *cp;
194+
195+
Iterator &operator++() {
196+
cp += *counts++;
197+
return *this;
198+
}
199+
200+
char *operator*() {
201+
return cp;
202+
}
203+
204+
auto next() noexcept {
205+
++(*this);
206+
return sentinel != counts;
207+
}
208+
};
209+
210+
Iterator commence() {
211+
return {
212+
counts_.data(), counts_.data() + counts_.size(), characters_.data()
213+
};
214+
}
215+
};
216+
217+
// For convenience, define the S<bits per lane>_<uint bits> type aliases
218+
// This is a draft
219+
220+
namespace zoo::swar {
221+
222+
template<int LaneBitCount, int TotalBitCount, bool>
223+
struct SWARIFY {
224+
using type = void;
225+
};
226+
227+
template<int LaneBitCount, int TotalBitCount>
228+
struct SWARIFY<LaneBitCount, TotalBitCount, true> {
229+
using type = SWAR<LaneBitCount, meta::UInteger<TotalBitCount>>;
230+
};
231+
232+
template<int LaneBitCount, int TotalBitCount>
233+
using SWARIFY_t = typename SWARIFY<LaneBitCount, TotalBitCount, LaneBitCount <= TotalBitCount>::type;
234+
static_assert(std::is_same_v<void, SWARIFY_t<16, 8>>);
235+
static_assert(std::is_same_v<swar::SWAR<8, uint32_t>, typename SWARIFY<8, 32, 8 <= 32>::type>);
236+
237+
}
238+
239+
#define LANE_BIT_COUNTS_Y_LIST(Base) Y(4, Base)Y(8, Base)Y(16, Base)Y(32, Base)Y(64, Base)
240+
#define BASE_TYPE_X_LIST X(8)X(16)X(32)X(64)
241+
242+
#define Y(BitCount, Base) using S##BitCount##_##Base = zoo::swar::SWARIFY_t<BitCount, Base>;
243+
#define X(BaseTypeBitCount) LANE_BIT_COUNTS_Y_LIST(BaseTypeBitCount)
244+
245+
BASE_TYPE_X_LIST
246+
247+
#undef X
248+
#undef Y

0 commit comments

Comments
 (0)