As for the wasm size, I think at least half of that is due to the Glk library, not the interpreter per se. Comparing the wasm file sizes of emglken 0.6 (using C RemGlk) vs. 0.7 (switched to remglk-rs), the Bocfel wasm went from 1.13 MB to 2.3 MB. Other interpreters grew by different amounts but the general trend is clear.
Of course, some of the growth may be due to changes in Bocfel and a smaller interpreter may be possible, but it appears that even “the subset of remglk-rs used by Bocfel” is currently over a megabyte of wasm. I’ve poked at it a little bit with twiggy and some things jump out to me:
246 KiB (ca. 10% of the whole file) is the “function names” section. Having this section is useful for debugging and for meaningful stack traces when users encounter errors (plus, I couldn’t have done this analysis without it), but it does inflate download size a bit. Probably not by 10% since it’s probably even more compressible than wasm code.
The biggest function is __wasm_call_ctors at 145 KiB. I don’t know what’s up with that but I think it comes from Bocfel and/or Emscripten support code, since Rust doesn’t have static constructors.
Using serde for JSON parsing and writing is very convenient but generates a ton of code, around 209 KiB total by my count. The RemGlk way of parsing into a JSON DOM and then picking out the fields that are required probably needs much less code.
Use of wasm-bindgen adds a custom metadata section of 65 KiB
There appears to be a fair amount of duplication between Rust and C++ code for basic standard library functionality, e.g. text and number formatting (printf and friends vs std::fmt) and parsing floating-point numbers from text.
A decently large chunk of Rust std::fmt code consists of Debug impls that are probably only needed for panic messages and could probably be reduced or avoided entirely.
Skimming over the sizes of the actually necessary Bocfel and glk routines, it feels like they could definitely be made smaller if code size was prioritized. Is it really necessary for Bocfel’s Options to pull in 20 KiB of code for a highly optimized sorting routine? Why does glk_set_terminators_line_event, which just copies a dynamic array of key codes into a field of the window, occupy 1476 bytes?
Looks like it should be possible to shave quite a bit off both Bocfel and remglk-rs, though it’s a mix of “easy win once you’ve spotted it” and “gotta rewrite this whole aspect of the library”.
This really wasn’t at all intended to prompt you to do that, but if you did decide to I wouldn’t complain!
Yes there are lots of potential size cuts that could be made to RemGlk-rs. Some things you mentioned like Serde may be large, but I think the benefits way outweigh the size cost, the easy and safety is just too good.
RemGlk-rs doesn’t use wasm-bindgen, but maybe Emscripten uses the same metadata section you found?
One other big thing is the unicode data tables. I think they’d be up to several hundred KB, however I think Bocfel doesn’t include them so it wouldn’t help here.
It was definitely the right choice during initial development. But given that the JSON protocol is now fully implemented and only evolves at a glacial pace, I think that it’s possible to write the JSON wrangling in a way that’s not much harder to maintain but has significant size benefits. It would be less efficient in a microbenchmark, but the messages probably aren’t big and frequent enough for that to matter.
Per cargo tree, it’s used via chrono. There’s probably decent savings to be had by (partially or entirely) pushing the date/time functions into JS just to have control of how much machinery is needed on the wasm side to talk to JS (necessary at least for the current system time and local UTC offset).
Yeah they don’t seem to be used in Bocfel, the relevant Glk functions aren’t included and the wasm module has “only” ca. 92 KiB of static data in total. When they are used, is there any problem with leaving those functions to JS (which already has the necessary tables), as done in previous versions of emglken?
I wrote RemGlk-rs in Rust in large part to avoid having to do any manual JSON parsing. I’m also intending to use Serde for autosaves with a binary format rather than JSON. So removing Serde is not something I anticipate considering, it’s really an integral part of the library. But maybe there are ways to optimise how I’m using Serde.
wasm-bindgen isn’t compiled by Emglken, and indeed the Chrono dependencies don’t include it for the emscripten target. That said, I’m not super happy with Chrono and would consider swapping it for something else.
Other than the performance hit from switching between WASM and JS (which would be identical to old versions of Emglken), not really. It will be the first thing I try to reduce file size.
I’m not doing any significant string formatting in RemGlk-rs and have just used format! as a convenience. It might be possible to cut this out.
I think exchanging some generics for dynamic dispatch would likely reduce the file size a lot, though I haven’t investigate the details yet.
Sorry, I was not sufficiently clear. The direct dependency is indeed not used on emscripten, but one of chrono’s dependencies also uses it and that is enabled when compiling to emscripten:
If you don’t use the wasm-bindgen stuff on the JS side, then I guess it’s not necessary and wouldn’t even work when called. But the Rust side of it is compiled in and there is a 67348-byte custom section called __wasm_bindgen_unstable in the emglken bocfel.wasm. I guess this is just dead weight that could be avoided by not using chrono?
If you were investigating the files from the npm package then it’s possible I used older dependencies than the fresh dependencies used by Parchment? Maybe it’s time to consider using lockfiles.
I’ve just released Bocfel 2.2.3, with the only change being a switch from the GPL to the MIT license. It’s out on bocfel.org and will be merged into Gargoyle shortly.
I thought about dual-licensing but in the end decided it’d be cleaner to just have the MIT; but I added a note to the README indicating its compatibility with the GPL so, hopefully, if anybody has concerns, they’ll at least see my position regarding license compatibility.
And actually, it’d have been triple-licensed, since I was already explicitly GPLv2 and GPLv3 before!