Replace seedrandom package with inline ESM implementation#3650
Open
andreialecu wants to merge 2 commits intojosdejong:developfrom
Open
Replace seedrandom package with inline ESM implementation#3650andreialecu wants to merge 2 commits intojosdejong:developfrom
andreialecu wants to merge 2 commits intojosdejong:developfrom
Conversation
Replaces the CommonJS-only `seedrandom` dependency with an inline ESM module based on the same ARC4 (RC4) cipher algorithm. This eliminates the "Module 'seedrandom' is not ESM" warning in Angular and other ESM-first bundlers, and enables proper tree-shaking. Refs josdejong#3649
Author
|
You can verify here that the code in this PR produces the exact same sequence as the https://codesandbox.io/p/devbox/frosty-pond-jq6qqt?workspaceId=ws_HeHwxRY4bpopppVUoiziuV |
V8 optimizes the tight while(count--) loop better than the manually unrolled version. Results in 20-28% faster bulk generation and 56-58% faster for realistic create+generate usage patterns.
Author
|
Benchmark script npm install --no-save seedrandom@3.0.5
node --input-type=module --eval "
import { createRequire } from 'module';
const require = createRequire(import.meta.url + '/../');
const originalSeedrandom = require('seedrandom');
const { seedrandom } = await import('./src/function/probability/util/seedrandom.js');
const seeds = ['hello', 'test123', '42', 'a longer seed with spaces'];
const iterations = 500;
const numPerIter = 10000;
console.log('Generate 10,000 random numbers per iteration (' + iterations + ' iterations):');
console.log('');
console.log('Seed | Original | Inline | Speedup');
console.log('-------------------------|----------|---------|--------');
for (const seed of seeds) {
for (let i = 0; i < 50; i++) { const r = originalSeedrandom(seed); for (let j = 0; j < numPerIter; j++) r(); }
for (let i = 0; i < 50; i++) { const r = seedrandom(seed); for (let j = 0; j < numPerIter; j++) r(); }
let start = performance.now();
for (let i = 0; i < iterations; i++) { const r = originalSeedrandom(seed); for (let j = 0; j < numPerIter; j++) r(); }
const orig = (performance.now() - start) / iterations;
start = performance.now();
for (let i = 0; i < iterations; i++) { const r = seedrandom(seed); for (let j = 0; j < numPerIter; j++) r(); }
const inl = (performance.now() - start) / iterations;
const ratio = (orig / inl).toFixed(1);
console.log(seed.padEnd(25) + '| ' + orig.toFixed(3).padStart(6) + 'ms | ' + inl.toFixed(3).padStart(5) + 'ms | ' + ratio + 'x');
}
console.log('');
console.log('Realistic usage (create + generate N numbers, 100k iterations):');
console.log('');
console.log('Numbers/call | Original | Inline | Speedup');
console.log('-------------|----------|---------|--------');
for (const count of [1, 5, 10, 50, 100]) {
const iter = 100000;
for (let i = 0; i < 1000; i++) { const r = originalSeedrandom('seed'); for (let j = 0; j < count; j++) r(); }
for (let i = 0; i < 1000; i++) { const r = seedrandom('seed'); for (let j = 0; j < count; j++) r(); }
let start = performance.now();
for (let i = 0; i < iter; i++) { const r = originalSeedrandom('seed'); for (let j = 0; j < count; j++) r(); }
const orig = (performance.now() - start) / iter;
start = performance.now();
for (let i = 0; i < iter; i++) { const r = seedrandom('seed'); for (let j = 0; j < count; j++) r(); }
const inl = (performance.now() - start) / iter;
const ratio = (orig / inl).toFixed(1);
console.log(String(count).padEnd(13) + '| ' + (orig * 1000).toFixed(1).padStart(6) + 'µs | ' + (inl * 1000).toFixed(1).padStart(5) + 'µs | ' + ratio + 'x');
}
console.log('');
for (const seed of seeds) {
const o = originalSeedrandom(seed); const n = seedrandom(seed);
let match = true;
for (let i = 0; i < 100; i++) { if (o() !== n()) { match = false; break; } }
console.log(seed + ': ' + (match ? 'identical' : 'MISMATCH'));
}
"Results (MBP M1 Max): |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
seedrandomdependency with an inline ESM module based on the same ARC4 (RC4) cipher algorithmPart of #3649
Details
The
seedrandompackage hasn't been updated in 7+ years and only ships CommonJS. Rather than waiting for an upstream ESM build, this inlines a minimal implementation that only includes what mathjs needs (thedouble()generator).The implementation produces identical output to the original
seedrandompackage — verified by exact sequence stability tests for multiple seeds.Test plan
seededrandom.test.jsintegration tests pass