Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SharedArrayBuffer support? #16

Open
eranimo opened this issue Jun 20, 2020 · 6 comments
Open

SharedArrayBuffer support? #16

eranimo opened this issue Jun 20, 2020 · 6 comments

Comments

@eranimo
Copy link

eranimo commented Jun 20, 2020

Is it possible to support SharedArrayBuffers?

@zandaqo
Copy link
Owner

zandaqo commented Jun 20, 2020

@eranimo Sure, most all structures using buffers extend interfaces such as TypedArrays or DataView, and as far as I know, you can use those with SharedArrayBuffers same as ArrayBuffers, you'll just have to create the said buffer first and instantiate the structure on it, e.g. a grid using a shared buffer:

const { GridMixin } = require('structurae');
const Grid = GridMixin(Int8Array);
// let's get the required byte length of a buffer to hold a grid of 16 rows and 8 columns
const length = Grid.getLength(16, 8);
// create a shared buffer of that length
const sharedBuffer = new SharedArrayBuffer(length);
// instantiate our structure using the sharedBuffer
const grid = new Grid({ rows: 16, columns: 8 }, sharedBuffer)

@eranimo
Copy link
Author

eranimo commented Jun 21, 2020

That seems to work for ObjectView but not MapView. After setting any field in a MapView getting it right after always returns undefined. The buffer looks empty.

@zandaqo
Copy link
Owner

zandaqo commented Jun 22, 2020

That's true, MapView is a special case here for now: it lacks the ability to write into an existing buffer and MapView.from creates a new one every time. For this particular case there is a workaround though, set MapView.maxView to a DataView that uses a SharedArrayBuffer:

const SomeMapView = MapViewMixin({ $id: 'SomeMapView, type: 'object', properties: { a: { type: 'number' } } });
SomeMapView.maxView = new DataView(new SharedArrayBuffer(8192));
// now all instances of SomeMapClass will use SharedArrayBuffer instead of ArrayBuffer
// you can set MapView.maxView as well if you want to use shared buffers in all map views.
// the size of the buffer is the maximum possible size of a single view
// it defaults to 8192 but can be any supported size

const view = SomeMapView.from({ a: 1 });
view.buffer instanceof SharedArrayBuffer
//=> true

@zandaqo
Copy link
Owner

zandaqo commented Jul 9, 2020

In the latest version (3.2.0) I've added support for nesting MapViews and now MapViews can be written within existing ArrayBuffers. That is, we can use SharedArrayBuffers with MapViews the same way as with ObjectViews:

const SomeMapView = MapViewMixin({ $id: 'SomeMapView', type: 'object', properties: { a: { type: 'number' } } });
const requiredLength = SomeMapView.getLength({ a: 1 });
const buffer = new SharedArrayBuffer(requiredLength);
const view = SomeMapView.from({ a: 1 }, new DataView(buffer));
view.buffer instanceof SharedArrayBuffer
//=> true

Unlike the workaround with MapView.maxView, this way we can choose to use normal or shared buffers for each instance.

@eranimo
Copy link
Author

eranimo commented Aug 30, 2020

Thanks, that does seem to work. What about ArrayView?
For context, I'm using your excellent library to build an entity component system on top of SharedArrayBuffers (components implemented as MapViews) that works across many Workers and the main thread.

@zandaqo
Copy link
Owner

zandaqo commented Aug 31, 2020

Thanks, that does seem to work. What about ArrayView?

The same principle, just use the max amount of items as an argument to get the required length:

const SomeArrayView = ArrayViewMixin(SomeObjectView);
const requiredLength = SomeArrayView.getLength(10); // the length of a buffer to hold 10 object views
const buffer = new SharedArrayBuffer(requiredLength);
const view = SomeArrayView.from([], new DataView(buffer));
view.buffer instanceof SharedArrayBuffer
// true

In general, all view types have *View.getLength method to get the required buffer size in bytes. For ObjectView we don't need any arguments because the size is fixed. For MapView and VectorView we supply the object or array that's being encoded, since the size depends on the value. For ArrayView we provide the amount of items it needs to hold since all items are of the same size.

For context, I'm using your excellent library to build an entity component system on top of SharedArrayBuffers (components implemented as MapViews) that works across many Workers and the main thread.

I'm glad you find it usefull. Truth be told, I don't have much experience with SharedBuffers per se, since they were disabled in browsers shortly after their introduction. However, I do use the views with pre-allocated buffers especially when integrating with WebAssembly, hence, the ability to use existing buffers was important and it should work equally well with normal and shared buffers alike.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants