Remix.run Logo
ayaros 9 hours ago

I'm working on a web app in JS, the sort of thing I think people on here will enjoy and I'll definitely share it when it's presentable. It's all in JS, including the graphical aspects of it (I'm drawing to a canvas). For per-pixel rendering I have 4 levels of nested loops. I feel like I've done everything in my power to optimize the process, and it's still not at the ideal speed. Really, it ought to be about 5-10 times faster than it is now.

I've been thinking of getting it into WASM somehow - it's really just one function that needs to be replaced by a WASM module - but one of the things which has given me pause are benchmarks like this, as well as other anecdotes, that demonstrate I'd only get a minor speedup from WASM. I guess part of this is due to modern JS engines being so optimized.

Just about all of the relevant code is adding and subtracting integers, but theres's some Boolean logic. There's also some multiplication in there that I could eliminate if I really had to, although at this point I'm not certain it would make a significant difference. Most of the data that contributes to the value of each pixel is spread across a tree of objects, and there's some polymorphism involved too. The point is it's not like I'm just dealing with a bunch of contiguous data in memory that is easily cached by the CPU, which might speed things up considerably. I guess I'm just venting now, lamenting a nightmare of my own creation...

com2kid 37 minutes ago | parent | next [-]

Don't write every pixel by hand to the screen. At high resolutions that just won't work. No modern UI does that anymore, they all use acceleration of some type. I ran into this trap when I tried to write a simple canvas game engine.

Just caching blobs of things that don't change frame to frame and got me a 5x-8x speedup, even if where I render those things at changes frame to frame.

catapart 7 hours ago | parent | prev [-]

For the record - I can't imagine any tangible value in writing a graphics engine (and form library) for an environment that was literally designed as a graphics engine for forms. But, that disclaimer aside, I've considered WASM for graphics for a game engine and I have had some trial success with allocating a shared array buffer, writing to it in WASM and reading from it at a js-friendly rate of 30-60 fps (whatever requestAnimationFrame is serving). The idea being that you treat the shared array buffer as your "screen" and write to it from the WASM as if it were an image buffer, while on the JS side you read it as an image buffer onto the canvas. Gives you a nice, finite, bandwidth and an easy to scale maximum for how much data you can pass through, and neither JS nor WASM ever has to pass any time-critical data.

Of course, this setup leaves a very thin JS client - it's essentially just rendering the same image over and over. So if you've already got everything done in JS, it would mean porting all of your functional code over to something that can compile into WASM. Not a huge lift, but not fun either.

And then, on the other hand, if it's just rendering you're seeing as a bottleneck, JS works pretty well with webGL and webGPU for simple form rendering. A little loop unrolling might be a lot easier than porting the rest of the app.

Anyway, best of luck on it!

lerp-io 2 hours ago | parent [-]

have you done any benchmarks to compare wasm performance with js? i have been doing some benchmark test cases after a large refactor of my game engine written in typescript that is built entirely on top of shared buffer arrays (using ECS pattern or so it’s called i guess). i’m done benching for a while now and am ok with results but i have also compared my handcrafted quadtree sort/query algorithm with off the shelf rust implementation that matched the test requirements and saw only 30% gain and figured anything lower than that would not be worth the effort assuming rust compiled to wasm is same or slower and never bothered to test compiled wasm because i could not figure out how to share my buffer arrays with the rust code using wasm bindgen fast enough to bother spending more time on more benching lol