Skip to content

Commit 6f4a49f

Browse files
committed
graph: Randomize map generation in stress
This makes the tests use more varied insertion orders into the maps, which influences their fill degree
1 parent 60007f0 commit 6f4a49f

File tree

1 file changed

+91
-49
lines changed

1 file changed

+91
-49
lines changed

graph/examples/stress.rs

Lines changed: 91 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use std::iter::FromIterator;
44
use std::sync::atomic::{AtomicUsize, Ordering::SeqCst};
55

66
use graph::prelude::{lazy_static, q};
7-
use rand::{rngs::SmallRng, thread_rng, Rng};
7+
use rand::{rngs::SmallRng, Rng};
88
use rand::{FromEntropy, SeedableRng};
99
use structopt::StructOpt;
1010

@@ -187,26 +187,26 @@ trait Template<T>: CacheWeight + Default {
187187
type Item;
188188

189189
// Create a new test object
190-
fn create(size: usize) -> Self;
190+
fn create(size: usize, rng: Option<&mut SmallRng>) -> Self;
191191

192192
// Return a sample of this test object of the given `size`. There's no
193193
// fixed definition of 'size', other than that smaller sizes will
194194
// take less memory than larger ones
195-
fn sample(&self, size: usize) -> Box<Self::Item>;
195+
fn sample(&self, size: usize, rng: Option<&mut SmallRng>) -> Box<Self::Item>;
196196
}
197197

198198
/// Template for testing caching of `String`
199199
impl Template<String> for String {
200200
type Item = String;
201201

202-
fn create(size: usize) -> Self {
202+
fn create(size: usize, _rng: Option<&mut SmallRng>) -> Self {
203203
let mut s = String::with_capacity(size);
204204
for _ in 0..size {
205205
s.push('x');
206206
}
207207
s
208208
}
209-
fn sample(&self, size: usize) -> Box<Self::Item> {
209+
fn sample(&self, size: usize, _rng: Option<&mut SmallRng>) -> Box<Self::Item> {
210210
Box::new(self[0..size].into())
211211
}
212212
}
@@ -215,10 +215,10 @@ impl Template<String> for String {
215215
impl Template<Vec<usize>> for Vec<usize> {
216216
type Item = Vec<usize>;
217217

218-
fn create(size: usize) -> Self {
218+
fn create(size: usize, _rng: Option<&mut SmallRng>) -> Self {
219219
Vec::from_iter(0..size)
220220
}
221-
fn sample(&self, size: usize) -> Box<Self::Item> {
221+
fn sample(&self, size: usize, _rng: Option<&mut SmallRng>) -> Box<Self::Item> {
222222
Box::new(self[0..size].into())
223223
}
224224
}
@@ -227,15 +227,15 @@ impl Template<Vec<usize>> for Vec<usize> {
227227
impl Template<HashMap<String, String>> for HashMap<String, String> {
228228
type Item = Self;
229229

230-
fn create(size: usize) -> Self {
230+
fn create(size: usize, _rng: Option<&mut SmallRng>) -> Self {
231231
let mut map = HashMap::new();
232232
for i in 0..size {
233233
map.insert(format!("key{}", i), format!("value{}", i));
234234
}
235235
map
236236
}
237237

238-
fn sample(&self, size: usize) -> Box<Self::Item> {
238+
fn sample(&self, size: usize, _rng: Option<&mut SmallRng>) -> Box<Self::Item> {
239239
Box::new(HashMap::from_iter(
240240
self.iter()
241241
.take(size)
@@ -246,16 +246,18 @@ impl Template<HashMap<String, String>> for HashMap<String, String> {
246246

247247
type ValueMap = MapMeasure<String, q::Value>;
248248

249-
/// Template for testing roughly a GraphQL response, i.e., a `BTreeMap<String, Value>`
250-
impl Template<ValueMap> for ValueMap {
251-
type Item = ValueMap;
252-
253-
fn create(size: usize) -> Self {
249+
impl ValueMap {
250+
fn make_map(size: usize, mut rng: Option<&mut SmallRng>) -> Self {
254251
let mut map = BTreeMap::new();
255252
let modulus = if *NESTED_MAP { 9 } else { 8 };
256253

257254
for i in 0..size {
258-
let value = match i % modulus {
255+
let kind = rng
256+
.as_deref_mut()
257+
.map(|rng| rng.gen_range(0, modulus))
258+
.unwrap_or(i % modulus);
259+
260+
let value = match kind {
259261
0 => q::Value::Boolean(i % 11 > 5),
260262
1 => q::Value::Int((i as i32).into()),
261263
2 => q::Value::Null,
@@ -276,42 +278,72 @@ impl Template<ValueMap> for ValueMap {
276278
}
277279
_ => unreachable!(),
278280
};
279-
map.insert(format!("val{}", i), value);
281+
282+
let key = rng.as_deref_mut().map(|rng| rng.gen()).unwrap_or(i) % modulus;
283+
map.insert(format!("val{}", key), value);
280284
}
281285
MapMeasure(map)
282286
}
287+
}
283288

284-
fn sample(&self, size: usize) -> Box<Self::Item> {
285-
Box::new(MapMeasure(BTreeMap::from_iter(
286-
self.0
287-
.iter()
288-
.take(size)
289-
.map(|(k, v)| (k.to_owned(), v.to_owned())),
290-
)))
289+
/// Template for testing roughly a GraphQL response, i.e., a `BTreeMap<String, Value>`
290+
impl Template<ValueMap> for ValueMap {
291+
type Item = ValueMap;
292+
293+
fn create(size: usize, rng: Option<&mut SmallRng>) -> Self {
294+
Self::make_map(size, rng)
295+
}
296+
297+
fn sample(&self, size: usize, rng: Option<&mut SmallRng>) -> Box<Self::Item> {
298+
// If the user specified '--fixed', don't build a new map every call
299+
// since that can be slow
300+
if rng.is_none() {
301+
Box::new(MapMeasure(BTreeMap::from_iter(
302+
self.0
303+
.iter()
304+
.take(size)
305+
.map(|(k, v)| (k.to_owned(), v.to_owned())),
306+
)))
307+
} else {
308+
Box::new(Self::make_map(size, rng))
309+
}
291310
}
292311
}
293312

294313
type UsizeMap = MapMeasure<usize, usize>;
295314

296-
/// Template for testing roughly a GraphQL response, i.e., a `BTreeMap<String, Value>`
297-
impl Template<UsizeMap> for UsizeMap {
298-
type Item = UsizeMap;
299-
300-
fn create(size: usize) -> Self {
315+
impl UsizeMap {
316+
fn make_map(size: usize, mut rng: Option<&mut SmallRng>) -> Self {
301317
let mut map = BTreeMap::new();
302318
for i in 0..size {
303-
map.insert(i * 2, i * 3);
319+
let key = rng.as_deref_mut().map(|rng| rng.gen()).unwrap_or(2 * i);
320+
map.insert(key, i * 3);
304321
}
305322
MapMeasure(map)
306323
}
324+
}
307325

308-
fn sample(&self, size: usize) -> Box<Self::Item> {
309-
Box::new(MapMeasure(BTreeMap::from_iter(
310-
self.0
311-
.iter()
312-
.take(size)
313-
.map(|(k, v)| (k.to_owned(), v.to_owned())),
314-
)))
326+
/// Template for testing roughly a GraphQL response, i.e., a `BTreeMap<String, Value>`
327+
impl Template<UsizeMap> for UsizeMap {
328+
type Item = UsizeMap;
329+
330+
fn create(size: usize, rng: Option<&mut SmallRng>) -> Self {
331+
Self::make_map(size, rng)
332+
}
333+
334+
fn sample(&self, size: usize, rng: Option<&mut SmallRng>) -> Box<Self::Item> {
335+
// If the user specified '--fixed', don't build a new map every call
336+
// since that can be slow
337+
if rng.is_none() {
338+
Box::new(MapMeasure(BTreeMap::from_iter(
339+
self.0
340+
.iter()
341+
.take(size)
342+
.map(|(k, v)| (k.to_owned(), v.to_owned())),
343+
)))
344+
} else {
345+
Box::new(Self::make_map(size, rng))
346+
}
315347
}
316348
}
317349

@@ -322,15 +354,15 @@ struct Cacheable<T> {
322354
}
323355

324356
impl<T: Template<T>> Cacheable<T> {
325-
fn new(size: usize) -> Self {
357+
fn new(size: usize, rng: Option<&mut SmallRng>) -> Self {
326358
Cacheable {
327359
cache: LfuCache::new(),
328-
template: T::create(size),
360+
template: T::create(size, rng),
329361
}
330362
}
331363

332-
fn sample(&self, size: usize) -> Box<T::Item> {
333-
self.template.sample(size)
364+
fn sample(&self, size: usize, rng: Option<&mut SmallRng>) -> Box<T::Item> {
365+
self.template.sample(size, rng)
334366
}
335367

336368
fn name(&self) -> &'static str {
@@ -361,13 +393,28 @@ struct Opt {
361393
/// Always use objects of size `--obj-size`
362394
#[structopt(short, long)]
363395
fixed: bool,
364-
/// The seed of the random number generator
396+
/// The seed of the random number generator. A seed of 0 means that all
397+
/// samples are taken from the same template object, and only differ in
398+
/// size
365399
#[structopt(long)]
366400
seed: Option<u64>,
367401
}
368402

403+
fn maybe_rng<'a>(opt: &'a Opt, rng: &'a mut SmallRng) -> Option<&'a mut SmallRng> {
404+
if opt.seed == Some(0) {
405+
None
406+
} else {
407+
Some(rng)
408+
}
409+
}
410+
369411
fn stress<T: Template<T, Item = T>>(opt: &Opt) {
370-
let mut cacheable: Cacheable<T> = Cacheable::new(opt.obj_size);
412+
let mut rng = match opt.seed {
413+
None => SmallRng::from_entropy(),
414+
Some(seed) => SmallRng::seed_from_u64(seed),
415+
};
416+
417+
let mut cacheable: Cacheable<T> = Cacheable::new(opt.obj_size, maybe_rng(opt, &mut rng));
371418

372419
println!("type: {}", cacheable.name());
373420
println!(
@@ -377,11 +424,6 @@ fn stress<T: Template<T, Item = T>>(opt: &Opt) {
377424
opt.cache_size
378425
);
379426

380-
let mut rng = match opt.seed {
381-
None => SmallRng::from_entropy(),
382-
Some(seed) => SmallRng::seed_from_u64(seed),
383-
};
384-
385427
let base_mem = ALLOCATED.load(SeqCst);
386428
let print_mod = opt.niter / opt.print_count + 1;
387429
let mut should_print = true;
@@ -416,15 +458,15 @@ fn stress<T: Template<T, Item = T>>(opt: &Opt) {
416458
rng.gen_range(0, opt.obj_size)
417459
};
418460
let before = ALLOCATED.load(SeqCst);
419-
let sample = cacheable.sample(size);
461+
let sample = cacheable.sample(size, maybe_rng(opt, &mut rng));
420462
let weight = sample.weight();
421463
let alloc = ALLOCATED.load(SeqCst) - before;
422464
sample_weight += weight;
423465
sample_alloc += alloc;
424466
if opt.samples {
425467
println!("sample: weight {:6} alloc {:6}", weight, alloc,);
426468
}
427-
cacheable.cache.insert(key, *cacheable.sample(size));
469+
cacheable.cache.insert(key, *sample);
428470
}
429471
if sample_alloc == sample_weight {
430472
println!(

0 commit comments

Comments
 (0)