733ecd2681
Add root Cargo workspace with 9 crates: salvium-crypto (extended), salvium-types, salvium-consensus, salvium-wallet, salvium-tx, salvium-rpc, salvium-miner (extended), salvium-cli, salvium-multisig. New modules: chain_state, block_weight, alt_chain, validation, offline signing, stake lifecycle, wallet sync/query/encryption/utxo, randomx utilities, and full multisig crate with CARROT support. Delete 188 JS test/helper/debug files; archive integration test scripts to test/legacy-js/ for live testnet use. Testnet integration tests (transfer, stake, burn, convert, sweep) remain as #[ignore]- gated Rust tests runnable with --ignored against a live daemon.
193 lines
5.5 KiB
JavaScript
193 lines
5.5 KiB
JavaScript
/**
|
|
* True Multi-threaded RandomX Benchmark using Worker Threads
|
|
*/
|
|
|
|
import { Worker } from 'worker_threads';
|
|
import { fileURLToPath } from 'url';
|
|
import { dirname, join } from 'path';
|
|
import { writeFileSync, unlinkSync } from 'fs';
|
|
import os from 'os';
|
|
|
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
const BENCHMARK_DURATION = 20000; // 20 seconds
|
|
const AVAILABLE_CORES = os.cpus().length;
|
|
|
|
// Write the worker code to a file
|
|
const workerCode = `
|
|
import { parentPort, workerData } from 'worker_threads';
|
|
import { randomx_init_cache, randomx_create_vm } from '../src/randomx/vendor/index.js';
|
|
|
|
let vm = null;
|
|
let hashes = 0;
|
|
let running = false;
|
|
|
|
async function init() {
|
|
const seed = new Uint8Array(32);
|
|
const cache = randomx_init_cache(seed);
|
|
vm = randomx_create_vm(cache);
|
|
parentPort.postMessage({ type: 'ready' });
|
|
}
|
|
|
|
function mine() {
|
|
if (!running || !vm) return;
|
|
|
|
// Hash batch
|
|
for (let i = 0; i < 10 && running; i++) {
|
|
vm.calculate_hash('benchmark input ' + hashes);
|
|
hashes++;
|
|
}
|
|
|
|
if (running) {
|
|
setImmediate(mine);
|
|
}
|
|
}
|
|
|
|
parentPort.on('message', async (msg) => {
|
|
if (msg.type === 'init') {
|
|
await init();
|
|
} else if (msg.type === 'start') {
|
|
running = true;
|
|
hashes = 0;
|
|
mine();
|
|
} else if (msg.type === 'stop') {
|
|
running = false;
|
|
parentPort.postMessage({ type: 'result', hashes });
|
|
}
|
|
});
|
|
`;
|
|
|
|
const workerPath = join(__dirname, '_temp_benchmark_worker.js');
|
|
|
|
async function createWorker() {
|
|
return new Promise((resolve, reject) => {
|
|
const worker = new Worker(workerPath);
|
|
|
|
worker.once('message', (msg) => {
|
|
if (msg.type === 'ready') {
|
|
resolve(worker);
|
|
}
|
|
});
|
|
|
|
worker.on('error', reject);
|
|
worker.postMessage({ type: 'init' });
|
|
});
|
|
}
|
|
|
|
async function benchmarkThreads(numThreads) {
|
|
console.log(`\n=== Benchmarking ${numThreads} Worker Thread${numThreads > 1 ? 's' : ''} ===\n`);
|
|
|
|
// Create workers
|
|
console.log(`Creating ${numThreads} workers (each with 256MB cache)...`);
|
|
const initStart = Date.now();
|
|
|
|
const workers = [];
|
|
for (let i = 0; i < numThreads; i++) {
|
|
const worker = await createWorker();
|
|
workers.push(worker);
|
|
process.stdout.write(`\rWorker ${i + 1}/${numThreads} ready`);
|
|
}
|
|
|
|
const initTime = Date.now() - initStart;
|
|
console.log(`\nAll workers initialized in ${initTime}ms\n`);
|
|
|
|
// Start mining
|
|
console.log(`Mining for ${BENCHMARK_DURATION / 1000} seconds...`);
|
|
const startTime = Date.now();
|
|
|
|
for (const worker of workers) {
|
|
worker.postMessage({ type: 'start' });
|
|
}
|
|
|
|
// Wait for benchmark duration
|
|
await new Promise(r => setTimeout(r, BENCHMARK_DURATION));
|
|
|
|
// Stop and collect results
|
|
const results = await Promise.all(workers.map(worker => {
|
|
return new Promise(resolve => {
|
|
worker.once('message', (msg) => {
|
|
if (msg.type === 'result') {
|
|
resolve(msg.hashes);
|
|
}
|
|
});
|
|
worker.postMessage({ type: 'stop' });
|
|
});
|
|
}));
|
|
|
|
const elapsed = (Date.now() - startTime) / 1000;
|
|
const totalHashes = results.reduce((a, b) => a + b, 0);
|
|
const hashrate = totalHashes / elapsed;
|
|
|
|
// Terminate workers
|
|
for (const worker of workers) {
|
|
worker.terminate();
|
|
}
|
|
|
|
console.log(`\nResults (${numThreads} thread${numThreads > 1 ? 's' : ''}):`);
|
|
console.log(` Total hashes: ${totalHashes.toLocaleString()}`);
|
|
console.log(` Time: ${elapsed.toFixed(2)}s`);
|
|
console.log(` Hashrate: ${hashrate.toFixed(2)} H/s`);
|
|
console.log(` Per thread: ${(hashrate / numThreads).toFixed(2)} H/s`);
|
|
console.log(` Memory: ${numThreads * 256}MB`);
|
|
|
|
return { threads: numThreads, hashrate, perThread: hashrate / numThreads };
|
|
}
|
|
|
|
async function main() {
|
|
console.log('RandomX Worker Threads Benchmark');
|
|
console.log('================================');
|
|
console.log(`Available CPU cores: ${AVAILABLE_CORES}`);
|
|
console.log(`Benchmark duration: ${BENCHMARK_DURATION / 1000}s per test\n`);
|
|
|
|
// Write worker file
|
|
writeFileSync(workerPath, workerCode);
|
|
|
|
const results = [];
|
|
|
|
try {
|
|
// Test different thread counts
|
|
const threadCounts = [1, 2, 4, Math.min(8, AVAILABLE_CORES)].filter((v, i, a) => a.indexOf(v) === i);
|
|
|
|
for (const threads of threadCounts) {
|
|
const result = await benchmarkThreads(threads);
|
|
results.push(result);
|
|
}
|
|
|
|
// Summary
|
|
console.log('\n================================');
|
|
console.log('SUMMARY');
|
|
console.log('================================\n');
|
|
|
|
const baseline = results[0].hashrate;
|
|
|
|
console.log('Threads | Hashrate | Per-Thread | Memory | Scaling');
|
|
console.log('--------|-------------|-------------|----------|--------');
|
|
|
|
for (const r of results) {
|
|
const scaling = ((r.hashrate / baseline) * 100).toFixed(0);
|
|
console.log(
|
|
`${r.threads.toString().padStart(7)} | ` +
|
|
`${r.hashrate.toFixed(2).padStart(9)} H/s | ` +
|
|
`${r.perThread.toFixed(2).padStart(9)} H/s | ` +
|
|
`${(r.threads * 256).toString().padStart(4)}MB | ` +
|
|
`${scaling}%`
|
|
);
|
|
}
|
|
|
|
console.log('\n================================');
|
|
console.log('Performance Notes');
|
|
console.log('================================\n');
|
|
console.log(`Single-thread baseline: ${results[0].perThread.toFixed(2)} H/s`);
|
|
console.log(`Best multi-thread: ${Math.max(...results.map(r => r.hashrate)).toFixed(2)} H/s`);
|
|
console.log('\nScaling efficiency depends on:');
|
|
console.log(' - Memory bandwidth (256MB cache per thread)');
|
|
console.log(' - CPU cache architecture');
|
|
console.log(' - WASM runtime overhead');
|
|
|
|
} finally {
|
|
// Clean up temp file
|
|
try { unlinkSync(workerPath); } catch {}
|
|
}
|
|
}
|
|
|
|
main().catch(console.error);
|