-
Notifications
You must be signed in to change notification settings - Fork 72
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
Efficient bulk transfer of a i8 array's contents into a JavaScript ArrayBuffer? #568
Comments
Okay this may be a duplicate of #395 |
If you can wrap the ArrayBuffer in a |
FWIW, if you decide to do the copying manually right now (instead of waiting for the instructions, which we hope will be added at at some point), the most performant way we could find to do this is by doing the copying in JS rather than in Wasm. This is because V8 optimizes JS calling Wasm better than Wasm calling JS. In dart2wasm, we generate this JS to copy one byte at a time from a Wasm (jsArray, jsArrayOffset, wasmArray, wasmArrayOffset, length) => {
const getValue = dartInstance.exports.$wasmI8ArrayGet;
for (let i = 0; i < length; i++) {
jsArray[jsArrayOffset + i] = getValue(wasmArray, wasmArrayOffset + i);
}
} The Wasm function called by this JS: (type $Array<WasmI8> (;0;) (array (field (mut i8))))
(func $_wasmI8ArrayGet (;87;) (export "$wasmI8ArrayGet") (param $var0 externref) (param $var1 i32) (result i32)
local.get $var0
any.convert_extern
ref.cast $Array<WasmI8>
local.get $var1
array.get_u $Array<WasmI8>) |
FYI, you can simplify that function by directly taking a (func $_wasmI8ArrayGet (;87;) (export "$wasmI8ArrayGet") (param $var0 (ref $Array<WasmI8>)) (param $var1 i32) (result i32)
local.get $var0
local.get $var1
array.get_u $Array<WasmI8>) If it's just this function, it won't make a difference. But I suspect you may have a bunch of these cases if you have one. ;) |
We can't use Wasm GC types in exports and imports,
It generates errors like:
I don't really know how beneficial |
Also, for the record, the V8 optimization that @osa1 mentioned currently only triggers for (nullable!) |
Huh. That's good to know. Does the optimization trigger for nullable I guess that means we could speed up some things if we change our codegen a bit. But also I'm reluctant to do so because it's going to remove incentives for the engines to improve on that. 🤷♂️ |
|
Note that that wasm-opt validation error has been removed in WebAssembly/binaryen#7019. After that, you can use any type on the boundary. However, types on the boundary are assumed to be public, which means that wasm-opt will not modify them, so it will inhibit some of the benefits (potentially a lot, depending on the type).
It is worth measuring how beneficial |
I’ve chosen to go the route of using a I was able to successfully implement some intrinsic functions into the WASM GC backend of the Java to WASM compiler I’m using (TeaVM) in order to allow WASM GC programs to still create a conventional WASM memory and load/store to specific addresses. I was then able to use the intrinsic functions I added to remake the essence of emmalloc, and it ended up working out better than I ever imagined. I believe I’ve squashed all the bugs and can try to get my app running in WASM GC now with some buffer classes based on the intrinsic memory load/store functions. The creator of TeaVM also said he plans to implement the same feature (“direct” Java NIO ByteBuffers using a WASM memory) into his compiler himself, he got pretty frustrated at me for suggesting the idea to him and forbid me from making any PRs on his repository related to this feature, so he’s clearly also planning to go the same route for TeaVM itself as some core feature. I also believe TeaVM is probably the most popular Java to WASM compiler right now among actual developers (not corporations trying to keep their old applets running), so it’s safe to say that accessing a conventional memory object from a WASM GC program is going to be a common practice for the near future in the JVM language crowd. However, we probably shouldn't let it become the "best" practice. I personally think its impossible to ever implement some dedicated ArrayBuffer load/store/copy instructions that are as fast as just using a slice of a You could always also create a way to make WASM GC arrays that are backed by ArrayBuffers, the only issue I see with this is it would be harder for the VM optimize the array load/store/copy instructions if WASM GC arrays could potentially come in multiple flavors like this, since the JIT compiler has no way of knowing ahead of time if the code is dealing with a native WASM GC array or a WASM GC array that is backed by an ArrayBuffer unless its handling a constant. |
Hello, first of all I am sorry if I've failed to do sufficient research on this topic and am fundamentally misunderstanding some core aspect of the WASM GC extension, please let me know if that is the case, but I've been browsing the spec for hours and can't come to any solid conclusions on this specific question of mine.
WebGL primarily accepts JavaScript typed arrays backed by ArrayBuffers for any functions that deal with large chunks of binary data to pass to the GPU efficiently (uploading textures, vertex buffers, glUniformMatrix, etc). For my specific application I am compiling Java to run in a browser, and being able to efficiently stream vertex data that is generated and uploaded on a per-frame basis is crucial for performance, since I am emulating the fixed function pipeline for a great deal of stuff.
Up to this point I have always just compiled Java to JavaScript and completely ignored WebAssembly under the assumption that any performance gains I would get would be outweighed by not having efficient garbage collection, therefore its common for me to prepare vertex data and textures inside byte or int arrays and then pass them to WebGL efficiently by getting a reference to the JavaScript typed array that backs the primitive numeric Java array at runtime.
I was very exited to learn that WASM GC is now available in Chrome and Firefox, however I've spent a considerable amount of time browsing the spec and I've come to the conclusion that it will probably no longer be possible to pass the data in a primitive numeric Java array to WebGL without copying the data, which in my opinion is still fine as long as performing the copy can be done efficiently.
However, it doesn't seem like it can be done efficiently, it seems like the only way to do it would be to program a
for
loop that copies the values out of the WASM GC array into a JavaScript typed array one value at a time. Doing it this way makes me want to bang my head against the wall, but I can't seem to find anyone else who's raised a similar concern yet. Is there any efficient way to perform a bulk copy of data from a numeric array in the WASM GC into an ArrayBuffer? If not, I think this feature would be a crucial addition to the spec in order to make porting code easier that was written for environments where this type of bulk transfer can be done efficiently.Thank you for your time, and I am sorry if I missed anything or there is already an open discussion about this. I did see some other open issues regarding bulk transfer of an array's contents but none seem to cover the case of actually getting the data out of WASM and into a JavaScript ArrayBuffer to pass to browser APIs.
The text was updated successfully, but these errors were encountered: