Skip to content

Commit

Permalink
Various performance related updates
Browse files Browse the repository at this point in the history
  • Loading branch information
dubzzz committed Oct 28, 2020
1 parent d8e9bfa commit 44fb49f
Show file tree
Hide file tree
Showing 3 changed files with 108 additions and 67 deletions.
141 changes: 77 additions & 64 deletions perf/benchmark.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const { testDistribution } = require('./tasks.cjs');
const Benchmark = require('benchmark');
const prandRef = require('../lib/pure-rand');
const prandTest = require('../lib-new/pure-rand');
const { countCallsToNext } = require('./helpers.cjs');

const PRERUN_SAMPLES = 50;
const WARMUP_SAMPLES = 1000;
Expand All @@ -23,71 +24,83 @@ const NUM_TESTS = 1000;
const PROF_GEN = process.env.PROF_GEN || 'xoroshiro128plus';
console.log(`Generator: ${PROF_GEN}\n`);

// Declare builder of benchmarks
const buildBenchmarks = (type, lib) => {
return [
new Benchmark(
`uniformIntDistribution[0;96]...............................@${type}`,
() => {
// Range size is prime
const g = genFor(lib, PROF_GEN);
const distribution = lib.uniformIntDistribution;
const settings = { min: 0, max: 0xffff };
testDistribution(distribution, g, NUM_TESTS, settings);
},
benchConf
),
new Benchmark(
`uniformIntDistribution[0;0xffff]...........................@${type}`,
() => {
// Range size is a small power of 2
const g = genFor(lib, PROF_GEN);
const distribution = lib.uniformIntDistribution;
const settings = { min: 0, max: 0xffff };
testDistribution(distribution, g, NUM_TESTS, settings);
},
benchConf
),
new Benchmark(
`uniformIntDistribution[0;0xffffffff].......................@${type}`,
() => {
// For range of size <=2**32 (ie to-from+1 <= 2**32)
// uniformIntDistribution uses another execution path
const g = genFor(lib, PROF_GEN);
const distribution = lib.uniformIntDistribution;
const settings = { min: 0, max: 0xffffffff };
testDistribution(distribution, g, NUM_TESTS, settings);
},
benchConf
),
new Benchmark(
`uniformIntDistribution[0;0xffffffff+1].....................@${type}`,
() => {
// Range size is just above threshold used by uniformIntDistribution
// to switch to another algorithm
const g = genFor(lib, PROF_GEN);
const distribution = lib.uniformIntDistribution;
const settings = { min: 0, max: 0xffffffff + 1 };
testDistribution(distribution, g, NUM_TESTS, settings);
},
benchConf
),
new Benchmark(
`uniformIntDistribution[MIN_SAFE_INTEGER;MAX_SAFE_INTEGER]..@${type}`,
() => {
// Range size is the maximal one
const g = genFor(lib, PROF_GEN);
const distribution = lib.uniformIntDistribution;
const settings = { min: Number.MIN_SAFE_INTEGER, max: Number.MAX_SAFE_INTEGER };
testDistribution(distribution, g, NUM_TESTS, settings);
},
benchConf
),
];
};
// Declare configuration matrix
const configurations = [
['Reference', prandRef],
['Test', prandTest],
];

