Skip to content

Commit

Permalink
Encapsulate PRNG usage (#53)
Browse files Browse the repository at this point in the history
Allow for us to more easily change the PRNG representation.

At startup run the PRNG a few times to reduce initial correlation in bottom bits.  Ultimately, we should update the PRNG to something better.
  • Loading branch information
mjp41 authored Sep 26, 2024
1 parent 665eb57 commit 9514989
Show file tree
Hide file tree
Showing 17 changed files with 183 additions and 133 deletions.
14 changes: 7 additions & 7 deletions src/rt/debug/systematic.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@

#include "../debug/logging.h"
#include "../pal/semaphore.h"
#include "ds/prng.h"
#include "ds/scramble.h"
#include "test/xoroshiro.h"

#include <snmalloc/snmalloc.h>

Expand Down Expand Up @@ -86,19 +86,19 @@ namespace verona::rt
/// Return a mutable reference to the pseudo random number generator (PRNG).
/// It is assumed that the PRNG will only be setup once via `set_seed`.
/// After it is setup, the PRNG must only be used via `get_prng_next`.
static xoroshiro::p128r32& get_prng_for_setup()
static PRNG<>& get_prng_for_setup()
{
static xoroshiro::p128r32 prng;
static PRNG<> prng;
return prng;
}

/// Return a mutable reference to the scrambler. It is assumed that the
/// scrambler will only be setup once via `set_seed`. After it is setup, the
/// scrambler must only be accessed via a const reference
/// (see `get_scrambler`).
static verona::Scramble& get_scrambler_for_setup()
static verona::rt::Scramble& get_scrambler_for_setup()
{
static verona::Scramble scrambler;
static verona::rt::Scramble scrambler;
return scrambler;
}

Expand All @@ -113,15 +113,15 @@ namespace verona::rt
}

/// Return a const reference to the scrambler.
static const verona::Scramble& get_scrambler()
static const verona::rt::Scramble& get_scrambler()
{
return get_scrambler_for_setup();
}

static void set_seed(uint64_t seed)
{
auto& rng = get_prng_for_setup();
rng.set_state(seed);
rng.set_seed(seed);
get_scrambler_for_setup().setup(rng);
}

Expand Down
81 changes: 81 additions & 0 deletions src/rt/ds/prng.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
// Copyright Microsoft and Project Verona Contributors.
// SPDX-License-Identifier: MIT
#pragma once

#include <random>
#include <test/xoroshiro.h>

namespace verona::rt
{
// Template parameter expresses if the context is multi-threaded
template<bool Multithreaded = false>
class PRNG
{
static constexpr bool ThreadSafeRequired =
#ifdef USE_SYSTEMATIC_TESTING
// If we are using systematic testing, then the PRNG does not need to be
// thread safe, even when called by multiple threads
false;
#else
Multithreaded;
#endif

std::conditional_t<ThreadSafeRequired, std::mt19937_64, xoroshiro::p128r32>
rng;

void set_seed(std::mt19937_64& r, uint64_t seed)
{
r.seed(seed);
}

void set_seed(xoroshiro::p128r32& r, uint64_t seed)
{
r.set_state(seed);
}

uint32_t next(std::mt19937_64& r)
{
return r();
}

uint32_t next(xoroshiro::p128r32& r)
{
return r.next();
}

public:
PRNG() : rng(5489) {}

PRNG(uint64_t seed)
{
set_seed(seed);
}

void set_seed(uint64_t seed)
{
set_seed(rng, seed);
// Discard the first 10 values to avoid correlation with the seed.
// Otherwise, using adjacent seeds will result in poor initial
// randomness.
for (size_t i = 0; i < 10; i++)
next();
}

uint32_t next()
{
return next(rng);
}

uint32_t next(uint32_t max)
{
return next() % max;
}

uint64_t next64()
{
auto top = next();
auto bottom = next();
return (static_cast<uint64_t>(top) << 32) | bottom;
}
};
} // namespace verona::rt
7 changes: 4 additions & 3 deletions src/rt/ds/scramble.h
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
// Copyright Microsoft and Project Verona Contributors.
// SPDX-License-Identifier: MIT
#pragma once
#include "test/xoroshiro.h"

#include "ds/prng.h"

#include <cstdint>
#include <iomanip>
#include <iostream>

namespace verona
namespace verona::rt
{
/**
* This class is used to provide alternative orderings on a pointer type.
Expand Down Expand Up @@ -35,7 +36,7 @@ namespace verona
public:
Scramble() {}

void setup(xoroshiro::p128r32& r)
void setup(verona::rt::PRNG<>& r)
{
for (size_t i = 0; i < ROUNDS; i++)
{
Expand Down
1 change: 0 additions & 1 deletion test/func/cownchain/cownchain.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
// SPDX-License-Identifier: MIT

#include <debug/harness.h>
#include <test/xoroshiro.h>
/**
* This example is design to test a long chain of cowns being collected.
**/
Expand Down
45 changes: 5 additions & 40 deletions test/func/cowngc1/cowngc1.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
// SPDX-License-Identifier: MIT
#include <debug/harness.h>
#include <random>
#include <test/xoroshiro.h>

/**
* This tests the cown leak detector.
Expand Down Expand Up @@ -41,40 +40,6 @@
* Check that the LD runs even if no work is scheduled.
**/

struct PRNG
{
#ifdef USE_SYSTEMATIC_TESTING
// Use xoroshiro for systematic testing, because it's simple and
// and deterministic across platforms.
xoroshiro::p128r64 rand;
#else
// We don't mind data races for our PRNG, because concurrent testing means
// our results will already be nondeterministic. However, data races may
// cause xoroshiro to abort.
std::mt19937_64 rand;
#endif

PRNG(size_t seed) : rand(seed) {}

uint64_t next()
{
#ifdef USE_SYSTEMATIC_TESTING
return rand.next();
#else
return rand();
#endif
}

void seed(size_t seed)
{
#ifdef USE_SYSTEMATIC_TESTING
return rand.set_state(seed);
#else
return rand.seed(seed);
#endif
}
};

static constexpr uint64_t others_count = 3;

struct CCown;
Expand Down Expand Up @@ -261,8 +226,8 @@ struct Pong
struct Ping
{
RCown* rcown;
PRNG* rand;
Ping(RCown* rcown, PRNG* rand) : rcown(rcown), rand(rand) {}
PRNG<true>* rand;
Ping(RCown* rcown, PRNG<true>* rand) : rcown(rcown), rand(rand) {}

void operator()()
{
Expand Down Expand Up @@ -372,11 +337,11 @@ void test_cown_gc(
uint64_t forward_count,
size_t ring_size,
SystematicTestHarness* h,
PRNG* rand)
PRNG<true>* rand)
{
rcown_first = nullptr;
auto a = new RCown(ring_size, forward_count);
rand->seed(h->current_seed());
rand->set_seed(h->current_seed());
schedule_lambda(a, Ping(a, rand));
}

Expand All @@ -389,7 +354,7 @@ void test_cown_gc_before_sched()
int main(int argc, char** argv)
{
SystematicTestHarness harness(argc, argv);
PRNG rand(harness.seed_lower);
PRNG<true> rand(harness.seed_lower);

size_t ring = harness.opt.is<size_t>("--ring", 10);
uint64_t forward = harness.opt.is<uint64_t>("--forward", 10);
Expand Down
46 changes: 6 additions & 40 deletions test/func/cowngc4/cowngc4.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
// SPDX-License-Identifier: MIT
#include <debug/harness.h>
#include <random>
#include <test/xoroshiro.h>

/**
* This tests the cown leak detector. This is a variant of cowngc1.
Expand Down Expand Up @@ -31,40 +30,6 @@
* messages, including in-flight messages.
**/

struct PRNG
{
#ifdef USE_SYSTEMATIC_TESTING
// Use xoroshiro for systematic testing, because it's simple and
// and deterministic across platforms.
xoroshiro::p128r64 rand;
#else
// We don't mind data races for our PRNG, because concurrent testing means
// our results will already be nondeterministic. However, data races may
// cause xoroshiro to abort.
std::mt19937_64 rand;
#endif

PRNG(size_t seed) : rand(seed) {}

uint64_t next()
{
#ifdef USE_SYSTEMATIC_TESTING
return rand.next();
#else
return rand();
#endif
}

void seed(size_t seed)
{
#ifdef USE_SYSTEMATIC_TESTING
return rand.set_state(seed);
#else
return rand.seed(seed);
#endif
}
};

struct CCown : public VCown<CCown>
{
CCown* child;
Expand Down Expand Up @@ -286,8 +251,9 @@ struct Ping
using RegionClass = typename RegionType_to_class<region_type>::T;

RCown<region_type>* rcown;
PRNG* rand;
Ping(RCown<region_type>* rcown, PRNG* rand) : rcown(rcown), rand(rand) {}
PRNG<true>* rand;
Ping(RCown<region_type>* rcown, PRNG<true>* rand) : rcown(rcown), rand(rand)
{}

void operator()()
{
Expand Down Expand Up @@ -387,18 +353,18 @@ void test_cown_gc(
uint64_t forward_count,
size_t ring_size,
SystematicTestHarness* h,
PRNG* rand)
PRNG<true>* rand)
{
rcown_first = nullptr;
auto a = new RCown<region_type>(ring_size, forward_count);
rand->seed(h->current_seed());
rand->set_seed(h->current_seed());
schedule_lambda(a, Ping<region_type>(a, rand));
}

int main(int argc, char** argv)
{
SystematicTestHarness harness(argc, argv);
PRNG rand(harness.seed_lower);
PRNG<true> rand(harness.seed_lower);

size_t ring = harness.opt.is<size_t>("--ring", 10);
uint64_t forward = harness.opt.is<uint64_t>("--forward", 10);
Expand Down
4 changes: 2 additions & 2 deletions test/func/diningphilosophers/diningphil.cc
Original file line number Diff line number Diff line change
Expand Up @@ -144,8 +144,8 @@ void test_dining(
Logging::cout() << "Fork " << i << " " << f << std::endl;
}

verona::Scramble scrambler;
xoroshiro::p128r32 rand(h->current_seed());
verona::rt::Scramble scrambler;
verona::rt::PRNG<> rand{h->current_seed()};

for (size_t i = 0; i < philosophers; i++)
{
Expand Down
4 changes: 2 additions & 2 deletions test/func/freeze/freeze.cc
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright Microsoft and Project Verona Contributors.
// SPDX-License-Identifier: MIT
#include "debug/harness.h"
#include "test/xoroshiro.h"
#include "ds/prng.h"

#include <vector>
#include <verona.h>
Expand Down Expand Up @@ -393,7 +393,7 @@ void test_random(size_t seed = 1, size_t max_edges = 128)
heap::debug_check_empty();
size_t id = 0;

xoroshiro::p128r32 r(seed);
PRNG<> r(seed);
Symbolic* root = new (RegionType::Trace) Symbolic;
#ifndef NDEBUG
bool* reach = new bool[max_edges * max_edges];
Expand Down
Loading

0 comments on commit 9514989

Please sign in to comment.