Skip to content

Commit c6cbf53

Browse files
cmcfarlenclaudeCopilot
authored
Optimize ts::Random performance by reusing distribution objects (#12624)
* Optimize ts::Random performance by reusing distribution objects The ts::Random class was creating new std::uniform_int_distribution and std::uniform_real_distribution objects on every call to random() and drandom(), which is inefficient. This change consolidates the random number generator state (engine and distributions) into a single thread_local State struct, so the distribution objects are created once per thread and reused across all calls. Performance impact: - Eliminates repeated construction/destruction of distribution objects - Maintains thread-safety through thread_local storage - Benchmark shows ~7% improvement in random() call performance 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]> * Call reset on dist objects after seeding engine Co-authored-by: Copilot <[email protected]> * Add unit test to verify post-seed behavior --------- Co-authored-by: Claude <[email protected]> Co-authored-by: Copilot <[email protected]>
1 parent bc784c0 commit c6cbf53

File tree

3 files changed

+51
-7
lines changed

3 files changed

+51
-7
lines changed

include/tscore/Random.h

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,24 +33,30 @@ class Random
3333
static uint64_t
3434
random()
3535
{
36-
std::uniform_int_distribution<uint64_t> dist{0, UINT64_MAX};
37-
return dist(_engine);
36+
return _state.int_dist(_state.engine);
3837
}
3938

4039
static double
4140
drandom()
4241
{
43-
std::uniform_real_distribution<double> dist{0, 1};
44-
return dist(_engine);
42+
return _state.real_dist(_state.engine);
4543
}
4644

4745
static void
4846
seed(uint64_t s)
4947
{
50-
_engine.seed(s);
48+
_state.engine.seed(s);
49+
_state.int_dist.reset();
50+
_state.real_dist.reset();
5151
}
5252

5353
private:
54-
thread_local static std::mt19937_64 _engine;
54+
struct State {
55+
std::mt19937_64 engine{std::random_device{}()};
56+
std::uniform_int_distribution<uint64_t> int_dist{0, UINT64_MAX};
57+
std::uniform_real_distribution<double> real_dist{0.0, 1.0};
58+
};
59+
60+
thread_local static State _state;
5561
};
5662
}; // namespace ts

src/tscore/Random.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,5 +25,5 @@
2525

2626
namespace ts
2727
{
28-
thread_local std::mt19937_64 Random::_engine{std::random_device{}()};
28+
thread_local Random::State Random::_state;
2929
}; // namespace ts

src/tscore/unit_tests/test_Random.cc

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,3 +50,41 @@ TEST_CASE("test random", "[libts][random]")
5050
diff = std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::high_resolution_clock::now() - start);
5151
std::cout << static_cast<double>(diff.count()) / 1000000 << " ns per InkRand::random()" << std::endl;
5252
}
53+
54+
TEST_CASE("test random reseeding", "[libts][random]")
55+
{
56+
// Test that reseeding produces deterministic sequences
57+
// This verifies that distribution reset() is called properly in seed()
58+
59+
// Generate first sequence
60+
ts::Random::seed(42);
61+
uint64_t first_int = ts::Random::random();
62+
double first_double = ts::Random::drandom();
63+
64+
// Generate some values to potentially populate distribution cache
65+
for (int i = 0; i < 100; ++i) {
66+
ts::Random::random();
67+
ts::Random::drandom();
68+
}
69+
70+
// Reseed with same value - should reset distributions and produce identical sequence
71+
ts::Random::seed(42);
72+
uint64_t second_int = ts::Random::random();
73+
double second_double = ts::Random::drandom();
74+
75+
REQUIRE(first_int == second_int);
76+
REQUIRE(first_double == second_double);
77+
78+
// Verify this works with InkRand too for consistency
79+
InkRand ink1(42);
80+
InkRand ink2(42);
81+
82+
REQUIRE(ink1.random() == ink2.random());
83+
REQUIRE(ink1.drandom() == ink2.drandom());
84+
85+
// And that ts::Random matches InkRand after reseeding
86+
ts::Random::seed(42);
87+
InkRand ink3(42);
88+
89+
REQUIRE(ts::Random::random() == ink3.random());
90+
}

0 commit comments

Comments
 (0)