// Declare benchmarks
const benchmarks = [...buildBenchmarks('Reference', prandRef), ...buildBenchmarks('Test', prandTest)];
// Declare performance tests
const performanceTests = [
{
name: (type) => `uniformIntDistribution[0;96]...............................@${type}`,
run: (lib, customGen = genFor) => {
// Range size is prime
const g = customGen(lib, PROF_GEN);
const distribution = lib.uniformIntDistribution;
const settings = { min: 0, max: 0xffff };
testDistribution(distribution, g, NUM_TESTS, settings);
},
},
{
name: (type) => `uniformIntDistribution[0;0xffff]...........................@${type}`,
run: (lib, customGen = genFor) => {
// Range size is a small power of 2
const g = customGen(lib, PROF_GEN);
const distribution = lib.uniformIntDistribution;
const settings = { min: 0, max: 0xffff };
testDistribution(distribution, g, NUM_TESTS, settings);
},
},
{
name: (type) => `uniformIntDistribution[0;0xffffffff].......................@${type}`,
run: (lib, customGen = genFor) => {
// For range of size <=2**32 (ie to-from+1 <= 2**32)
// uniformIntDistribution uses another execution path
const g = customGen(lib, PROF_GEN);
const distribution = lib.uniformIntDistribution;
const settings = { min: 0, max: 0xffffffff };
testDistribution(distribution, g, NUM_TESTS, settings);
},
},
{
name: (type) => `uniformIntDistribution[0;0xffffffff+1].....................@${type}`,
run: (lib, customGen = genFor) => {
// Range size is just above threshold used by uniformIntDistribution
// to switch to another algorithm
const g = customGen(lib, PROF_GEN);
const distribution = lib.uniformIntDistribution;
const settings = { min: 0, max: 0xffffffff + 1 };
testDistribution(distribution, g, NUM_TESTS, settings);
},
},
{
name: (type) => `uniformIntDistribution[MIN_SAFE_INTEGER;MAX_SAFE_INTEGER]..@${type}`,
run: (lib, customGen = genFor) => {
// Range size is the maximal one
const g = customGen(lib, PROF_GEN);
const distribution = lib.uniformIntDistribution;
const settings = { min: Number.MIN_SAFE_INTEGER, max: Number.MAX_SAFE_INTEGER };
testDistribution(distribution, g, NUM_TESTS, settings);
},
},
];

// Declare the benchmarks
const benchmarks = configurations.flatMap(([type, lib]) =>
performanceTests.map((test) => new Benchmark(test.name(type), () => test.run(lib), benchConf))
);

// Simple checks concerning number of calls to the underlying generators
console.log(`Measuring number of calls to next...\n`);
for (const test of performanceTests) {
for (const [type, lib] of configurations) {
const [g, counter] = countCallsToNext(genFor(lib, PROF_GEN));
test.run(lib, () => g);
console.log(`${test.name(type)} called generator on next ${counter.count} times`);
}
}
console.log(``);

// Run all the code of all the benchmarks at least once before running them for measurements.
// It ensures that non-optimized path will not be wrongly optimized. In the past we had reports like:
Expand Down
22 changes: 22 additions & 0 deletions perf/helpers.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,25 @@ exports.genFor = (lib, genName) => {
const seed = 42;
return lib[genName](seed);
};

// Build a wrapper around a generator to count calls to next
exports.countCallsToNext = (gen) => {
const data = { count: 0 };
class Wrapper {
constructor(g) {
this.g = g;
}
min() {
return this.g.min();
}
max() {
return this.g.max();
}
next() {
data.count += 1;
const [v, ng] = this.g.next();
return [v, new Wrapper(ng)];
}
}
return [new Wrapper(gen), data];
};
12 changes: 9 additions & 3 deletions perf/profiler.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ const { testNext, testJump, testDistribution } = require('./tasks.cjs');
const prand = require('../lib/pure-rand');

const NUM_TESTS = 10000000;
const PROF_TYPE = process.env.PROF_TYPE || 'skip';
const PROF_GEN = process.env.PROF_GEN || 'congruential32';
const PROF_TYPE = process.env.PROF_TYPE || 'distrib';
const PROF_GEN = process.env.PROF_GEN || 'xoroshiro128plus';
console.log(`Profiler type: ${PROF_TYPE}`);
console.log(`Generator....: ${PROF_GEN}`);

Expand All @@ -22,7 +22,13 @@ switch (PROF_TYPE) {
case 'jump':
testJump(g, NUM_TESTS);
break;
case 'skip':
case 'distrib':
testDistribution(prand.uniformIntDistribution, g, NUM_TESTS);
break;
case 'distrib-large':
testDistribution(prand.uniformIntDistribution, g, NUM_TESTS, {
min: Number.MIN_SAFE_INTEGER,
max: Number.MAX_SAFE_INTEGER,
});
break;
}

0 comments on commit 44fb49f

Please sign in to comment.