diff --git a/aad_coin_miner_wasm.c b/aad_coin_miner_wasm.c index e2bbf07..035e15b 100644 --- a/aad_coin_miner_wasm.c +++ b/aad_coin_miner_wasm.c @@ -1,7 +1,7 @@ // // Arquiteturas de Alto Desempenho 2025/2026 // -// DETI Coin Miner - WebAssembly implementation +// DETI Coin Miner - WebAssembly implementation with SIMD support // #include @@ -19,14 +19,20 @@ #include "aad_vault.h" #endif +// WASM SIMD support +#if defined(__wasm_simd128__) +#include +#endif + // Global mining state static volatile int keep_running = 1; +static volatile int use_simd = 0; static u64_t total_attempts = 0; static u32_t coins_found = 0; static double mining_start_time = 0; -static double pause_time_offset = 0; // Track paused time -static double last_pause_time = 0; // When mining was paused -static u32_t found_coins[1024][14]; // Store up to 1024 found coins +static double pause_time_offset = 0; +static double last_pause_time = 0; +static u32_t found_coins[1024][14]; static u32_t found_coins_count = 0; // @@ -82,6 +88,185 @@ static double get_time() #endif } +#if defined(__wasm_simd128__) +// +// Helper macro for Left Rotate (ROTL) using SIMD shifts +// CORRECTION: Used wasm_u32x4_shr instead of incorrect wasm_i32x4_shr_u +// +#define SIMD_ROTL(x, n) wasm_v128_or(wasm_i32x4_shl(x, n), wasm_u32x4_shr(x, 32 - n)) + +// +// SIMD implementation for WebAssembly (4-way parallel) +// +static void prepare_coins_simd(u32_t base_coin[14], u32_t *interleaved_data) +{ + const int SIMD_WIDTH = 4; + for(int lane = 0; lane < SIMD_WIDTH; lane++) + { + u32_t coin[14]; + memcpy(coin, base_coin, sizeof(coin)); + + for(int idx = 0; idx < 14; idx++) + { + interleaved_data[idx * SIMD_WIDTH + lane] = coin[idx]; + } + + increment_coin(base_coin); + } +} + +static void extract_hashes_simd(u32_t *interleaved_hash, u32_t hashes[][5]) +{ + const int SIMD_WIDTH = 4; + for(int lane = 0; lane < SIMD_WIDTH; lane++) + { + for(int idx = 0; idx < 5; idx++) + { + hashes[lane][idx] = interleaved_hash[idx * SIMD_WIDTH + lane]; + } + } +} + +static void extract_coins_simd(u32_t *interleaved_data, u32_t coins[][14]) +{ + const int SIMD_WIDTH = 4; + for(int lane = 0; lane < SIMD_WIDTH; lane++) + { + for(int idx = 0; idx < 14; idx++) + { + coins[lane][idx] = interleaved_data[idx * SIMD_WIDTH + lane]; + } + } +} + +static void sha1_wasm_simd(u32_t *interleaved_data, u32_t *interleaved_hash) +{ + const int SIMD_WIDTH = 4; + + // SHA1 State vectors + v128_t a, b, c, d, e; + v128_t w[80]; // Message schedule + + // Initial SHA1 constants + a = wasm_i32x4_splat(0x67452301u); + b = wasm_i32x4_splat(0xEFCDAB89u); + c = wasm_i32x4_splat(0x98BADCFEu); + d = wasm_i32x4_splat(0x10325476u); + e = wasm_i32x4_splat(0xC3D2E1F0u); + + // 1. Prepare Message Schedule (Interleaved loads) + // Load first 14 words + for(int i = 0; i < 14; i++) + { + w[i] = wasm_v128_load(&interleaved_data[i * SIMD_WIDTH]); + } + // Standard padding (assumed handled by caller/init) + w[14] = wasm_i32x4_splat(0); + w[15] = wasm_i32x4_splat(440); // Length in bits (55 bytes * 8) + + // Expand message schedule from 16 to 80 + for (int i = 16; i < 80; i++) { + v128_t temp = wasm_v128_xor(w[i-3], w[i-8]); + temp = wasm_v128_xor(temp, w[i-14]); + temp = wasm_v128_xor(temp, w[i-16]); + w[i] = SIMD_ROTL(temp, 1); + } + + // 2. Main Loop (80 rounds) + for (int i = 0; i < 80; i++) { + v128_t f, k; + + if (i < 20) { + // F1: (b & c) | (~b & d) + f = wasm_v128_or(wasm_v128_and(b, c), wasm_v128_and(wasm_v128_not(b), d)); + k = wasm_i32x4_splat(0x5A827999u); + } else if (i < 40) { + // F2: b ^ c ^ d + f = wasm_v128_xor(wasm_v128_xor(b, c), d); + k = wasm_i32x4_splat(0x6ED9EBA1u); + } else if (i < 60) { + // F3: (b & c) | (b & d) | (c & d) + f = wasm_v128_or(wasm_v128_or(wasm_v128_and(b, c), wasm_v128_and(b, d)), wasm_v128_and(c, d)); + k = wasm_i32x4_splat(0x8F1BBCDCu); + } else { + // F4: b ^ c ^ d + f = wasm_v128_xor(wasm_v128_xor(b, c), d); + k = wasm_i32x4_splat(0xCA62C1D6u); + } + + // temp = ROTL(a, 5) + f + e + k + w[i] + v128_t temp = wasm_i32x4_add(SIMD_ROTL(a, 5), f); + temp = wasm_i32x4_add(temp, e); + temp = wasm_i32x4_add(temp, k); + temp = wasm_i32x4_add(temp, w[i]); + + e = d; + d = c; + c = SIMD_ROTL(b, 30); + b = a; + a = temp; + } + + // 3. Add to initial state and store + v128_t h0 = wasm_i32x4_add(a, wasm_i32x4_splat(0x67452301u)); + v128_t h1 = wasm_i32x4_add(b, wasm_i32x4_splat(0xEFCDAB89u)); + v128_t h2 = wasm_i32x4_add(c, wasm_i32x4_splat(0x98BADCFEu)); + v128_t h3 = wasm_i32x4_add(d, wasm_i32x4_splat(0x10325476u)); + v128_t h4 = wasm_i32x4_add(e, wasm_i32x4_splat(0xC3D2E1F0u)); + + // Store results back to interleaved_hash + wasm_v128_store(&interleaved_hash[0 * SIMD_WIDTH], h0); + wasm_v128_store(&interleaved_hash[1 * SIMD_WIDTH], h1); + wasm_v128_store(&interleaved_hash[2 * SIMD_WIDTH], h2); + wasm_v128_store(&interleaved_hash[3 * SIMD_WIDTH], h3); + wasm_v128_store(&interleaved_hash[4 * SIMD_WIDTH], h4); +} + +static int mine_coins_wasm_simd_internal(u32_t iterations_per_call, u32_t coin[14]) +{ + const int SIMD_WIDTH = 4; + u32_t interleaved_data[14 * SIMD_WIDTH] __attribute__((aligned(16))); + u32_t interleaved_hash[5 * SIMD_WIDTH] __attribute__((aligned(16))); + + u32_t batches = (iterations_per_call + SIMD_WIDTH - 1) / SIMD_WIDTH; + + for(u32_t batch = 0; batch < batches && keep_running; batch++) + { + prepare_coins_simd(coin, interleaved_data); + + // This now uses the REAL vectorized implementation + sha1_wasm_simd(interleaved_data, interleaved_hash); + + total_attempts += SIMD_WIDTH; + + u32_t hashes[SIMD_WIDTH][5]; + extract_hashes_simd(interleaved_hash, hashes); + + for(int lane = 0; lane < SIMD_WIDTH; lane++) + { + if(is_valid_coin(hashes[lane])) + { + if(found_coins_count < 1024) + { + u32_t coins[SIMD_WIDTH][14]; + extract_coins_simd(interleaved_data, coins); + memcpy(found_coins[found_coins_count], coins[lane], sizeof(coins[lane])); + found_coins_count++; + } + coins_found++; + +#ifndef __EMSCRIPTEN__ + printf("COIN FOUND! (attempt %llu, lane %d)\n", + (unsigned long long)(total_attempts - SIMD_WIDTH + lane), lane); +#endif + } + } + } + + return keep_running; +} +#endif + // // Main mining iteration (called from JavaScript) // @@ -92,6 +277,8 @@ int mine_coins_wasm(u32_t iterations_per_call) { static u32_t coin[14]; static int initialized = 0; + static int last_logged_mode = -1; // Track last reported mode + u32_t hash[5]; // Initialize coin template on first call @@ -123,7 +310,23 @@ int mine_coins_wasm(u32_t iterations_per_call) if(!keep_running) return 0; - // Mine for the specified number of iterations +#if defined(__wasm_simd128__) + if(use_simd) + { + if (last_logged_mode != 1) { + printf("C: Running in SIMD Mode (Vectorized)\n"); + last_logged_mode = 1; + } + return mine_coins_wasm_simd_internal(iterations_per_call, coin); + } +#endif + + if (last_logged_mode != 0) { + printf("C: Running in Scalar Mode\n"); + last_logged_mode = 0; + } + + // Scalar implementation for(u32_t i = 0; i < iterations_per_call && keep_running; i++) { sha1(coin, hash); @@ -165,10 +368,8 @@ void get_statistics(u64_t *attempts, u32_t *coins, double *hash_rate, double *el double actual_elapsed; if(!keep_running && last_pause_time > 0) { - // If paused, use the paused time actual_elapsed = last_pause_time - mining_start_time - pause_time_offset; } else { - // If running, calculate normally actual_elapsed = current_time - mining_start_time - pause_time_offset; } @@ -176,6 +377,44 @@ void get_statistics(u64_t *attempts, u32_t *coins, double *hash_rate, double *el *hash_rate = (actual_elapsed > 0) ? (total_attempts / actual_elapsed) : 0; } +// +// Enable/disable SIMD +// +#ifdef __EMSCRIPTEN__ +EMSCRIPTEN_KEEPALIVE +#endif +void set_simd_enabled(int enabled) +{ + use_simd = enabled; + printf("C: SIMD mode set to: %d\n", use_simd); +} + +// +// Check if SIMD is available +// +#ifdef __EMSCRIPTEN__ +EMSCRIPTEN_KEEPALIVE +#endif +int is_simd_available() +{ +#if defined(__wasm_simd128__) + return 1; +#else + return 0; +#endif +} + +// +// Get current SIMD state +// +#ifdef __EMSCRIPTEN__ +EMSCRIPTEN_KEEPALIVE +#endif +int is_simd_enabled() +{ + return use_simd; +} + // // Stop mining // @@ -207,7 +446,7 @@ void resume_mining() } // -// Get found coin data (returns pointer to coin array) +// Get found coin data // #ifdef __EMSCRIPTEN__ EMSCRIPTEN_KEEPALIVE @@ -259,6 +498,12 @@ int main(int argc, char *argv[]) max_attempts = strtoull(argv[1], NULL, 10); printf("Mining DETI coins using WebAssembly implementation (standalone mode)...\n"); +#if defined(__wasm_simd128__) + printf("SIMD support: available\n"); + use_simd = 1; +#else + printf("SIMD support: not available\n"); +#endif printf("Press Ctrl+C to stop\n\n"); time_measurement(); diff --git a/index.html b/index.html index d6ed4d9..48f824f 100644 --- a/index.html +++ b/index.html @@ -13,6 +13,11 @@ padding: 10px 20px; margin: 5px; font-size: 16px; + cursor: pointer; + } + button:disabled { + opacity: 0.5; + cursor: not-allowed; } #stats { margin-top: 20px; @@ -56,22 +61,96 @@ .coin-data { color: #000080; } + .simd-toggle { + display: inline-flex; + align-items: center; + gap: 10px; + } + .toggle-switch { + position: relative; + display: inline-block; + width: 60px; + height: 34px; + } + .toggle-switch input { + opacity: 0; + width: 0; + height: 0; + } + .slider { + position: absolute; + cursor: pointer; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: #ccc; + transition: .4s; + border-radius: 34px; + } + .slider:before { + position: absolute; + content: ""; + height: 26px; + width: 26px; + left: 4px; + bottom: 4px; + background-color: white; + transition: .4s; + border-radius: 50%; + } + input:checked + .slider { + background-color: #2196F3; + } + input:checked + .slider:before { + transform: translateX(26px); + } + input:disabled + .slider { + background-color: #ddd; + cursor: not-allowed; + } + .simd-status { + font-weight: bold; + } + .simd-available { + color: #28a745; + } + .simd-unavailable { + color: #dc3545; + } + .button-group { + margin: 15px 0; + }

DETI Coin Miner (WebAssembly)

+ +
+ + + +
+
- +
+
- + +
+ +
+ + + +
- - - -
Waiting to start... @@ -90,11 +169,56 @@ let updateInterval; let lastDisplayedCoinCount = 0; let pausedStats = false; + let simdAvailable = false; 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'); + + if (simdAvailable) { + simdStatus.textContent = 'SIMD Available'; + 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 { + simdStatus.textContent = 'SIMD Not Available'; + simdStatus.className = 'simd-status simd-unavailable'; + simdToggle.disabled = true; + simdToggle.checked = false; + Module._set_simd_enabled(0); + 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 = + `${currentMode} selected. Click Start to begin mining.`; + } + } + }; + document.getElementById('start').onclick = () => { if (!mining) { mining = true; @@ -114,10 +238,10 @@ updateStats(); let currentHTML = document.getElementById('stats').innerHTML; - document.getElementById('stats').innerHTML = currentHTML.replace('Mining Statistics:', 'Mining Statistics (PAUSED):'); + document.getElementById('stats').innerHTML = + currentHTML.replace('Mining Statistics:', 'Mining Statistics (PAUSED):'); pausedStats = true; - console.log('Mining stopped'); }; @@ -128,7 +252,10 @@ clearInterval(miningInterval); clearInterval(updateInterval); lastDisplayedCoinCount = 0; - document.getElementById('stats').innerHTML = 'Reset complete. Click Start to begin.'; + + 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'); @@ -175,7 +302,8 @@ const timestamp = new Date().toLocaleTimeString(); const entry = document.createElement('div'); - entry.innerHTML = `[${timestamp}] Coin #${i + 1}: ${coinStr}`; + entry.innerHTML = + `[${timestamp}] Coin #${i + 1}: ${coinStr}`; coinsDiv.appendChild(entry); coinsDiv.scrollTop = coinsDiv.scrollHeight; @@ -193,23 +321,18 @@ const hashRatePtr = Module._malloc(8); const elapsedPtr = Module._malloc(8); - // Zero-initialize the memory before calling Module.setValue(attemptsPtr, 0, 'i32'); Module.setValue(attemptsPtr + 4, 0, 'i32'); Module._get_statistics(attemptsPtr, coinsPtr, hashRatePtr, elapsedPtr); - // Read 64-bit unsigned value correctly - // On little-endian, low 32 bits come first const attemptsLowUnsigned = Module.getValue(attemptsPtr, 'i32') >>> 0; const attemptsHighUnsigned = Module.getValue(attemptsPtr + 4, 'i32') >>> 0; - // Combine - for display purposes, if high part is 0, just show low part let attempts; if (attemptsHighUnsigned === 0) { attempts = attemptsLowUnsigned; } else { - // Use BigInt for values > 32 bits const low = BigInt(attemptsLowUnsigned); const high = BigInt(attemptsHighUnsigned); attempts = (high * BigInt(4294967296)) + low; @@ -224,8 +347,10 @@ Module._free(hashRatePtr); Module._free(elapsedPtr); + const mode = Module._is_simd_enabled() ? 'SIMD' : 'Scalar'; + document.getElementById('stats').innerHTML = ` - Mining Statistics:
+ Mining Statistics (${mode} Mode):
Attempts: ${attempts.toString()}
Coins Found: ${coins}
Hash Rate: ${(hashRate / 1e6).toFixed(2)} MH/s
diff --git a/makefile b/makefile index 1a5feef..c4c0298 100644 --- a/makefile +++ b/makefile @@ -86,7 +86,7 @@ coin_miner_cuda: aad_coin_miner_cuda.c coin_miner_cuda_kernel.cubin aad_sha1.h a cc -march=native -Wall -Wshadow -Werror -O3 -I$(CUDA_DIR)/include $< -o $@ -lcuda coin_miner_wasm: aad_coin_miner_wasm.c aad_sha1.h aad_sha1_cpu.h aad_sha1_wasm.h aad_data_types.h aad_utilities.h aad_vault.h makefile - emcc -O3 -flto -o coin_miner_wasm.js aad_coin_miner_wasm.c \ + emcc -O3 -flto -msimd128 -o coin_miner_wasm.js aad_coin_miner_wasm.c \ -s WASM=1 \ -s EXPORTED_FUNCTIONS='["_mine_coins_wasm","_get_statistics","_stop_mining","_reset_mining","_get_found_coin","_get_found_coins_count","_malloc","_free"]' \ -s EXPORTED_RUNTIME_METHODS='["cwrap","ccall","getValue","setValue"]' \