Signed-off-by: Tiago Garcia <tiago.rgarcia@ua.pt>
This commit is contained in:
Tiago Garcia 2025-11-23 22:46:25 +00:00
parent 2a178133a6
commit 7347ddd1db
Signed by: TiagoRG
GPG Key ID: DFCD48E3F420DB42
2 changed files with 244 additions and 26 deletions

View File

@ -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;
} }
} }
} }

View File

@ -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;
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; mining = true;
Module._resume_mining(); Module._resume_mining();
pausedStats = false; 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);