changeset 33:e9a0f66ee81c

Quick in-browser performance test
author Chris Cannam
date Sat, 24 Sep 2016 16:26:04 +0100
parents 30028b3c95e4
children 0eafc96a039c
files perf-test.html perf-test.js
diffstat 2 files changed, 169 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/perf-test.html	Sat Sep 24 16:26:04 2016 +0100
@@ -0,0 +1,21 @@
+<html>
+  <head>
+    <meta charset="UTF-8">
+    <title>VamPipe Adapter Test</title>
+
+    <style type="text/css">
+      body { margin: 5%; }
+      table, td, th { border: 0.1em solid #e0e0e0; border-collapse: collapse }
+      td, th { padding: 0.5em }
+    </style>
+
+    <script src="VampExamplePlugins.js"></script>
+    <script src="perf-test.js"></script>
+  </head>
+  <body>
+    <h3>VamPipe Adapter Test</h3>
+
+    <p id="test-result"></p>
+    
+  </body>
+</html>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/perf-test.js	Sat Sep 24 16:26:04 2016 +0100
@@ -0,0 +1,148 @@
+"use strict";
+
+var exampleModule = VampExamplePluginsModule();
+
+// It is possible to declare both parameters and return values as
+// "string", in which case Emscripten will take care of
+// conversions. But it's not clear how one would manage memory for
+// newly-constructed returned C strings -- the returned pointer from
+// vampipeRequestJson would appear (?) to be thrown away by the
+// Emscripten string converter if we declare it as returning a string,
+// so we have no opportunity to pass it to vampipeFreeJson, which
+// suggests this would leak memory if the string isn't static. Not
+// wholly sure though. Anyway, passing and returning pointers (as
+// numbers) means we can manage the Emscripten heap memory however we
+// want in our request wrapper function below.
+
+var vampipeRequestJson = exampleModule.cwrap(
+    'vampipeRequestJson', 'number', ['number']
+);
+
+var vampipeProcessRaw = exampleModule.cwrap(
+    "vampipeProcessRaw", "number", ["number", "number", "number", "number"]
+);
+
+var vampipeFreeJson = exampleModule.cwrap(
+    'vampipeFreeJson', 'void', ['number']
+);
+
+function note(blah) {
+    document.getElementById("test-result").innerHTML += blah + "<br>";
+}
+
+function comment(blah) {
+    note("<br><i>" + blah + "</i>");
+}
+
+function processRaw(request) {
+    
+    const nChannels = request.processInput.inputBuffers.length;
+    const nFrames = request.processInput.inputBuffers[0].values.length;
+
+    const buffersPtr = exampleModule._malloc(nChannels * 4);
+    const buffers = new Uint32Array(
+        exampleModule.HEAPU8.buffer, buffersPtr, nChannels);
+
+    for (let i = 0; i < nChannels; ++i) {
+        const framesPtr = exampleModule._malloc(nFrames * 4);
+        const frames = new Float32Array(
+            exampleModule.HEAPU8.buffer, framesPtr, nFrames);
+        frames.set(request.processInput.inputBuffers[i].values);
+        buffers[i] = framesPtr;
+    }
+    
+    const responseJson = vampipeProcessRaw(
+        request.pluginHandle,
+        buffersPtr,
+        request.processInput.timestamp.s,
+        request.processInput.timestamp.n);
+    
+    for (let i = 0; i < nChannels; ++i) {
+        exampleModule._free(buffers[i]);
+    }
+    exampleModule._free(buffersPtr);
+
+    const response = JSON.parse(
+        exampleModule.Pointer_stringify(responseJson));
+    
+    vampipeFreeJson(responseJson);
+    
+    return response;
+}
+
+function request(jsonStr) {
+    note("Request JSON = " + jsonStr);
+    var m = exampleModule;
+    // Inspection reveals that intArrayFromString converts the string
+    // from utf16 to utf8, which is what we want (though the docs
+    // don't mention this). Note the *Cstr values are Emscripten heap
+    // pointers
+    var inCstr = m.allocate(m.intArrayFromString(jsonStr), 'i8', m.ALLOC_NORMAL);
+    var outCstr = vampipeRequestJson(inCstr);
+    m._free(inCstr);
+    var result = m.Pointer_stringify(outCstr);
+    vampipeFreeJson(outCstr);
+    note("Returned JSON = " + result);
+    return result;
+}
+
+function test() {
+
+    comment("Loading zero crossings plugin...");
+    let result = request('{"type":"load","content": {"pluginKey":"vamp-example-plugins:zerocrossing","inputSampleRate":44100,"adapterFlags":["AdaptAllSafe"]}}');
+
+    const blockSize = 1024;
+
+    result = request('{"type":"configure","content":{"pluginHandle":1,"configuration":{"blockSize": ' + blockSize + ', "channelCount": 1, "stepSize": ' + blockSize + '}}}');
+
+    const nblocks = 1000;
+
+    let processInputBuffers = [new Float32Array(
+        Array.from(Array(blockSize).keys(), n => n / blockSize))
+    ];
+    
+    comment("Now processing " + nblocks + " blocks of 1024 samples each...");
+
+    let start = (new Date()).getTime();
+    comment("Start at " + start);
+    
+    for (let i = 0; i < nblocks; ++i) {
+	let ts = { "s": i, "n": 0 }; // wholly bogus, but ZC plugin doesn't use it
+	result = processRaw({
+	    "pluginHandle": 1,
+	    "processInput": {
+		"timestamp": ts,
+		"inputBuffers": processInputBuffers
+	    }
+	});
+    }
+
+    let finish = (new Date()).getTime();
+    comment("Finish at " + finish + " for a time of " + (finish - start) + " ms");
+
+    comment("Again...");
+    
+    start = (new Date()).getTime();
+    comment("Start at " + start);
+    
+    for (let i = 0; i < nblocks; ++i) {
+	let ts = { "s": i, "n": 0 }; // wholly bogus, but ZC plugin doesn't use it
+	result = processRaw({
+	    "pluginHandle": 1,
+	    "processInput": {
+		"timestamp": ts,
+		"inputBuffers": processInputBuffers
+	    }
+	});
+    }
+
+    finish = (new Date()).getTime();
+    comment("Finish at " + finish + " for a time of " + (finish - start) + " ms");
+    
+    comment("Cleaning up the plugin and getting any remaining features...");
+    result = request('{"type":"finish","content":{"pluginHandle":1}}');
+}
+
+window.onload = function() {
+    test();
+}