From 7347ddd1db8cd93bae65775a46d677d462108bb5 Mon Sep 17 00:00:00 2001 From: Tiago Garcia Date: Sun, 23 Nov 2025 22:46:25 +0000 Subject: [PATCH] updates Signed-off-by: Tiago Garcia --- aad_coin_miner_simd.c | 35 +++++-- index.html | 235 +++++++++++++++++++++++++++++++++++++++--- 2 files changed, 244 insertions(+), 26 deletions(-) diff --git a/aad_coin_miner_simd.c b/aad_coin_miner_simd.c index f96fccc..d8af5a1 100644 --- a/aad_coin_miner_simd.c +++ b/aad_coin_miner_simd.c @@ -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"); double start_time = get_wall_time(); + int should_stop = 0; #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++) ((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 - if(max_attempts > 0 && attempts >= max_attempts) + // Check stopping conditions (check shared flag first) + if(should_stop) break; + if(max_attempts > 0 && attempts >= max_attempts) + { + should_stop = 1; + break; + } + double elapsed_time = get_wall_time() - start_time; if(max_time > 0 && elapsed_time >= max_time) + { + should_stop = 1; break; + } prepare_coins(base_coin, interleaved_data, SIMD_WIDTH); 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) // Periodically update the shared counter and report - if(thread_attempts % 100000 < SIMD_WIDTH) + if(thread_attempts >= 100000) { #pragma omp atomic attempts += thread_attempts; thread_attempts = 0; - #pragma omp barrier - - #pragma omp master + // Only master thread reports progress (no barrier to avoid blocking) + if(thread_id == 0) { - 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 rate = attempts / elapsed; + double rate = current_attempts / elapsed; printf("Attempts: %llu, Rate: %.2f MH/s, Coins: %u, Elapsed: %.2fs\n", - (unsigned long long)attempts, rate / 1e6, coins_found, elapsed); - last_reported_attempts = attempts; + (unsigned long long)current_attempts, rate / 1e6, coins_found, elapsed); + last_reported_attempts = current_attempts; } } } diff --git a/index.html b/index.html index 48f824f..58c4837 100644 --- a/index.html +++ b/index.html @@ -121,6 +121,30 @@ .button-group { 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; + } @@ -145,6 +169,20 @@ +
+

Mining Limits (Optional)

+
+ + + (Leave empty for unlimited) +
+
+ + + (Leave empty for unlimited) +
+
+
@@ -152,6 +190,8 @@
+ +
Waiting to start...
@@ -170,12 +210,93 @@ let lastDisplayedCoinCount = 0; let pausedStats = 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 => { Module = mod; console.log('WebAssembly module loaded'); - // Check if SIMD is available simdAvailable = Module._is_simd_available(); const simdToggle = document.getElementById('simdToggle'); const simdStatus = document.getElementById('simdStatus'); @@ -185,11 +306,8 @@ simdStatus.className = 'simd-status simd-available'; simdToggle.disabled = false; - // SYNC LOGIC: Read C state first to match backend const cState = Module._is_simd_enabled(); simdToggle.checked = (cState === 1); - - // Force sync again just to be safe Module._set_simd_enabled(simdToggle.checked ? 1 : 0); console.log(`JS: Initialized - SIMD Available, Toggle set to ${simdToggle.checked}`); } else { @@ -201,16 +319,12 @@ console.log('JS: Initialized - SIMD Not Available'); } - // SIMD toggle handler simdToggle.onchange = () => { if (simdAvailable) { const newState = simdToggle.checked ? 1 : 0; console.log(`JS: User toggled SIMD to ${newState}`); - - // Call the C function Module._set_simd_enabled(newState); - // Update status display const currentMode = simdToggle.checked ? 'SIMD Mode' : 'Scalar Mode'; if (!mining) { document.getElementById('stats').innerHTML = @@ -221,10 +335,55 @@ document.getElementById('start').onclick = () => { 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; + Module._resume_mining(); + 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...'); + if (maxAttempts) console.log('Max attempts:', maxAttempts); + if (maxTime) console.log('Max time:', maxTime, 'seconds'); startMining(); } }; @@ -235,7 +394,7 @@ clearInterval(miningInterval); clearInterval(updateInterval); - updateStats(); + updateStatsInternal(); let currentHTML = document.getElementById('stats').innerHTML; document.getElementById('stats').innerHTML = @@ -246,19 +405,34 @@ }; document.getElementById('reset').onclick = () => { - Module._reset_mining(); + // Stop mining completely - use aggressive stopping mining = false; - pausedStats = false; clearInterval(miningInterval); 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; + maxAttempts = null; + maxTime = null; + miningStartTime = 0; + + document.getElementById('limitReached').style.display = 'none'; const mode = simdToggle.checked ? 'SIMD' : 'Scalar'; document.getElementById('stats').innerHTML = `Reset complete. Using ${mode} mode. Click Start to begin.`; document.getElementById('coins').innerHTML = ''; document.getElementById('coin-count').textContent = '0'; - console.log('Mining reset'); + console.log('Mining reset - will reinitialize on next start'); }; document.getElementById('clearCoins').onclick = () => { @@ -315,7 +489,8 @@ } } - function updateStats() { + // Assign to outer scope variable + updateStatsInternal = function() { const attemptsPtr = Module._malloc(8); const coinsPtr = Module._malloc(4); const hashRatePtr = Module._malloc(8); @@ -349,15 +524,32 @@ const mode = Module._is_simd_enabled() ? 'SIMD' : 'Scalar'; + let limitsInfo = ''; + if (maxAttempts !== null || maxTime !== null) { + limitsInfo = '
Limits:
'; + if (maxAttempts !== null) { + const attemptsPercent = Math.min(100, (Number(attempts) / maxAttempts) * 100); + limitsInfo += `Max Attempts: ${maxAttempts.toLocaleString()} (${attemptsPercent.toFixed(1)}%)
`; + } + if (maxTime !== null) { + const timePercent = Math.min(100, (elapsed / maxTime) * 100); + limitsInfo += `Max Time: ${maxTime}s (${timePercent.toFixed(1)}%)`; + } + } + document.getElementById('stats').innerHTML = ` Mining Statistics (${mode} Mode):
Attempts: ${attempts.toString()}
Coins Found: ${coins}
Hash Rate: ${(hashRate / 1e6).toFixed(2)} MH/s
- Elapsed Time: ${elapsed.toFixed(2)} seconds + Elapsed Time: ${elapsed.toFixed(2)} seconds${limitsInfo} `; displayNewCoins(); + }; + + function updateStats() { + updateStatsInternal(); } function startMining() { @@ -369,10 +561,23 @@ clearInterval(miningInterval); return; } + + // Check limits BEFORE mining this batch + if (checkLimits()) { + return; + } + Module._mine_coins_wasm(batchSize); + + // Check limits immediately AFTER mining this batch + if (checkLimits()) { + return; + } }, 0); - updateInterval = setInterval(updateStats, updateMs); + updateInterval = setInterval(() => { + updateStats(); + }, updateMs); } }).catch(err => { console.error('Failed to load WebAssembly module:', err);