<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<meta name="author" content="TransparentLC">
<title>WebAssembly RC4 Benchmark</title>
</head>
<body>
<h1>WebAssembly RC4 Benchmark</h1>
Data Length (Bytes):
<input id="length" type="number" value="16777216">
<button id="run">Run</button>
<pre id="result"></pre>
<script>
(async () => {
const wasmMemory = new WebAssembly.Memory({ initial: 1 });
const wasmModule = (await WebAssembly.instantiateStreaming(
fetch('data:application/wasm;base64,AGFzbQEAAAAADQZkeWxpbmuAAgQAAAAADQZkeWxpbmuAAgQAAAABCwJgAABgBH9/f38AAiQCA2Vudg1fX21lbW9yeV9iYXNlA38AA2VudgZtZW1vcnkCAAADAwIAAQYGAX8AQQALB0EEE19fd2FzbV9hcHBseV9yZWxvY3MAAANyYzQAAQxfX2Rzb19oYW5kbGUDARJfX3Bvc3RfaW5zdGFudGlhdGUAAAr1AQIDAAEL7gEBBX8DQCMAIAVqIAQ6AAAgBEEBaiEEIAVBAWoiBUGAAkcNAAtBACEFA0AjACIEIAZqIgcgBCAAIAYgAnBqLQAAIActAAAiByAFQf8BcWpqIgVB/wFxaiIELQAAOgAAIAQgBzoAACAGQQFqIgZBgAJHDQALIAMEQEEAIQZBACEAQQAhAgNAIwAiBSACQQFqQf8BcSICaiIEIAUgBC0AACIHIABB/wFxaiIAQf8BcWoiCC0AADoAACAIIAc6AAAgASAGaiIIIAgtAAAgBSAHIAQtAABqQf8BcWotAABzOgAAIAZBAWoiBiADRw0ACwsLC4cCAQAjAAuAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA='),
{
env: {
memory: wasmMemory,
__memory_base: 0,
},
}
)).instance;
/**
* @param {Uint8Array} key
* @param {Uint8Array} input
* @returns {Uint8Array}
*/
const wasmRc4 = (key, input) => {
const sboxOffset = 256;
const memoryRequired = key.length + input.length + sboxOffset;
if (memoryRequired > wasmMemory.buffer.byteLength) {
wasmMemory.grow(Math.ceil((memoryRequired - wasmMemory.buffer.byteLength) / 65536));
}
const wasmBuffer = new Uint8Array(wasmMemory.buffer);
wasmBuffer.set(key, sboxOffset);
wasmBuffer.set(input, sboxOffset + key.length);
wasmModule.exports.rc4(sboxOffset, sboxOffset + key.length, key.length, input.length);
return new Uint8Array(wasmBuffer.slice(sboxOffset + key.length, sboxOffset + key.length + input.length));
}
/**
* @param {Uint8Array} key
* @param {Uint8Array} input
* @returns {Uint8Array}
*/
const jsRc4 = (key, input) => {
let i = 0;
let j = 0;
const keyLength = key.length;
const inputLength = input.length;
const sbox = new Uint8Array(256);
for (i = 0; i < 256; ++i) sbox[i] = i;
for (i = 0; i < 256; ++i) {
j = (j + sbox[i] + key[i % keyLength]) & 0xFF;
[sbox[i], sbox[j]] = [sbox[j], sbox[i]];
}
const output = new Uint8Array(inputLength);
i = j = 0;
for (let k = 0; k < inputLength; ++k) {
i = (i + 1) & 0xFF;
j = (j + sbox[i]) & 0xFF;
[sbox[i], sbox[j]] = [sbox[j], sbox[i]];
output[k] = input[k] ^ sbox[(sbox[i] + sbox[j]) & 0xFF];
}
return output;
}
document.getElementById('run').onclick = () => {
const inputLength = parseInt(document.getElementById('length').value);
if (inputLength <= 0) return;
const rc4Key = new Uint8Array(1024).map(() => Math.random() * 256);
const rc4Input = new Uint8Array(inputLength).map(() => Math.random() * 256);
performance.mark('wasm-start');
const wasmEncrypted = wasmRc4(rc4Key, rc4Input);
performance.mark('wasm-end');
performance.mark('js-start');
const jsEncrypted = jsRc4(rc4Key, rc4Input);
performance.mark('js-end');
performance.measure('wasm', 'wasm-start', 'wasm-end');
performance.measure('js', 'js-start', 'js-end');
for (let j = 0; j < inputLength; j++) if (wasmEncrypted[j] !== jsEncrypted[j]) throw 'Not equal';
const result = {
length: inputLength,
wasmTime: performance.getEntriesByName('wasm')[0].duration,
jsTime: performance.getEntriesByName('js')[0].duration,
};
result.wasmSpeed = result.length / result.wasmTime;
result.jsSpeed = result.length / result.jsTime;
result.ratio = result.wasmSpeed / result.jsSpeed;
const r2 = x => Math.round(x * 100) / 100;
document.getElementById('result').innerText = [
'Length: ' + result.length + ' Bytes',
'WASM time: ' + r2(result.wasmTime) + ' ms',
'JS time (ms): ' + r2(result.jsTime) + ' ms',
'WASM speed: ' + r2(result.wasmSpeed) + ' Bytes/ms',
'JS speed: ' + r2(result.jsSpeed) + ' Bytes/ms',
'Speed up: ' + r2(result.ratio) + 'x',
].join('\n');
performance.clearMarks();
performance.clearMeasures();
}
})()
</script>
</body>
</html>
Output
You can jump to the latest bin by adding /latest
to your URL
Keyboard Shortcuts
Shortcut | Action |
---|---|
ctrl + [num] | Toggle nth panel |
ctrl + 0 | Close focused panel |
ctrl + enter | Re-render output. If console visible: run JS in console |
Ctrl + l | Clear the console |
ctrl + / | Toggle comment on selected lines |
ctrl + ] | Indents selected lines |
ctrl + [ | Unindents selected lines |
tab | Code complete & Emmet expand |
ctrl + shift + L | Beautify code in active panel |
ctrl + s | Save & lock current Bin from further changes |
ctrl + shift + s | Open the share options |
ctrl + y | Archive Bin |
Complete list of JS Bin shortcuts |
JS Bin URLs
URL | Action |
---|---|
/ | Show the full rendered output. This content will update in real time as it's updated from the /edit url. |
/edit | Edit the current bin |
/watch | Follow a Code Casting session |
/embed | Create an embeddable version of the bin |
/latest | Load the very latest bin (/latest goes in place of the revision) |
/[username]/last | View the last edited bin for this user |
/[username]/last/edit | Edit the last edited bin for this user |
/[username]/last/watch | Follow the Code Casting session for the latest bin for this user |
/quiet | Remove analytics and edit button from rendered output |
.js | Load only the JavaScript for a bin |
.css | Load only the CSS for a bin |
Except for username prefixed urls, the url may start with http://jsbin.com/abc and the url fragments can be added to the url to view it differently. |