<html> <head> <title>ArrayBuffer External Memory test</title> <script> var log; function print(message, color) { var paragraph = document.createElement("div"); paragraph.appendChild(document.createTextNode(message)); paragraph.style.fontFamily = "monospace"; if (color) paragraph.style.color = color; log.appendChild(paragraph); } function pass(msg) { print("PASS: " + msg, "green"); } function fail(msg) { print("FAIL: " + msg, "red"); } var KB = 1024; var MB = KB * KB; var noise = KB; function externalMemory() { return getV8Statistics().amount_of_external_allocated_memory; } function collectGarbage() { for (var i = 0; i < 10; i++) gc(); } function allocationsThatIncreaseExternalMemory() { function test(expression) { var before = externalMemory(); (function () { eval(expression); }) (); var now = externalMemory(); if (now < before + MB - noise) { fail(expression + " did not increase the amount of external memory (" + before + ", " + now + ")."); } else { pass(expression + " increased the amount of external memory."); } collectGarbage(); var after = externalMemory(); if (after > now + noise) { fail("Garbage collection after " + expression + " did not return the amount of external memory to the initial value (" + now + ", " + after + ")."); } else { pass("Garbage collection after " + expression + " returned the amount of external memory to the initial value."); } } test("(new ArrayBuffer(MB))"); test("(new Float32Array(MB))"); test("(new Float64Array(MB))"); test("(new Int8Array(MB))"); test("(new Int16Array(MB))"); test("(new Int32Array(MB))"); test("(new Uint8Array(MB))"); test("(new Uint16Array(MB))"); test("(new Uint32Array(MB))"); test("(new BigInt64Array(MB))"); test("(new BigUint64Array(MB))"); var largeJSArray = []; for (var i = 0; i < MB; i++) largeJSArray.push(i); test("(new Float32Array(largeJSArray))"); test("(new Float64Array(largeJSArray))"); test("(new Int8Array(largeJSArray))"); test("(new Int16Array(largeJSArray))"); test("(new Int32Array(largeJSArray))"); test("(new Uint8Array(largeJSArray))"); test("(new Uint16Array(largeJSArray))"); test("(new Uint32Array(largeJSArray))"); test("(new BigInt64Array(largeJSArray, (x) => BigInt(x))"); test("(new BigUint64Array(largeJSArray, (x) => BigInt(x))"); var int8Array = new Int8Array(MB); test("(new Float32Array(int8Array))"); test("(new Float64Array(int8Array))"); test("(new Int8Array(int8Array))"); test("(new Int16Array(int8Array))"); test("(new Int32Array(int8Array))"); test("(new Uint8Array(int8Array))"); test("(new Uint16Array(int8Array))"); test("(new Uint32Array(int8Array))"); test("(new BigInt64Array(int8Array, (x) => BigInt(x))"); test("(new BigUint64Array(int8Array, (x) => BigInt(x))"); } function allocationsThatDoNotChangeExternalMemory() { function test(expression) { var before = externalMemory(); (function () { eval(expression); }) (); var now = externalMemory(); if (now > before + noise) { fail(expression + " increased the amount of external memory (" + before + ", " + now + ")."); } else { pass(expression + " did not increase the amount of external memory."); } collectGarbage(); var after = externalMemory(); if (after < now - noise) { fail("Garbage collection after " + expression + " decreased the amount of external memory (" + now + ", " + after + ")."); } else { pass("Garbage collection after " + expression + " did not decrease the amount of external memory."); } } var arrayBuffer = new ArrayBuffer(MB); test("(new Float32Array(arrayBuffer))"); test("(new Float64Array(arrayBuffer))"); test("(new Int8Array(arrayBuffer))"); test("(new Int16Array(arrayBuffer))"); test("(new Int32Array(arrayBuffer))"); test("(new Uint8Array(arrayBuffer))"); test("(new Uint16Array(arrayBuffer))"); test("(new Uint32Array(arrayBuffer))"); test("(new BigInt64Array(arrayBuffer))"); test("(new BigUint64Array(arrayBuffer))"); var int8Array = new Int8Array(MB); test("(new Float32Array(int8Array.buffer))"); test("(new Float64Array(int8Array.buffer))"); test("(new Int8Array(int8Array.buffer))"); test("(new Int16Array(int8Array.buffer))"); test("(new Int32Array(int8Array.buffer))"); test("(new Uint8Array(int8Array.buffer))"); test("(new Uint16Array(int8Array.buffer))"); test("(new Uint32Array(int8Array.buffer))"); test("(new BigInt64Array(int8Array.buffer))"); test("(new BigUint64Array(int8Array.buffer))"); } function transfersThatDecreaseExternalMemory() { var workerSource = "function externalMemory() {\n" + " return getV8Statistics().amount_of_external_allocated_memory;\n" + "}\n" + "var KB = 1024;\n" + "var MB = KB * KB;\n" + "var noise = KB;\n" + "self.onmessage = function(e) {\n" + " var before = externalMemory();\n" + " e.data;\n" + " var after = externalMemory();\n" + " if (after > before + MB - noise) {\n" + " self.postMessage('PASS: Amount of external memory increased.');\n" + " } else {\n" + " self.postMessage('FAIL: Amount of external memory did not increase.');\n" + " }\n" + "}\n"; var blob = new Blob([workerSource]); var worker = new Worker(window.webkitURL.createObjectURL(blob)); worker.onmessage = function (e) { print("message from worker: " + e.data, "blue"); } function test(expression) { var buffer = eval(expression); try { var before = externalMemory(); worker.postMessage(buffer, [buffer]); var now = externalMemory(); if (now > before - MB + noise) { fail("Transfer of " + expression + " did not decrease the amount of external memory (" + before + ", " + now + ")."); } else { pass("Transfer of " + expression + " decreased the amount of external memory."); } collectGarbage(); var after = externalMemory(); if (after < now - noise) { fail("Garbage collection after transfer of " + expression + " decreased the amount of external memory (" + now + ", " + after + ")."); } else { pass("Garbage collection after transfer of " + expression + " did not decrease the amount of external memory."); } } catch (e) { fail("Transfer of " + name + ": could not webkitPostMessage: " + e); return false; } return true; } test("(new ArrayBuffer(MB))"); test("(new Float32Array(MB)).buffer"); test("(new Float64Array(MB)).buffer"); test("(new Int8Array(MB)).buffer"); test("(new Int16Array(MB)).buffer"); test("(new Int32Array(MB)).buffer"); test("(new Uint8Array(MB)).buffer"); test("(new Uint16Array(MB)).buffer"); test("(new Uint32Array(MB)).buffer"); test("(new BigInt64Array(MB)).buffer"); test("(new BigUint64Array(MB)).buffer"); } function runAll() { log = document.getElementById("log1"); if (typeof gc == "undefined" || typeof getV8Statistics == "undefined") { print("Run chrome browser with --js-flags='--expose_gc --track_gc_object_stats'", "red"); } else { allocationsThatIncreaseExternalMemory(); collectGarbage(); allocationsThatDoNotChangeExternalMemory(); collectGarbage(); log = document.getElementById("log2"); transfersThatDecreaseExternalMemory(); collectGarbage(); } } </script> </head> <body onload="runAll()"> <p>This test checks that allocation and deallocation of typed arrays correctly adjusts the amount of external memory in V8.</p> <div id='log1'></div> <p>This test checks that transfer of an array buffer to worker decreases amount of external memory in the main V8 isolate.</p> <div id='log2'></div> </body> </html>