updates
Signed-off-by: Tiago Garcia <tiago.rgarcia@ua.pt>
This commit is contained in:
parent
2a178133a6
commit
7347ddd1db
|
|
@ -363,6 +363,7 @@ static void mine_coins_avx2_omp(u64_t max_attempts, double max_time)
|
||||||
printf("Press Ctrl+C to stop\n\n");
|
printf("Press Ctrl+C to stop\n\n");
|
||||||
|
|
||||||
double start_time = get_wall_time();
|
double start_time = get_wall_time();
|
||||||
|
int should_stop = 0;
|
||||||
|
|
||||||
#pragma omp parallel
|
#pragma omp parallel
|
||||||
{
|
{
|
||||||
|
|
@ -393,15 +394,24 @@ static void mine_coins_avx2_omp(u64_t max_attempts, double max_time)
|
||||||
for(int i = 12; i < 54; i++)
|
for(int i = 12; i < 54; i++)
|
||||||
((u08_t *)base_coin)[i ^ 3] = 'A' + ((i - 12 + thread_id * SIMD_WIDTH) % 26);
|
((u08_t *)base_coin)[i ^ 3] = 'A' + ((i - 12 + thread_id * SIMD_WIDTH) % 26);
|
||||||
|
|
||||||
while(keep_running)
|
while(keep_running && !should_stop)
|
||||||
{
|
{
|
||||||
// Check stopping conditions
|
// Check stopping conditions (check shared flag first)
|
||||||
if(max_attempts > 0 && attempts >= max_attempts)
|
if(should_stop)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
if(max_attempts > 0 && attempts >= max_attempts)
|
||||||
|
{
|
||||||
|
should_stop = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
double elapsed_time = get_wall_time() - start_time;
|
double elapsed_time = get_wall_time() - start_time;
|
||||||
if(max_time > 0 && elapsed_time >= max_time)
|
if(max_time > 0 && elapsed_time >= max_time)
|
||||||
|
{
|
||||||
|
should_stop = 1;
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
prepare_coins(base_coin, interleaved_data, SIMD_WIDTH);
|
prepare_coins(base_coin, interleaved_data, SIMD_WIDTH);
|
||||||
sha1_avx2((v8si *)interleaved_data, (v8si *)interleaved_hash);
|
sha1_avx2((v8si *)interleaved_data, (v8si *)interleaved_hash);
|
||||||
|
|
@ -429,23 +439,26 @@ static void mine_coins_avx2_omp(u64_t max_attempts, double max_time)
|
||||||
|
|
||||||
// Print progress every 1M attempts (only from one thread)
|
// Print progress every 1M attempts (only from one thread)
|
||||||
// Periodically update the shared counter and report
|
// Periodically update the shared counter and report
|
||||||
if(thread_attempts % 100000 < SIMD_WIDTH)
|
if(thread_attempts >= 100000)
|
||||||
{
|
{
|
||||||
#pragma omp atomic
|
#pragma omp atomic
|
||||||
attempts += thread_attempts;
|
attempts += thread_attempts;
|
||||||
thread_attempts = 0;
|
thread_attempts = 0;
|
||||||
|
|
||||||
#pragma omp barrier
|
// Only master thread reports progress (no barrier to avoid blocking)
|
||||||
|
if(thread_id == 0)
|
||||||
#pragma omp master
|
|
||||||
{
|
{
|
||||||
if(attempts - last_reported_attempts >= 1000000)
|
u64_t current_attempts;
|
||||||
|
#pragma omp atomic read
|
||||||
|
current_attempts = attempts;
|
||||||
|
|
||||||
|
if(current_attempts - last_reported_attempts >= 1000000)
|
||||||
{
|
{
|
||||||
double elapsed = get_wall_time() - start_time;
|
double elapsed = get_wall_time() - start_time;
|
||||||
double rate = attempts / elapsed;
|
double rate = current_attempts / elapsed;
|
||||||
printf("Attempts: %llu, Rate: %.2f MH/s, Coins: %u, Elapsed: %.2fs\n",
|
printf("Attempts: %llu, Rate: %.2f MH/s, Coins: %u, Elapsed: %.2fs\n",
|
||||||
(unsigned long long)attempts, rate / 1e6, coins_found, elapsed);
|
(unsigned long long)current_attempts, rate / 1e6, coins_found, elapsed);
|
||||||
last_reported_attempts = attempts;
|
last_reported_attempts = current_attempts;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
235
index.html
235
index.html
|
|
@ -121,6 +121,30 @@
|
||||||
.button-group {
|
.button-group {
|
||||||
margin: 15px 0;
|
margin: 15px 0;
|
||||||
}
|
}
|
||||||
|
.limits-section {
|
||||||
|
background: #e8f4f8;
|
||||||
|
padding: 15px;
|
||||||
|
border-radius: 5px;
|
||||||
|
margin: 15px 0;
|
||||||
|
}
|
||||||
|
.limits-section h3 {
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
color: #0066cc;
|
||||||
|
}
|
||||||
|
.limit-note {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #666;
|
||||||
|
margin-top: 5px;
|
||||||
|
}
|
||||||
|
.limit-reached {
|
||||||
|
background: #fff3cd;
|
||||||
|
color: #856404;
|
||||||
|
padding: 10px;
|
||||||
|
border-radius: 5px;
|
||||||
|
margin-top: 10px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
@ -145,6 +169,20 @@
|
||||||
<input type="number" id="updateInterval" value="100" step="50" min="50">
|
<input type="number" id="updateInterval" value="100" step="50" min="50">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="limits-section">
|
||||||
|
<h3>Mining Limits (Optional)</h3>
|
||||||
|
<div class="control-group">
|
||||||
|
<label>Max Attempts:</label>
|
||||||
|
<input type="number" id="maxAttempts" value="" placeholder="Unlimited" min="1" step="1000000">
|
||||||
|
<span class="limit-note">(Leave empty for unlimited)</span>
|
||||||
|
</div>
|
||||||
|
<div class="control-group">
|
||||||
|
<label>Max Time (seconds):</label>
|
||||||
|
<input type="number" id="maxTime" value="" placeholder="Unlimited" min="1" step="60">
|
||||||
|
<span class="limit-note">(Leave empty for unlimited)</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="button-group">
|
<div class="button-group">
|
||||||
<button id="start">Start Mining</button>
|
<button id="start">Start Mining</button>
|
||||||
<button id="stop">Stop Mining</button>
|
<button id="stop">Stop Mining</button>
|
||||||
|
|
@ -152,6 +190,8 @@
|
||||||
<button id="clearCoins">Clear Coins Display</button>
|
<button id="clearCoins">Clear Coins Display</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div id="limitReached" class="limit-reached" style="display: none;"></div>
|
||||||
|
|
||||||
<div id="stats">
|
<div id="stats">
|
||||||
Waiting to start...
|
Waiting to start...
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -170,12 +210,93 @@
|
||||||
let lastDisplayedCoinCount = 0;
|
let lastDisplayedCoinCount = 0;
|
||||||
let pausedStats = false;
|
let pausedStats = false;
|
||||||
let simdAvailable = false;
|
let simdAvailable = false;
|
||||||
|
let miningStartTime = 0;
|
||||||
|
let maxAttempts = null;
|
||||||
|
let maxTime = null;
|
||||||
|
let updateStatsInternal; // Declare in outer scope
|
||||||
|
let needsReset = false; // Flag to track if we need to reset on next start
|
||||||
|
|
||||||
|
function checkLimits(beforeBatch = false) {
|
||||||
|
const attemptsPtr = Module._malloc(8);
|
||||||
|
const coinsPtr = Module._malloc(4);
|
||||||
|
const hashRatePtr = Module._malloc(8);
|
||||||
|
const elapsedPtr = Module._malloc(8);
|
||||||
|
|
||||||
|
Module.setValue(attemptsPtr, 0, 'i32');
|
||||||
|
Module.setValue(attemptsPtr + 4, 0, 'i32');
|
||||||
|
|
||||||
|
Module._get_statistics(attemptsPtr, coinsPtr, hashRatePtr, elapsedPtr);
|
||||||
|
|
||||||
|
const attemptsLowUnsigned = Module.getValue(attemptsPtr, 'i32') >>> 0;
|
||||||
|
const attemptsHighUnsigned = Module.getValue(attemptsPtr + 4, 'i32') >>> 0;
|
||||||
|
|
||||||
|
let attempts;
|
||||||
|
if (attemptsHighUnsigned === 0) {
|
||||||
|
attempts = attemptsLowUnsigned;
|
||||||
|
} else {
|
||||||
|
const low = BigInt(attemptsLowUnsigned);
|
||||||
|
const high = BigInt(attemptsHighUnsigned);
|
||||||
|
attempts = (high * BigInt(4294967296)) + low;
|
||||||
|
}
|
||||||
|
|
||||||
|
const elapsed = Module.getValue(elapsedPtr, 'double');
|
||||||
|
|
||||||
|
Module._free(attemptsPtr);
|
||||||
|
Module._free(coinsPtr);
|
||||||
|
Module._free(hashRatePtr);
|
||||||
|
Module._free(elapsedPtr);
|
||||||
|
|
||||||
|
let limitReached = false;
|
||||||
|
let limitMessage = '';
|
||||||
|
|
||||||
|
// If checking before batch, use a small margin to prevent overshoot
|
||||||
|
const batchSize = parseInt(document.getElementById('batchSize').value);
|
||||||
|
|
||||||
|
if (maxAttempts !== null) {
|
||||||
|
if (beforeBatch && Number(attempts) + batchSize >= maxAttempts) {
|
||||||
|
limitReached = true;
|
||||||
|
limitMessage = `Maximum attempts limit reached (${maxAttempts.toLocaleString()})`;
|
||||||
|
} else if (!beforeBatch && Number(attempts) >= maxAttempts) {
|
||||||
|
limitReached = true;
|
||||||
|
limitMessage = `Maximum attempts limit reached (${maxAttempts.toLocaleString()})`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (maxTime !== null && elapsed >= maxTime) {
|
||||||
|
limitReached = true;
|
||||||
|
if (limitMessage) limitMessage += ' and ';
|
||||||
|
limitMessage += `Maximum time limit reached (${maxTime} seconds)`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (limitReached && mining) {
|
||||||
|
mining = false;
|
||||||
|
Module._stop_mining();
|
||||||
|
clearInterval(miningInterval);
|
||||||
|
clearInterval(updateInterval);
|
||||||
|
|
||||||
|
// Force immediate stats update before showing the message
|
||||||
|
setTimeout(() => {
|
||||||
|
updateStatsInternal();
|
||||||
|
|
||||||
|
const limitDiv = document.getElementById('limitReached');
|
||||||
|
limitDiv.textContent = '⚠️ Mining stopped: ' + limitMessage;
|
||||||
|
limitDiv.style.display = 'block';
|
||||||
|
|
||||||
|
let currentHTML = document.getElementById('stats').innerHTML;
|
||||||
|
document.getElementById('stats').innerHTML =
|
||||||
|
currentHTML.replace('Mining Statistics:', 'Mining Statistics (LIMIT REACHED):');
|
||||||
|
}, 10);
|
||||||
|
|
||||||
|
console.log('Mining stopped due to limit:', limitMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
return limitReached;
|
||||||
|
}
|
||||||
|
|
||||||
CoinMinerModule().then(mod => {
|
CoinMinerModule().then(mod => {
|
||||||
Module = mod;
|
Module = mod;
|
||||||
console.log('WebAssembly module loaded');
|
console.log('WebAssembly module loaded');
|
||||||
|
|
||||||
// Check if SIMD is available
|
|
||||||
simdAvailable = Module._is_simd_available();
|
simdAvailable = Module._is_simd_available();
|
||||||
const simdToggle = document.getElementById('simdToggle');
|
const simdToggle = document.getElementById('simdToggle');
|
||||||
const simdStatus = document.getElementById('simdStatus');
|
const simdStatus = document.getElementById('simdStatus');
|
||||||
|
|
@ -185,11 +306,8 @@
|
||||||
simdStatus.className = 'simd-status simd-available';
|
simdStatus.className = 'simd-status simd-available';
|
||||||
simdToggle.disabled = false;
|
simdToggle.disabled = false;
|
||||||
|
|
||||||
// SYNC LOGIC: Read C state first to match backend
|
|
||||||
const cState = Module._is_simd_enabled();
|
const cState = Module._is_simd_enabled();
|
||||||
simdToggle.checked = (cState === 1);
|
simdToggle.checked = (cState === 1);
|
||||||
|
|
||||||
// Force sync again just to be safe
|
|
||||||
Module._set_simd_enabled(simdToggle.checked ? 1 : 0);
|
Module._set_simd_enabled(simdToggle.checked ? 1 : 0);
|
||||||
console.log(`JS: Initialized - SIMD Available, Toggle set to ${simdToggle.checked}`);
|
console.log(`JS: Initialized - SIMD Available, Toggle set to ${simdToggle.checked}`);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -201,16 +319,12 @@
|
||||||
console.log('JS: Initialized - SIMD Not Available');
|
console.log('JS: Initialized - SIMD Not Available');
|
||||||
}
|
}
|
||||||
|
|
||||||
// SIMD toggle handler
|
|
||||||
simdToggle.onchange = () => {
|
simdToggle.onchange = () => {
|
||||||
if (simdAvailable) {
|
if (simdAvailable) {
|
||||||
const newState = simdToggle.checked ? 1 : 0;
|
const newState = simdToggle.checked ? 1 : 0;
|
||||||
console.log(`JS: User toggled SIMD to ${newState}`);
|
console.log(`JS: User toggled SIMD to ${newState}`);
|
||||||
|
|
||||||
// Call the C function
|
|
||||||
Module._set_simd_enabled(newState);
|
Module._set_simd_enabled(newState);
|
||||||
|
|
||||||
// Update status display
|
|
||||||
const currentMode = simdToggle.checked ? 'SIMD Mode' : 'Scalar Mode';
|
const currentMode = simdToggle.checked ? 'SIMD Mode' : 'Scalar Mode';
|
||||||
if (!mining) {
|
if (!mining) {
|
||||||
document.getElementById('stats').innerHTML =
|
document.getElementById('stats').innerHTML =
|
||||||
|
|
@ -221,10 +335,55 @@
|
||||||
|
|
||||||
document.getElementById('start').onclick = () => {
|
document.getElementById('start').onclick = () => {
|
||||||
if (!mining) {
|
if (!mining) {
|
||||||
|
// If reset flag is set, reinitialize everything
|
||||||
|
if (needsReset) {
|
||||||
|
// Stop first to ensure everything is halted
|
||||||
|
Module._stop_mining();
|
||||||
|
// Small delay to ensure stop takes effect
|
||||||
|
setTimeout(() => {
|
||||||
|
Module._reset_mining();
|
||||||
|
}, 50);
|
||||||
|
lastDisplayedCoinCount = 0;
|
||||||
|
miningStartTime = 0;
|
||||||
|
needsReset = false;
|
||||||
|
|
||||||
|
// Wait a bit before starting mining
|
||||||
|
setTimeout(() => {
|
||||||
|
const maxAttemptsInput = document.getElementById('maxAttempts').value;
|
||||||
|
const maxTimeInput = document.getElementById('maxTime').value;
|
||||||
|
|
||||||
|
maxAttempts = maxAttemptsInput ? parseInt(maxAttemptsInput) : null;
|
||||||
|
maxTime = maxTimeInput ? parseInt(maxTimeInput) : null;
|
||||||
|
|
||||||
|
document.getElementById('limitReached').style.display = 'none';
|
||||||
|
|
||||||
mining = true;
|
mining = true;
|
||||||
Module._resume_mining();
|
Module._resume_mining();
|
||||||
pausedStats = false;
|
pausedStats = false;
|
||||||
|
miningStartTime = Date.now();
|
||||||
|
console.log('Starting mining after reset...');
|
||||||
|
if (maxAttempts) console.log('Max attempts:', maxAttempts);
|
||||||
|
if (maxTime) console.log('Max time:', maxTime, 'seconds');
|
||||||
|
startMining();
|
||||||
|
}, 100);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const maxAttemptsInput = document.getElementById('maxAttempts').value;
|
||||||
|
const maxTimeInput = document.getElementById('maxTime').value;
|
||||||
|
|
||||||
|
maxAttempts = maxAttemptsInput ? parseInt(maxAttemptsInput) : null;
|
||||||
|
maxTime = maxTimeInput ? parseInt(maxTimeInput) : null;
|
||||||
|
|
||||||
|
document.getElementById('limitReached').style.display = 'none';
|
||||||
|
|
||||||
|
mining = true;
|
||||||
|
Module._resume_mining();
|
||||||
|
pausedStats = false;
|
||||||
|
miningStartTime = Date.now();
|
||||||
console.log('Starting mining...');
|
console.log('Starting mining...');
|
||||||
|
if (maxAttempts) console.log('Max attempts:', maxAttempts);
|
||||||
|
if (maxTime) console.log('Max time:', maxTime, 'seconds');
|
||||||
startMining();
|
startMining();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -235,7 +394,7 @@
|
||||||
clearInterval(miningInterval);
|
clearInterval(miningInterval);
|
||||||
clearInterval(updateInterval);
|
clearInterval(updateInterval);
|
||||||
|
|
||||||
updateStats();
|
updateStatsInternal();
|
||||||
|
|
||||||
let currentHTML = document.getElementById('stats').innerHTML;
|
let currentHTML = document.getElementById('stats').innerHTML;
|
||||||
document.getElementById('stats').innerHTML =
|
document.getElementById('stats').innerHTML =
|
||||||
|
|
@ -246,19 +405,34 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
document.getElementById('reset').onclick = () => {
|
document.getElementById('reset').onclick = () => {
|
||||||
Module._reset_mining();
|
// Stop mining completely - use aggressive stopping
|
||||||
mining = false;
|
mining = false;
|
||||||
pausedStats = false;
|
|
||||||
clearInterval(miningInterval);
|
clearInterval(miningInterval);
|
||||||
clearInterval(updateInterval);
|
clearInterval(updateInterval);
|
||||||
|
|
||||||
|
// Multiple stop calls to ensure it really stops
|
||||||
|
Module._stop_mining();
|
||||||
|
Module._stop_mining();
|
||||||
|
Module._stop_mining();
|
||||||
|
|
||||||
|
// Set the reset flag so next start will reinitialize
|
||||||
|
needsReset = true;
|
||||||
|
|
||||||
|
// Clear JavaScript state
|
||||||
|
pausedStats = false;
|
||||||
lastDisplayedCoinCount = 0;
|
lastDisplayedCoinCount = 0;
|
||||||
|
maxAttempts = null;
|
||||||
|
maxTime = null;
|
||||||
|
miningStartTime = 0;
|
||||||
|
|
||||||
|
document.getElementById('limitReached').style.display = 'none';
|
||||||
|
|
||||||
const mode = simdToggle.checked ? 'SIMD' : 'Scalar';
|
const mode = simdToggle.checked ? 'SIMD' : 'Scalar';
|
||||||
document.getElementById('stats').innerHTML =
|
document.getElementById('stats').innerHTML =
|
||||||
`Reset complete. Using ${mode} mode. Click Start to begin.`;
|
`Reset complete. Using ${mode} mode. Click Start to begin.`;
|
||||||
document.getElementById('coins').innerHTML = '';
|
document.getElementById('coins').innerHTML = '';
|
||||||
document.getElementById('coin-count').textContent = '0';
|
document.getElementById('coin-count').textContent = '0';
|
||||||
console.log('Mining reset');
|
console.log('Mining reset - will reinitialize on next start');
|
||||||
};
|
};
|
||||||
|
|
||||||
document.getElementById('clearCoins').onclick = () => {
|
document.getElementById('clearCoins').onclick = () => {
|
||||||
|
|
@ -315,7 +489,8 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateStats() {
|
// Assign to outer scope variable
|
||||||
|
updateStatsInternal = function() {
|
||||||
const attemptsPtr = Module._malloc(8);
|
const attemptsPtr = Module._malloc(8);
|
||||||
const coinsPtr = Module._malloc(4);
|
const coinsPtr = Module._malloc(4);
|
||||||
const hashRatePtr = Module._malloc(8);
|
const hashRatePtr = Module._malloc(8);
|
||||||
|
|
@ -349,15 +524,32 @@
|
||||||
|
|
||||||
const mode = Module._is_simd_enabled() ? 'SIMD' : 'Scalar';
|
const mode = Module._is_simd_enabled() ? 'SIMD' : 'Scalar';
|
||||||
|
|
||||||
|
let limitsInfo = '';
|
||||||
|
if (maxAttempts !== null || maxTime !== null) {
|
||||||
|
limitsInfo = '<br><strong>Limits:</strong><br>';
|
||||||
|
if (maxAttempts !== null) {
|
||||||
|
const attemptsPercent = Math.min(100, (Number(attempts) / maxAttempts) * 100);
|
||||||
|
limitsInfo += `Max Attempts: ${maxAttempts.toLocaleString()} (${attemptsPercent.toFixed(1)}%)<br>`;
|
||||||
|
}
|
||||||
|
if (maxTime !== null) {
|
||||||
|
const timePercent = Math.min(100, (elapsed / maxTime) * 100);
|
||||||
|
limitsInfo += `Max Time: ${maxTime}s (${timePercent.toFixed(1)}%)`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
document.getElementById('stats').innerHTML = `
|
document.getElementById('stats').innerHTML = `
|
||||||
<strong>Mining Statistics (${mode} Mode):</strong><br>
|
<strong>Mining Statistics (${mode} Mode):</strong><br>
|
||||||
Attempts: ${attempts.toString()}<br>
|
Attempts: ${attempts.toString()}<br>
|
||||||
Coins Found: ${coins}<br>
|
Coins Found: ${coins}<br>
|
||||||
Hash Rate: ${(hashRate / 1e6).toFixed(2)} MH/s<br>
|
Hash Rate: ${(hashRate / 1e6).toFixed(2)} MH/s<br>
|
||||||
Elapsed Time: ${elapsed.toFixed(2)} seconds
|
Elapsed Time: ${elapsed.toFixed(2)} seconds${limitsInfo}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
displayNewCoins();
|
displayNewCoins();
|
||||||
|
};
|
||||||
|
|
||||||
|
function updateStats() {
|
||||||
|
updateStatsInternal();
|
||||||
}
|
}
|
||||||
|
|
||||||
function startMining() {
|
function startMining() {
|
||||||
|
|
@ -369,10 +561,23 @@
|
||||||
clearInterval(miningInterval);
|
clearInterval(miningInterval);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check limits BEFORE mining this batch
|
||||||
|
if (checkLimits()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Module._mine_coins_wasm(batchSize);
|
Module._mine_coins_wasm(batchSize);
|
||||||
|
|
||||||
|
// Check limits immediately AFTER mining this batch
|
||||||
|
if (checkLimits()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
}, 0);
|
}, 0);
|
||||||
|
|
||||||
updateInterval = setInterval(updateStats, updateMs);
|
updateInterval = setInterval(() => {
|
||||||
|
updateStats();
|
||||||
|
}, updateMs);
|
||||||
}
|
}
|
||||||
}).catch(err => {
|
}).catch(err => {
|
||||||
console.error('Failed to load WebAssembly module:', err);
|
console.error('Failed to load WebAssembly module:', err);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue