Use std random number generators in tests.

An earlier CL introduced absl::Uniform, which is not yet open sourced,
and therefore unavailable in the open source build.

This CL removes absl::Uniform and ACMRandom in favor of equivalent C++11
standard random generators. Abseil promises to be faster than the
standard library, but we can afford a speed hit in tests in return for
an easier open sourcing story.
This commit is contained in:
costan
2019-01-04 14:35:15 -08:00
committed by Victor Costan
parent 925c3094c4
commit 3fcbc47f99
2 changed files with 54 additions and 80 deletions

View File

@@ -189,56 +189,6 @@ string ReadTestDataFile(const string& base);
// Not safe for general use due to truncation issues.
string StringPrintf(const char* format, ...);
// A simple, non-cryptographically-secure random generator.
class ACMRandom {
public:
explicit ACMRandom(uint32 seed) : seed_(seed) {}
int32 Next();
int32 Uniform(int32 n) {
return Next() % n;
}
uint8 Rand8() {
return static_cast<uint8>((Next() >> 1) & 0x000000ff);
}
bool OneIn(int X) { return Uniform(X) == 0; }
// Skewed: pick "base" uniformly from range [0,max_log] and then
// return "base" random bits. The effect is to pick a number in the
// range [0,2^max_log-1] with bias towards smaller numbers.
int32 Skewed(int max_log);
private:
static const uint32 M = 2147483647L; // 2^31-1
uint32 seed_;
};
inline int32 ACMRandom::Next() {
static const uint64 A = 16807; // bits 14, 8, 7, 5, 2, 1, 0
// We are computing
// seed_ = (seed_ * A) % M, where M = 2^31-1
//
// seed_ must not be zero or M, or else all subsequent computed values
// will be zero or M respectively. For all other values, seed_ will end
// up cycling through every number in [1,M-1]
uint64 product = seed_ * A;
// Compute (product % M) using the fact that ((x << 31) % M) == x.
seed_ = (product >> 31) + (product & M);
// The first reduction may overflow by 1 bit, so we may need to repeat.
// mod == M is not possible; using > allows the faster sign-bit-based test.
if (seed_ > M) {
seed_ -= M;
}
return seed_;
}
inline int32 ACMRandom::Skewed(int max_log) {
const int32 base = (Next() - 1) % (max_log+1);
return (Next() - 1) & ((1u << base)-1);
}
// A wall-time clock. This stub is not super-accurate, nor resistant to the
// system time changing.
class CycleTimer {

View File

@@ -31,6 +31,7 @@
#include <algorithm>
#include <random>
#include <string>
#include <utility>
#include <vector>
@@ -405,24 +406,28 @@ static void VerifyIOVec(const string& input) {
// Try uncompressing into an iovec containing a random number of entries
// ranging from 1 to 10.
char* buf = new char[input.size()];
ACMRandom rnd(input.size());
size_t num = absl::Uniform<uint32_t>(rnd) % 10 + 1;
std::minstd_rand0 rng(input.size());
std::uniform_int_distribution<size_t> uniform_1_to_10(1, 10);
size_t num = uniform_1_to_10(rng);
if (input.size() < num) {
num = input.size();
}
struct iovec* iov = new iovec[num];
int used_so_far = 0;
std::bernoulli_distribution one_in_five(1.0 / 5);
for (size_t i = 0; i < num; ++i) {
assert(used_so_far < input.size());
iov[i].iov_base = buf + used_so_far;
if (i == num - 1) {
iov[i].iov_len = input.size() - used_so_far;
} else {
// Randomly choose to insert a 0 byte entry.
if (rnd.OneIn(5)) {
if (one_in_five(rng)) {
iov[i].iov_len = 0;
} else {
iov[i].iov_len =
absl::Uniform<uint32_t>(rnd, 0, input.size() - used_so_far);
std::uniform_int_distribution<size_t> uniform_not_used_so_far(
0, input.size() - used_so_far - 1);
iov[i].iov_len = uniform_not_used_so_far(rng);
}
}
used_so_far += iov[i].iov_len;
@@ -665,42 +670,58 @@ TEST(Snappy, SimpleTests) {
// Verify max blowup (lots of four-byte copies)
TEST(Snappy, MaxBlowup) {
std::mt19937 rng;
std::uniform_int_distribution<uint8_t> random_byte;
string input;
for (int i = 0; i < 20000; i++) {
ACMRandom rnd(i);
uint32 bytes = static_cast<uint32>(absl::Uniform<uint32_t>(rnd));
input.append(reinterpret_cast<char*>(&bytes), sizeof(bytes));
}
for (int i = 19999; i >= 0; i--) {
ACMRandom rnd(i);
uint32 bytes = static_cast<uint32>(absl::Uniform<uint32_t>(rnd));
input.append(reinterpret_cast<char*>(&bytes), sizeof(bytes));
for (int i = 0; i < 80000; ++i)
input.push_back(static_cast<char>(random_byte(rng)));
for (int i = 0; i < 80000; i += 4) {
string four_bytes(input.end() - i - 4, input.end() - i);
input.append(four_bytes);
}
Verify(input);
}
TEST(Snappy, RandomData) {
ACMRandom rnd(FLAGS_test_random_seed);
std::minstd_rand0 rng(FLAGS_test_random_seed);
std::uniform_int_distribution<int> uniform_0_to_3(0, 3);
std::uniform_int_distribution<int> uniform_0_to_8(0, 8);
std::uniform_int_distribution<uint8_t> uniform_byte;
std::uniform_int_distribution<size_t> uniform_4k(0, 4095);
std::uniform_int_distribution<size_t> uniform_64k(0, 65535);
std::bernoulli_distribution one_in_ten(1.0 / 10);
const int num_ops = 20000;
constexpr int num_ops = 20000;
for (int i = 0; i < num_ops; i++) {
if ((i % 1000) == 0) {
VLOG(0) << "Random op " << i << " of " << num_ops;
}
string x;
size_t len = absl::Uniform<uint32_t>(rnd, 0, 4096);
size_t len = uniform_4k(rng);
if (i < 100) {
len = 65536 + absl::Uniform<uint32_t>(rnd, 0, 65536);
len = 65536 + uniform_64k(rng);
}
while (x.size() < len) {
int run_len = 1;
if (rnd.OneIn(10)) {
run_len = rnd.Skewed(8);
if (one_in_ten(rng)) {
int skewed_bits = uniform_0_to_8(rng);
// int is guaranteed to hold at least 16 bits, this uses at most 8 bits.
std::uniform_int_distribution<int> skewed_low(0,
(1 << skewed_bits) - 1);
run_len = skewed_low(rng);
}
char c = static_cast<char>(uniform_byte(rng));
if (i >= 100) {
int skewed_bits = uniform_0_to_3(rng);
// int is guaranteed to hold at least 16 bits, this uses at most 3 bits.
std::uniform_int_distribution<int> skewed_low(0,
(1 << skewed_bits) - 1);
c = static_cast<char>(skewed_low(rng));
}
char c = (i < 100) ? absl::Uniform<uint32_t>(rnd, 0, 256) : rnd.Skewed(3);
while (run_len-- > 0 && x.size() < len) {
x += c;
x.push_back(c);
}
}
@@ -1038,17 +1059,20 @@ TEST(Snappy, FindMatchLength) {
}
TEST(Snappy, FindMatchLengthRandom) {
const int kNumTrials = 10000;
const int kTypicalLength = 10;
ACMRandom rnd(FLAGS_test_random_seed);
constexpr int kNumTrials = 10000;
constexpr int kTypicalLength = 10;
std::minstd_rand0 rng(FLAGS_test_random_seed);
std::uniform_int_distribution<uint8_t> uniform_byte;
std::bernoulli_distribution one_in_two(1.0 / 2);
std::bernoulli_distribution one_in_typical_length(1.0 / kTypicalLength);
for (int i = 0; i < kNumTrials; i++) {
string s, t;
char a = absl::Uniform<uint8_t>(rnd);
char b = absl::Uniform<uint8_t>(rnd);
while (!rnd.OneIn(kTypicalLength)) {
s.push_back(rnd.OneIn(2) ? a : b);
t.push_back(rnd.OneIn(2) ? a : b);
char a = uniform_byte(rng);
char b = uniform_byte(rng);
while (!one_in_typical_length(rng)) {
s.push_back(one_in_two(rng) ? a : b);
t.push_back(one_in_two(rng) ? a : b);
}
DataEndingAtUnreadablePage u(s);
DataEndingAtUnreadablePage v(t);