9

I have read that transferable objects can be transferred really fast using postmessage of web worker. According to this transferable objects are either arraybuffer or messageport.

Question is, how do I convert say an arbitrary object that is of large size (30 mb) to a transferable object and pass it as an argument to postmessage. From what I understand I can convert my array to json string and then convert json string to raw byte data and store that inside of an array object. However, this seems to defeat the purpose of fast transferring.

could someone enlighten me to pass an object as transferable object or if it's even possible?

Thanks in advance!

Mark
  • 8,408
  • 15
  • 57
  • 81
  • you have 30 mb of non-binary data in a javascript object??? – markasoftware Dec 03 '15 at 03:09
  • @Markasoftware: yes since, I am loading large geometry object files in webgl. They are usually around 30mb. I would assume the produced geometry object is around the same size. – Mark Dec 03 '15 at 03:13
  • 1
    Do you have these geometry files as `File` or `Blob` objects? If so, you could use a `FileReader` to [read them as `ArrayBuffer`s](https://developer.mozilla.org/en-US/docs/Web/API/FileReader/readAsArrayBuffer). – Noah Freitas Dec 03 '15 at 03:32
  • _"Question is, how do I convert say an arbitrary object that is of large size (30 mb) to a transferable object and pass it as an argument to postmessage. "_ Is requirement to pass object to web worker ? Or does web worker transfer to document ? – guest271314 Dec 03 '15 at 03:36
  • @NoahFreitas: that sounds like a good idea! I will look into that. – Mark Dec 03 '15 at 03:38
  • @guest271314: I need to pass large geometry object of size 30 mb to webworker using postmessage. – Mark Dec 03 '15 at 03:39
  • _"need to pass large geometry object of size 30 mb to webworker using postmessage."_ When tried what occurs ? Tried `Blob` , as suggested by @NoahFreitas ? Could alternatively pass as `data URI` in chunks , them re-assemble `data URI` at web worker – guest271314 Dec 03 '15 at 03:44
  • @guest271314: it took around 1 min to transfer the object from main thread to worker. Therefore I need to use fast transferring – Mark Dec 03 '15 at 03:49
  • @Mark _"it took around 1 min to transfer the object from main thread to worker."_ If 1 minute duration is too lengthy for expected results , what is total duration of process trying to achieve ? What does web worker do with object when transfer complete ? – guest271314 Dec 03 '15 at 03:57
  • @guest271314: the process is trying to reducing load time for geometry by allocating computational work to the worker. Since shared memory isn't available for workers the object has to be copied. However after using conventional method of transferring the object i.e. passing the object as a json string to be copied. It took way too long and defeated the purpose of allocating work to reduce loading time. Therefore I have to find a more efficient way to transfering large objects. – Mark Dec 03 '15 at 04:02
  • Does the object require a `document` to process geometry ? Or is object a series of objects , or arrays with values. That is, can worker process entire object , then transfer object to main thread ? Instead of transferring process to worker from main thread then back to main thread ? – guest271314 Dec 03 '15 at 04:13
  • Have you actually tested how long the transfer takes? And is your data format optimal? Are you sure you can't create the data within the web worker? – Tomáš Zato Dec 03 '15 at 08:52

2 Answers2

25

This misconception is quite recurring here. You're imagining that it's possible to write some fast javascript code to convert your large object into transferable. But indeed, any conversion code you write defeats the purpose, just as you said. And the more complex data, the more speed you lose.

Objects are normally (when not transfering) converted by native structured clone algorithm (which uses implementation defined format and sure is optimal). Any javascript code you write will most likely be slower than structured clone, while achieving the same goal - transferring data as binary.

The purpose of transferable objects is to allow transfer for binary data, such as images (from canvas), audio or video. These kinds of data can be transferred without being processed by structured clone algorithm, which is why transferable interface was added. And the effect is insignificant even for these - see an answer about transferable speed.

As a last note, I wrote a prototype based library that converts javascript objects to ArrayBuffer and back. It's slower, especially for JSON like data. It's advantages (and advantages of any similar code you write) are:

  1. You can define custom object conversions
  2. You can use inheritance (eg. sending your own types, like Foo)

Code to transfer JSON like object

If your data is like JSON, just stick to structured clone and do not transfer. If you don't trust me, test it with this code. You will see it's slower than normal postMessage.

var object = {dd:"ddd", sub:{xx:"dd"}, num:666};
var string = JSON.stringify(object);
var uint8_array = new TextEncoder(document.characterSet.toLowerCase()).encode(string);
var array_buffer = uint8_array.buffer;
// now transfer array buffer
worker.postMessage(array_buffer, [array_buffer])

The opposite conversion, considering you have some ArrayBuffer:

// Let me just generate some array buffer for the simulation
var array_buffer = new Uint8Array([123,34,100,100,34,58,34,100,100,100,34,44,34,115,117,98,34,58,123,34,120,120,34,58,34,100,100,34,125,44,34,110,117,109,34,58,54,54,54,125]).buffer;
// Now to the decoding
var decoder = new TextDecoder("utf-8");
var view = new DataView(array_buffer, 0, array_buffer.byteLength);
var string = decoder.decode(view);
var object = JSON.parse(string);
Community
  • 1
  • 1
Tomáš Zato
  • 50,171
  • 52
  • 268
  • 778
2

Should have looked up Tomas's answer earlier.

Proof, although not specifically the way Tomas suggested.

Version A

Version B

I manually converted to a stringified json obejct to a Uint8Array like so:

function stringToUintArray(message) {
  var encoded = self.btoa(message);
  var uintArray = Array.prototype.slice.call(encoded).map(ch => ch.charCodeAt(0));
  var uarray = new Uint8Array(uintArray);
  return uarray;
}

and transferred it like so from the web worker to main thread:

console.time('generate');
    var result = generate(params.low, params.high, params.interval, params.size);
    var uarr = stringToUintArray(JSON.stringify(result));
    console.timeEnd('generate');
    self.postMessage(uarr.buffer, [uarr.buffer]);

and on the main thread I did something like this:

var uarr = new Uint8Array(e.data);
var json = UintArrayToString(uarr);
var result = JSON.parse(json);

enter image description here

deostroll
  • 11,661
  • 21
  • 90
  • 161