blob uris much slower than data uris
Reported by
oss...@cendio.com,
Jun 9 2017
|
|||||||||
Issue descriptionUserAgent: Mozilla/5.0 (X11; Fedora; Linux x86_64; rv:53.0) Gecko/20100101 Firefox/53.0 Steps to reproduce the problem: 1. Replace base64 encoded data uris with Blob()/createObjectURL() What is the expected behavior? Execution should be at least as fast, preferably faster. What went wrong? Code seems to execute at half the speed, or worse. Did this work before? N/A Chrome version: 58.0.3029.110 Channel: stable OS Version: Fedora 25 Flash Version: None This was discovered whilst debugging a performance issue with noVNC in Firefox: https://bugzilla.mozilla.org/show_bug.cgi?id=1369317 Blobs should be more efficient as it avoids all the base 64 encoding and decoding, as well as all the string management. So it would be nice if Chromium isn't preventing developers from using that technology for performance reasons.
,
Jun 12 2017
Blobs are managed in the browser process while data uris are processed in renderer, so it's possible that blobs have larger overhead due to IPC, etc. How did you create blobs, how big they are? It would be great if you could provide a test page.
,
Jun 12 2017
The blobs are created from a Uint8Array (from data received over a WebSocket). They are generally small (kBs), but very numerous. I'm afraid I don't have a simple test page right now. If you are willing to download noVNC then I could provide a patch that shows the issue.
,
Jun 12 2017
Thank you for providing more feedback. Adding requester "ksakamoto@chromium.org" to the cc list and removing "Needs-Feedback" label. For more details visit https://www.chromium.org/issue-tracking/autotriage - Your friendly Sheriffbot
,
Jun 12 2017
,
Jun 12 2017
Naïvely I would expect this behavior - blobs involve storage and inter-process communication in Chrome, whereas data URIs should not. My own tests seem to confirm the behavior you report: (this HTML+JS snippet is also hosted at https://fiddle.jshell.net/bcfbjguk/35/ so you can easily test it yourself) Output on the ~two-year-old Chromebook I'm using: blobs: 1265.664 kbps dataUri: 3444.736 kbps blobs: 1026.048 kbps dataUri: 2224.128 kbps blobs: 614.4 kbps dataUri: 3465.216 kbps blobs: 669.696 kbps dataUri: 3272.704 kbps blobs: 722.944 kbps dataUri: 3409.92 kbps done HTML: <h1>Pseudorandom Base64-encoded data: URI rate</h1> <textarea id=output cols=80 rows=20 style="width:100%"></textarea> JS: (async function(){ let chunkSize = 2048; let msPerRun = 1e3; let repetitions = 5; for (let runPair = 0; runPair < repetitions; ++runPair) { await (async function(startTime, duration){ let count = 0; let buffer = new Uint8Array(chunkSize); while (Date.now() < (startTime + duration)) { crypto.getRandomValues(buffer); let url = URL.createObjectURL(new Blob([buffer.buffer], {type: 'application/octet-binary'})); let response = await fetch(url); let out = await response.arrayBuffer(); let oBuffer = new Uint8Array(out); if (oBuffer.length !== buffer.length) throw '1wrong response length ' + [oBuffer.length,buffer.length]; for (let i = 0; i < oBuffer.length; ++i) { if (oBuffer[i] !== buffer[i]) throw '1wrong byte in response'; ++count; } } document.getElementById('output').value += 'blobs: ' + (count / duration) + ' kbps\n'; })(Date.now(), msPerRun); await (async function(startTime, duration){ let count = 0; let buffer = new Uint8Array(chunkSize); while (Date.now() < (startTime + duration)) { crypto.getRandomValues(buffer); let url = 'data:application/octet-binary;base64,' + btoa(String.fromCharCode.apply(String, buffer)); let response = await fetch(url); let out = await response.arrayBuffer(); let oBuffer = new Uint8Array(out); if (oBuffer.length !== buffer.length) throw '2wrong response length ' + [oBuffer.length,buffer.length]; for (let i = 0; i < oBuffer.length; ++i) { if (oBuffer[i] !== buffer[i]) throw '2wrong byte in response'; ++count; } } document.getElementById('output').value += 'dataUri: ' + (count / duration) + ' kbps\n'; })(Date.now(), msPerRun); } })().then(() => document.getElementById('output').value += 'done');
,
Jun 12 2017
Updated to call revokeObjectURL https://fiddle.jshell.net/bcfbjguk/37/ blobs: 1265.664 kbps dataUri: 3444.736 kbps blobs: 1026.048 kbps dataUri: 2224.128 kbps blobs: 614.4 kbps dataUri: 3465.216 kbps blobs: 669.696 kbps dataUri: 3272.704 kbps blobs: 722.944 kbps dataUri: 3409.92 kbps done
,
Jun 12 2017
Blobs without the URI+Fetch step perform similarly BTW: https://fiddle.jshell.net/bcfbjguk/39/ blobResponse: 1224.704 kbps blobObjectUri: 1417.216 kbps dataUri: 3233.792 kbps blobResponse: 1400.832 kbps blobObjectUri: 1394.688 kbps dataUri: 3244.032 kbps blobResponse: 1548.288 kbps blobObjectUri: 1458.176 kbps dataUri: 3457.024 kbps blobResponse: 1554.432 kbps blobObjectUri: 1472.512 kbps dataUri: 3186.688 kbps blobResponse: 1302.528 kbps blobObjectUri: 1333.248 kbps dataUri: 2990.08 kbps done
,
Jun 12 2017
Interesting. I think this is something we could profile and improve performance on. Let's bring this up in our meeting to see how we should prioritize.
,
Jun 12 2017
https://fiddle.jshell.net/bcfbjguk/42/ adds escape()-based data: URIs as a comparison point also tries power-of-2 chunk sizes from 65536 (maximum allowed by getRandomValues) all the way down to 1
,
Jun 12 2017
https://fiddle.jshell.net/bcfbjguk/45/ is updated to use correct units in the output
,
Jun 13 2017
Assigning to dmurph@chromium.org for prioritization; feel free to bounce back to me and/or discuss further if my repro code should turn into a performance benchmark of some sort
,
Jun 13 2017
This is a performance issue I'd like to keep track of. First priority here is getting a perf test about this use case. Once we get that needle in place, we can work towards getting it faster. @ ossman@cendio.com - can you provide a little more details about the usage pattern for blobs here? I want to create a perf test that models what you're doing as closely as possible (something full-stack, not microbenchmarky) so we can create a metric here.
,
Jun 15 2017
A rough outline of the flow: - Screen updates are sliced up in to many small JPEG images and sent from the server. Somewhat "bursty" as there will be a bunch of images, then a pause until the next screen update. - Data is read from the client WebSocket in to a Uint8Array - Once we have enough data for an entire JPEG image we give a slice to the Blob constructor - An Image object is created and .src is set via createObjectURL() - If .complete isn't set then we set up an event listener for "load" and start queueing Image objects as data comes in - When an Image is loaded we do drawImage() to an offscreen canvas - Once all images are processed we use drawImage() from the offscreen canvas to the visible canvas Repeat for each screen update
,
Aug 27
|
|||||||||
►
Sign in to add a comment |
|||||||||
Comment 1 by dtapu...@chromium.org
, Jun 9 2017