Skip to content

Instantly share code, notes, and snippets.

@w8r
Last active August 1, 2018 09:13
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save w8r/82fd0680111b09f08b8d688cb0de545b to your computer and use it in GitHub Desktop.
Save w8r/82fd0680111b09f08b8d688cb0de545b to your computer and use it in GitHub Desktop.
Transferrable objects demo
license: mit
height: 500
border: no
body{padding:2em;font-family:"Open Sans",Helvetica,Arial,sans-serif}#result{max-height:500px;overflow:auto}button{padding:15px;border-radius:5px}
!function(n){function e(c){if(t[c])return t[c].exports;var g=t[c]={i:c,l:!1,exports:{}};return n[c].call(g.exports,g,g.exports,e),g.l=!0,g.exports}var t={};e.m=n,e.c=t,e.i=function(n){return n},e.d=function(n,t,c){e.o(n,t)||Object.defineProperty(n,t,{configurable:!1,enumerable:!0,get:c})},e.n=function(n){var t=n&&n.__esModule?function(){return n.default}:function(){return n};return e.d(t,"a",t),t},e.o=function(n,e){return Object.prototype.hasOwnProperty.call(n,e)},e.p="",e(e.s=0)}([function(module,exports,__webpack_require__){"use strict";eval("\n\n// set some constants/vars\nvar SIZE = 1024 * 1024 * 32; // 32MB for our data\nvar arrayBuffer = null;\nvar uInt8View = null;\nvar originalLength = null;\n\n// build our example array of 32MB numbers\n// later in the worker we will work on them with some simple math operations\nfunction setupArray() {\n arrayBuffer = new ArrayBuffer(SIZE);\n uInt8View = new Uint8Array(arrayBuffer);\n originalLength = uInt8View.length;\n\n for (var i = 0; i < originalLength; ++i) {\n uInt8View[i] = i;\n }\n\n log(source() + 'filled ' + toMB(originalLength) + ' MB buffer');\n}\n\n//\n// helper functions to measure performance\n//\n\n// return time stemp\nfunction time() {\n var now = new Date();\n var time = /(\\d+:\\d+:\\d+)/.exec(now)[0] + ':';\n for (var ms = String(now.getMilliseconds()), i = ms.length - 3; i < 0; ++i) {\n time += '0';\n }\n return time + ms;\n}\n\n// We are now our page (on the worker will have some nice RED color for the answers)\nfunction source() {\n return '<span style=\"color:green;\">Our page:</span> ';\n}\n\nfunction seconds(since) {\n return (new Date() - since) / 1000.0;\n}\n\nfunction toMB(bytes) {\n return Math.round(bytes / 1024 / 1024);\n}\n\n//\n// Initial\nvar worker = null;\nvar startTime = 0;\nvar supported = false;\n\n// Move output panel down further if <details> isn't supported (collapsible).\nvar details = document.querySelector('details');\nif (!('open' in details)) {\n document.querySelector('section').classList.add('down');\n}\n\nfunction log(str) {\n var elem = document.getElementById('result');\n var log = function log(s) {\n elem.innerHTML += ''.concat(time(), ' ', s, '\\n');\n };\n log(str);\n}\n\nfunction init() {\n worker = new Worker('worker.js');\n\n // Take care of vendor prefixes.\n worker.postMessage = worker.webkitPostMessage || worker.postMessage;\n\n worker.onmessage = function (e) {\n console.timeEnd('actual postMessage round trip was');\n // capture elapsed time since the original postMessage();\n if (!e.data.type) {\n var elapsed = seconds(startTime);\n }\n\n var data = e.data;\n\n if (data.type && data.type == 'debug') {\n log(data.msg);\n } else {\n if (data.copy) {\n data.byteLength = data.ourArray.byteLength;\n }\n var rate = Math.round(toMB(data.byteLength) / elapsed);\n log(source() + 'postMessage roundtrip took: ' + elapsed * 1000 + ' ms');\n log(source() + 'postMessage roundtrip rate: ' + rate + ' MB/s');\n }\n };\n\n log(source() + 'We are good to go!');\n}\n\nfunction test(useIt) {\n var useTransferrable = useIt;\n setupArray(); // Need to do this on every run for the repeated runs with transferable arrays. They're cleared out after they're transferred.\n\n startTime = new Date();\n console.time('actual postMessage round trip was');\n\n if (useTransferrable) {\n console.log(\"## Using Transferrable object method on size: \" + uInt8View.length);\n worker.postMessage(uInt8View.buffer, [uInt8View.buffer]);\n } else {\n console.log(\"## Using old COPY method on size: \" + uInt8View.length);\n worker.postMessage({ 'copy': 'true', 'ourArray': uInt8View.buffer }); //uInt8View.buffer\n }\n}\n\nwindow.addEventListener('load', function (e) {\n init();\n}, false);\n\nwindow.test = test;//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiMC5qcyIsInNvdXJjZXMiOlsid2VicGFjazovLy9zY3JpcHQuanM/OWE5NSJdLCJzb3VyY2VzQ29udGVudCI6WyIvLyBzZXQgc29tZSBjb25zdGFudHMvdmFyc1xudmFyIFNJWkUgPSAxMDI0ICogMTAyNCAqIDMyOyAvLyAzMk1CIGZvciBvdXIgZGF0YVxudmFyIGFycmF5QnVmZmVyID0gbnVsbDtcbnZhciB1SW50OFZpZXcgPSBudWxsO1xudmFyIG9yaWdpbmFsTGVuZ3RoID0gbnVsbDtcblxuLy8gYnVpbGQgb3VyIGV4YW1wbGUgYXJyYXkgb2YgMzJNQiBudW1iZXJzXG4vLyBsYXRlciBpbiB0aGUgd29ya2VyIHdlIHdpbGwgd29yayBvbiB0aGVtIHdpdGggc29tZSBzaW1wbGUgbWF0aCBvcGVyYXRpb25zXG5mdW5jdGlvbiBzZXR1cEFycmF5KCkge1xuICBhcnJheUJ1ZmZlciA9IG5ldyBBcnJheUJ1ZmZlcihTSVpFKTtcbiAgdUludDhWaWV3ID0gbmV3IFVpbnQ4QXJyYXkoYXJyYXlCdWZmZXIpO1xuICBvcmlnaW5hbExlbmd0aCA9IHVJbnQ4Vmlldy5sZW5ndGg7XG5cbiAgZm9yICh2YXIgaSA9IDA7IGkgPCBvcmlnaW5hbExlbmd0aDsgKytpKSB7XG4gICAgdUludDhWaWV3W2ldID0gaTtcbiAgfVxuXG4gIGxvZyhzb3VyY2UoKSArICdmaWxsZWQgJyArIHRvTUIob3JpZ2luYWxMZW5ndGgpICsgJyBNQiBidWZmZXInKTtcbn1cblxuLy9cbi8vIGhlbHBlciBmdW5jdGlvbnMgdG8gbWVhc3VyZSBwZXJmb3JtYW5jZVxuLy9cblxuLy8gcmV0dXJuIHRpbWUgc3RlbXBcbmZ1bmN0aW9uIHRpbWUoKSB7XG4gIHZhciBub3cgPSBuZXcgRGF0ZSgpO1xuICB2YXIgdGltZSA9IC8oXFxkKzpcXGQrOlxcZCspLy5leGVjKG5vdylbMF0gKyAnOic7XG4gIGZvciAodmFyIG1zID0gU3RyaW5nKG5vdy5nZXRNaWxsaXNlY29uZHMoKSksIGkgPSBtcy5sZW5ndGggLSAzOyBpIDwgMDsgKytpKSB7XG4gICAgdGltZSArPSAnMCc7XG4gIH1cbiAgcmV0dXJuIHRpbWUgKyBtcztcbn1cblxuLy8gV2UgYXJlIG5vdyBvdXIgcGFnZSAob24gdGhlIHdvcmtlciB3aWxsIGhhdmUgc29tZSBuaWNlIFJFRCBjb2xvciBmb3IgdGhlIGFuc3dlcnMpXG5mdW5jdGlvbiBzb3VyY2UoKSB7XG4gICAgcmV0dXJuICc8c3BhbiBzdHlsZT1cImNvbG9yOmdyZWVuO1wiPk91ciBwYWdlOjwvc3Bhbj4gJztcbn1cblxuZnVuY3Rpb24gc2Vjb25kcyhzaW5jZSkge1xuICByZXR1cm4gKG5ldyBEYXRlKCkgLSBzaW5jZSkgLyAxMDAwLjA7XG59XG5cbmZ1bmN0aW9uIHRvTUIoYnl0ZXMpIHtcbiAgcmV0dXJuIE1hdGgucm91bmQoYnl0ZXMgLyAxMDI0IC8gMTAyNCk7XG59XG5cbi8vXG4vLyBJbml0aWFsXG52YXIgd29ya2VyID0gbnVsbDtcbnZhciBzdGFydFRpbWUgPSAwO1xudmFyIHN1cHBvcnRlZCA9IGZhbHNlO1xuXG4vLyBNb3ZlIG91dHB1dCBwYW5lbCBkb3duIGZ1cnRoZXIgaWYgPGRldGFpbHM+IGlzbid0IHN1cHBvcnRlZCAoY29sbGFwc2libGUpLlxudmFyIGRldGFpbHMgPSBkb2N1bWVudC5xdWVyeVNlbGVjdG9yKCdkZXRhaWxzJyk7XG5pZiAoISgnb3BlbicgaW4gZGV0YWlscykpIHtcbiAgZG9jdW1lbnQucXVlcnlTZWxlY3Rvcignc2VjdGlvbicpLmNsYXNzTGlzdC5hZGQoJ2Rvd24nKTtcbn1cblxuZnVuY3Rpb24gbG9nKHN0cikge1xuICB2YXIgZWxlbSA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdyZXN1bHQnKTtcbiAgdmFyIGxvZyA9IGZ1bmN0aW9uKHMpIHtcbiAgIGVsZW0uaW5uZXJIVE1MICs9ICcnLmNvbmNhdCh0aW1lKCksICcgJywgcywgJ1xcbicpO1xuICB9O1xuICBsb2coc3RyKTtcbn1cblxuZnVuY3Rpb24gaW5pdCgpIHtcbiAgd29ya2VyID0gbmV3IFdvcmtlcignd29ya2VyLmpzJyk7XG5cbiAgLy8gVGFrZSBjYXJlIG9mIHZlbmRvciBwcmVmaXhlcy5cbiAgd29ya2VyLnBvc3RNZXNzYWdlID0gd29ya2VyLndlYmtpdFBvc3RNZXNzYWdlIHx8IHdvcmtlci5wb3N0TWVzc2FnZTtcblxuICB3b3JrZXIub25tZXNzYWdlID0gZnVuY3Rpb24oZSkge1xuICAgIGNvbnNvbGUudGltZUVuZCgnYWN0dWFsIHBvc3RNZXNzYWdlIHJvdW5kIHRyaXAgd2FzJyk7XG4gICAgLy8gY2FwdHVyZSBlbGFwc2VkIHRpbWUgc2luY2UgdGhlIG9yaWdpbmFsIHBvc3RNZXNzYWdlKCk7XG4gICAgaWYgKCFlLmRhdGEudHlwZSkge1xuICAgICAgdmFyIGVsYXBzZWQgPSBzZWNvbmRzKHN0YXJ0VGltZSk7XG4gICAgfVxuXG4gICAgdmFyIGRhdGEgPSBlLmRhdGE7XG5cbiAgICBpZiAoZGF0YS50eXBlICYmIGRhdGEudHlwZSA9PSAnZGVidWcnKSB7XG4gICAgICBsb2coZGF0YS5tc2cpO1xuICAgIH0gZWxzZSB7XG4gICAgICBpZiAoZGF0YS5jb3B5KSB7XG4gICAgICAgIGRhdGEuYnl0ZUxlbmd0aCA9IGRhdGEub3VyQXJyYXkuYnl0ZUxlbmd0aDtcbiAgICAgIH1cbiAgICAgIHZhciByYXRlID0gTWF0aC5yb3VuZCh0b01CKGRhdGEuYnl0ZUxlbmd0aCkgLyBlbGFwc2VkKTtcbiAgICAgIGxvZyhzb3VyY2UoKSArICdwb3N0TWVzc2FnZSByb3VuZHRyaXAgdG9vazogJyArIChlbGFwc2VkICogMTAwMCkgKyAnIG1zJyk7XG4gICAgICBsb2coc291cmNlKCkgKyAncG9zdE1lc3NhZ2Ugcm91bmR0cmlwIHJhdGU6ICcgKyByYXRlICsgJyBNQi9zJyk7XG4gICAgfVxuICB9O1xuXG4gIGxvZyhzb3VyY2UoKSArICdXZSBhcmUgZ29vZCB0byBnbyEnKTtcbn1cblxuZnVuY3Rpb24gdGVzdCh1c2VJdCkge1xuICB2YXIgdXNlVHJhbnNmZXJyYWJsZSA9IHVzZUl0O1xuICBzZXR1cEFycmF5KCk7IC8vIE5lZWQgdG8gZG8gdGhpcyBvbiBldmVyeSBydW4gZm9yIHRoZSByZXBlYXRlZCBydW5zIHdpdGggdHJhbnNmZXJhYmxlIGFycmF5cy4gVGhleSdyZSBjbGVhcmVkIG91dCBhZnRlciB0aGV5J3JlIHRyYW5zZmVycmVkLlxuXG4gIHN0YXJ0VGltZSA9IG5ldyBEYXRlKCk7XG4gIGNvbnNvbGUudGltZSgnYWN0dWFsIHBvc3RNZXNzYWdlIHJvdW5kIHRyaXAgd2FzJyk7XG5cbiAgaWYgKHVzZVRyYW5zZmVycmFibGUgKSB7XG4gICAgY29uc29sZS5sb2cgKFwiIyMgVXNpbmcgVHJhbnNmZXJyYWJsZSBvYmplY3QgbWV0aG9kIG9uIHNpemU6IFwiICsgdUludDhWaWV3Lmxlbmd0aCk7XG4gICAgd29ya2VyLnBvc3RNZXNzYWdlKHVJbnQ4Vmlldy5idWZmZXIsIFt1SW50OFZpZXcuYnVmZmVyXSk7XG4gIH0gZWxzZSB7XG4gICAgY29uc29sZS5sb2cgKFwiIyMgVXNpbmcgb2xkIENPUFkgbWV0aG9kIG9uIHNpemU6IFwiKyB1SW50OFZpZXcubGVuZ3RoKTtcbiAgICB3b3JrZXIucG9zdE1lc3NhZ2Uoeydjb3B5JzogJ3RydWUnLCAnb3VyQXJyYXknOiB1SW50OFZpZXcuYnVmZmVyfSk7IC8vdUludDhWaWV3LmJ1ZmZlclxuICB9XG59XG5cbndpbmRvdy5hZGRFdmVudExpc3RlbmVyKCdsb2FkJywgZnVuY3Rpb24oZSkge1xuICBpbml0KCk7XG59LCBmYWxzZSk7XG5cbndpbmRvdy50ZXN0ID0gdGVzdDtcblxuXG5cbi8vIFdFQlBBQ0sgRk9PVEVSIC8vXG4vLyBzY3JpcHQuanMiXSwibWFwcGluZ3MiOiI7O0FBQUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EiLCJzb3VyY2VSb290IjoiIn0=\n//# sourceURL=webpack-internal:///0\n")}]);
<!DOCTYPE html>
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1" />
<title>Big Data In Worker Example</title>
<link href="https://fonts.googleapis.com/css?family=Open+Sans" rel="stylesheet">
<link rel="stylesheet" type="text/css" href="dist.css">
<body>
<h1>Big Data (using transferable objects) In Web Worker Example</h1>
<details>
<summary>What is 'Big Data' and Web Worker got to do with each other?</summary>
<div>
TODO...
<p>This demo illustrates <a href="http://dev.w3.org/html5/spec/common-dom-interfaces.html#transferable-objects" target="_blank">transferable objects</a>.
Transferable objects are objects that are not copied (e.g. using something like <a href="http://updates.html5rocks.com/2011/09/Workers-ArrayBuffer" target="_blank">structured cloning</a>).
Instead, the data is transferred from one context to another. The 'version' from the calling context is no longer available once transferred
to the new context. For example, when transferring an <code>ArrayBuffer</code> from main app to Worker, the
original <code>ArrayBuffer</code> from the main thread is cleared and no longer usable.
This vastly improves performance of sending data to a Worker. </p>
<p>This demo sends a 32MB <code>ArrayBuffer</code> to a worker and back using a prefixed version of <code>postMessage()</code>
that supports transferable objects: <code><a href="http://dev.w3.org/html5/workers/#dedicated-workers-and-the-dedicatedworkerglobalscope-interface" target="_blank">webkitPostMessage()</a></code>.
If your browser doesn't support transferables, the sample falls back to old-skool structured cloning.</p>
<p><b>Support:</b> Chrome Dev Channel 17+</p>
</div>
</details>
<section>
<p><a href="http://dev.w3.org/html5/spec/common-dom-interfaces.html#transferable-objects" target="_blank">Transferable Objects</a> are lightning fast! The prefixed <code>[window|worker].webkitPostMessage()</code>
now supports sending an <code>ArrayBuffer</code> as a transferable.</p>
<button onclick="test(true);">Run test with transferable objects</button>
<button onclick="test(false);">Run test with COPY of objects</button>
<pre id="result"></pre>
</section>
<script src="dist.js"> </script>
</body>
</html>
{
"standard": {
"globals": [
"d3"
]
}
}
// set some constants/vars
var SIZE = 1024 * 1024 * 32; // 32MB for our data
var arrayBuffer = null;
var uInt8View = null;
var originalLength = null;
// build our example array of 32MB numbers
// later in the worker we will work on them with some simple math operations
function setupArray() {
arrayBuffer = new ArrayBuffer(SIZE);
uInt8View = new Uint8Array(arrayBuffer);
originalLength = uInt8View.length;
for (var i = 0; i < originalLength; ++i) {
uInt8View[i] = i;
}
log(source() + 'filled ' + toMB(originalLength) + ' MB buffer');
}
//
// helper functions to measure performance
//
// return time stemp
function time() {
var now = new Date();
var time = /(\d+:\d+:\d+)/.exec(now)[0] + ':';
for (var ms = String(now.getMilliseconds()), i = ms.length - 3; i < 0; ++i) {
time += '0';
}
return time + ms;
}
// We are now our page (on the worker will have some nice RED color for the answers)
function source() {
return '<span style="color:green;">Our page:</span> ';
}
function seconds(since) {
return (new Date() - since) / 1000.0;
}
function toMB(bytes) {
return Math.round(bytes / 1024 / 1024);
}
//
// Initial
var worker = null;
var startTime = 0;
var supported = false;
// Move output panel down further if <details> isn't supported (collapsible).
var details = document.querySelector('details');
if (!('open' in details)) {
document.querySelector('section').classList.add('down');
}
function log(str) {
var elem = document.getElementById('result');
var log = function(s) {
elem.innerHTML += ''.concat(time(), ' ', s, '\n');
};
log(str);
}
function init() {
worker = new Worker('worker.js');
// Take care of vendor prefixes.
worker.postMessage = worker.webkitPostMessage || worker.postMessage;
worker.onmessage = function(e) {
console.timeEnd('actual postMessage round trip was');
// capture elapsed time since the original postMessage();
if (!e.data.type) {
var elapsed = seconds(startTime);
}
var data = e.data;
if (data.type && data.type == 'debug') {
log(data.msg);
} else {
if (data.copy) {
data.byteLength = data.ourArray.byteLength;
}
var rate = Math.round(toMB(data.byteLength) / elapsed);
log(source() + 'postMessage roundtrip took: ' + (elapsed * 1000) + ' ms');
log(source() + 'postMessage roundtrip rate: ' + rate + ' MB/s');
}
};
log(source() + 'We are good to go!');
}
function test(useIt) {
var useTransferrable = useIt;
setupArray(); // Need to do this on every run for the repeated runs with transferable arrays. They're cleared out after they're transferred.
startTime = new Date();
console.time('actual postMessage round trip was');
if (useTransferrable ) {
console.log ("## Using Transferrable object method on size: " + uInt8View.length);
worker.postMessage(uInt8View.buffer, [uInt8View.buffer]);
} else {
console.log ("## Using old COPY method on size: "+ uInt8View.length);
worker.postMessage({'copy': 'true', 'ourArray': uInt8View.buffer}); //uInt8View.buffer
}
}
window.addEventListener('load', function(e) {
init();
}, false);
window.test = test;
body
padding: 2em
font-family: "Open Sans", Helvetica, Arial, sans-serif
#result
max-height: 500px
overflow: auto
button
padding: 15px
border-radius: 5px
// Take care of vendor prefixes.
self.postMessage = self.webkitPostMessage || self.postMessage;
var ready = false;
function time() {
var now = new Date();
var time = /(\d+:\d+:\d+)/.exec(now)[0] + ':';
for (var ms = String(now.getMilliseconds()), i = ms.length - 3; i < 0; ++i) {
time += '0';
}
return time + ms;
}
function source() {
return '<span style="color:red;">The Worker:</span> ';
}
self.onmessage = function(e) {
if (!ready) {
initComplete();
return;
}
var USE_TRANSFERRABLE = true;
var dataLength;
var uInt8View = null;
if (e.data.copy !== undefined) {
// not a copy case
USE_TRANSFERRABLE = false;
uInt8View = new Uint8Array(e.data.ourArray);
dataLength = e.data.ourArray.byteLength;
}
else {
uInt8View = new Uint8Array(e.data);
e.data.byteLength = e.data.byteLength;
}
// Here we are 'computing' something important on the data.
// In our case - just play with %
for (var i=0; i < dataLength; i++ ) {
uInt8View[i] = uInt8View[i] % 2;
}
if (USE_TRANSFERRABLE) {
self.postMessage(uInt8View.buffer, [uInt8View.buffer]);
} else {
self.postMessage(e.data.ourArray);
}
};
self.onerror = function(message) {
log('worker error');
};
function log(msg) {
var object = {
type: 'debug',
msg: source() + msg + ' [' + time() + ']'
};
self.postMessage(object);
}
function initComplete() {
ready = true;
log('READY!');
}
//setupArray();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment