Swift wasm binary sizes?

Hey folks,

I had a need to delve into the world of WASM, and so I figured I'd see what Swift WASM is all about. Long story short, the environment that I'd like to deploy to is seriously memory constrained (less than 8MB). Now, thanks to the awesome work of @Max_Desiatov and others, I was able to produce a wasm binary from Swift source without too much struggle, but, it weighs in at 15 MB, well over my limit :frowning:

So, my question is, what's typical for the size of a swift wasm binary? Is there a way that I can thin mine out?

1 Like

We have a separate issue on GitHub dedicated to this: build wasm file is very big??? ยท Issue #7 ยท swiftwasm/swift ยท GitHub

In short, wasm-strip and wasm-opt as discussed in that GitHub thread can make your binary somewhat smaller. You could also avoid using Foundation to avoid some overhead from that library.

Right now there is a minimum size of a statically linked Swift binary dictated by the size of the ICU library and the fact that Swift compiler can't strip unused protocol conformances. The dependency on ICU can't be removed completely, as it is required to make String indexing work. This is an issue for Swift on all platforms, not just Wasm.

In the meantime, if binary size is critical for you, I'd recommend using Rust, or maybe even C/C++. Rust may be especially suitable, since their no_std mode allows compiling without Rust standard library altogether.

6 Likes

My understanding is that Rust's standard library is basically split in 2 parts: the core library, which you can't omit and is roughly equivalent to Swift's standard library. It also includes some Unicode tables (e.g. for case-folding). The other half is the std library, which includes files, networking and more, and is closer to Foundation than the Swift standard library.

So no_std Rust is conceptually not a whole lot different to Swift without Foundation. The protocol conformances/reflection metadata/ICU issues are probably a bigger deal, would you agree?

3 Likes

Ok thanks for the info, it seems that I could maybe thin it to under 8MB with some of the steps mentioned in that issue thread, but I think Iโ€™ll just move to rust for the time being for this instead.

Side topic : i had no idea wasm was a realistic target for swift at the moment. Is there any guide or documentation somewhere on what the experience is and how to get started on swift on those less known swift target platforms ? (i'm especially thinking about android, and wasm)

1 Like

We're* making some progress on dropping the ICU dependency, by the way. No promises of course, but I'm quite hopeful.

*not me specifically, I'm just watching excitedly while working on other stuff

14 Likes

It is absolutely incredible at how well it works. I have redesigned an entire client app using Swift WASM. The team behind SwiftWASM are beyond experts.

3 Likes

Interesting ! how did you deal with Foundation dependencies ?

Parts of Foundation are available when targeting Wasm (which is never written in all uppercase according to the WebAssembly spec). What isn't available through Foundation is mainly file access, locale/time zone information, and multi-threading. We're interested in supporting the latter, but currently Safari is the only browser without multi-threading support for WebAssembly. Our approach so far has been to target WebAssembly features that are available in all major browsers.

More info on Foundation support is available in the SwiftWasm book.

5 Likes

Thanks a lot for the answer.

Out of curiosity (and also to understand what amount of work we're talking here) : at which level do you need to work to support Foundation on Wasm ? Does Foundation rely on some kind of Posix standard, which you would need to emulate and then all its code could simply be linked against that layer ? Or are we talking about a complete rewrite ? Are you able to leverage work that has been done when porting Foundation to linux ?

Yes, it's not completely POSIX, but it's relatively close. It's called WASI.

Yes, we already leverage it, as long as it's not related to multi-threading, networking/sockets, or file access, because those features are sufficiently different from other platforms, or aren't standardized yet with WASI.

3 Likes

Reviving this thread because Iโ€™m wondering what impact removing the ICU dependency from the stdlib has made on SwiftWasm.

From what I can see, binaries built on swiftwasm.org are still ~4.5MB in size โ€“ is it using an older version of the compiler or did removing ICU just not make that much of a difference?

2 Likes

I believe swiftwasm.org has not updated to Swift 5.6 yet.

1 Like

There have been nightly snapshots. I haven't checked, but they should include removal of ICU, since upstream trunk is frequently merged into the SwiftWasm fork.

Ah yes, I believe the change is already in SwiftWasm.
I was specifically talking about the one used by the official online playground.

1 Like

In most cases removing ICU dependency won't have a significant impact on binary size. The ICU library was used for multi-megabyte Unicode data tables, which were basically "moved" from ICU to the Swift distribution itself.

In fact, for apps and libraries depending on Foundation, binaries compiled by SwiftWasm will become bigger because of this.

This is caused by Foundation still depending on ICU. Until it's updated to rely on Unicode data tables within Swift itself, we'll have to embed both the ICU Unicode data tables and a version of those embedded in Swift runtime/stdlib.

1 Like

I think you're underestimating how much data ICU had that we don't have anymore. You mention ICU having multi-megabytes of Unicode data tables where the stdlib's Unicode data tables only clock in at around 500kb (less than 1 megabyte let alone multi-megabyte).

3 Likes

I'm not debating this, and yes, I should've phrased it as "prohibitively expensive" instead of "multi-megabyte".

The main point is that people interested in reducing size of binaries produced by SwiftWasm are mainly targeting the browser environment. There we're competing with alternatives (AssemblyScript, Rust, C/C++) that can produce 2kb binaries for "Hello, World!" apps, or even less than 2kb when writing plain JavaScript.

In the past years we've received multiple requests for reducing binary size of binaries produced by SwiftWasm. People are saying it's a deal breaker for them and they can't even consider using Swift in the browser in anything close to production environments because of this. For them 500kb Unicode data tables is 500kb more than their available size allowance.

I'm only trying to manage their expectations, as apparently there's a common misunderstanding that with the removal of ICU all Unicode data tables were somehow completely removed as well, which is not true. And when Foundation is linked, it actually makes things worse with a substantial subset of these tables duplicated.

9 Likes

Just curious are developers looking to shrink the swift wasm binary size because of download (data) constraints on their servers or loading time constraints?

Load time on the client is one of the concerns. Even if the .wasm binary is cached by the browser, it still takes time to load the module into a Wasm VM, run JIT on it, and then execute, and this will happen every time on every page load. While some parts of this process may utilize caching, we can't always rely on that.

Because of this, apps built with SwiftWasm may feel slow, as they only can become interactive after the full loading process is complete, which may take seconds. Compare this to JavaScript and other languages compiled to Wasm, which have binaries with comparable functionality that are many times (or even orders of magnitude) smaller and can be loaded immediately without any lag noticeable by users.

Build time is also a big concern. Larger binaries take more time to build and link. Since Swift doesn't support hot reloading in general, our only viable option is to rebuild the binary from scratch and then reload it in the browser when developing.

When building apps with JavaScript/TypeScript and other languages compiled to WebAssembly, developers can see changes they make in their code in front of them in the browser almost instantaneously. With SwiftWasm, Unicode data tables need to be copied and relinked in the final binary, and then reloaded in the browser every time no matter how small was the change that the developer made.

4 Likes