Skip to content

Commit 7a8d1d6

Browse files
author
Eddie
committed
Draft of strlen
1 parent fd782f3 commit 7a8d1d6

File tree

5 files changed

+124
-0
lines changed

5 files changed

+124
-0
lines changed

benchmark/atoi-corpus.h

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
1+
#include "atoi.h"
2+
13
#include <vector>
24
#include <string>
5+
#include <cstring>
36
#include <random>
47

58
struct Corpus8DecimalDigits {
@@ -63,8 +66,67 @@ struct Corpus8DecimalDigits {
6366
X(Zoo, lemire_as_zoo_swar)\
6467
X(LIBC, atoi)
6568

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+
for(;;) {
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+
#define STRLEN_CORPUS_X_LIST \
123+
X(LIBC_STRLEN, strlen) \
124+
X(ZOO_BEST_STRLEN, zoo::c_strLength) \
125+
X(ZOO_NATURAL_STRLEN, zoo::c_strLength_MoreNaturalButSlightlyWorse)
126+
66127
#define X(Typename, FunctionToCall) \
67128
struct Invoke##Typename { int operator()(const char *p) { return FunctionToCall(p); } };
68129

69130
PARSE8BYTES_CORPUS_X_LIST
131+
STRLEN_CORPUS_X_LIST
70132
#undef X

benchmark/atoi.cpp

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,3 +49,45 @@ auto lemire_as_zoo_swar(const char *chars) {
4949
auto by10001base2to32 = byteQuads.multiply(1 + (10000ull << 32));
5050
return uint32_t(by10001base2to32.value() >> 32);
5151
}
52+
53+
namespace zoo {
54+
55+
std::size_t c_strLength(const char *s) {
56+
std::size_t rv = 0;
57+
using S = swar::SWAR<8, std::size_t>;
58+
S bytes;
59+
constexpr auto MSBs = S{S::MostSignificantBit};
60+
for(auto base = s;; base += 8) {
61+
memcpy(&bytes.m_v, base, 8);
62+
// A null byte is detected in two steps:
63+
// 1. it has the MSB off, and
64+
// the least significant bits are also off.
65+
// The swar library allows the detection of lsbs off
66+
// By comparing greater equal to 0,
67+
// 0 can only be greater-equal to a byte with LSBs 0
68+
auto haveMSB_cleared = bytes ^ MSBs;
69+
auto lsbNulls = zoo::swar::greaterEqual_MSB_off(S{0}, bytes);
70+
auto nulls = swar::asBooleanSWAR(haveMSB_cleared & lsbNulls);
71+
if(nulls) {
72+
auto firstNullIndex = nulls.lsbIndex();
73+
return firstNullIndex + (base - s);
74+
}
75+
}
76+
}
77+
78+
std::size_t c_strLength_MoreNaturalButSlightlyWorse(const char *s) {
79+
std::size_t rv = 0;
80+
using S = swar::SWAR<8, std::size_t>;
81+
S bytes;
82+
constexpr auto MSBs = S{S::MostSignificantBit};
83+
for(auto base = s;; base += 8) {
84+
memcpy(&bytes.m_v, base, 8);
85+
auto nulls = zoo::swar::equals(bytes, S{0});
86+
if(nulls) { // there is a null!
87+
auto firstNullIndex = nulls.lsbIndex();
88+
return firstNullIndex + (base - s);
89+
}
90+
}
91+
}
92+
93+
}

benchmark/atoi.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,12 @@
11
#include "stdint.h"
2+
#include <cstdlib>
23

34
uint32_t parse_eight_digits_swar(const char *chars);
45
uint32_t lemire_as_zoo_swar(const char *chars);
6+
7+
namespace zoo {
8+
9+
std::size_t c_strLength(const char *s);
10+
std::size_t c_strLength_MoreNaturalButSlightlyWorse(const char *s);
11+
12+
}

benchmark/bm-swar.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,7 @@ void runBenchmark(benchmark::State &s) {
3232
PARSE8BYTES_CORPUS_X_LIST
3333
#undef X
3434

35+
#define X(TN, FTC) \
36+
BENCHMARK(runBenchmark<CorpusStringLength, Invoke##TN>);
37+
STRLEN_CORPUS_X_LIST
38+
#undef X

inc/zoo/swar/SWAR.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -362,8 +362,16 @@ struct BooleanSWAR: SWAR<NBits, T> {
362362
template<int NB, typename TT>
363363
friend constexpr BooleanSWAR<NB, TT>
364364
greaterEqual_MSB_off(SWAR<NB, TT>, SWAR<NB, TT>) noexcept;
365+
366+
template<int NB, typename TT>
367+
friend constexpr BooleanSWAR<NB, TT>
368+
asBooleanSWAR(SWAR<NB, TT> arg) noexcept;
365369
};
366370

371+
template<int NBits, typename T>
372+
constexpr BooleanSWAR<NBits, T>
373+
asBooleanSWAR(SWAR<NBits, T> arg) noexcept { return arg; }
374+
367375
template<int N, int NBits, typename T>
368376
constexpr BooleanSWAR<NBits, T>
369377
constantIsGreaterEqual(SWAR<NBits, T> subtrahend) noexcept {

0 commit comments

Comments
 (0)