Motoko WASM size diagnosing - Part 2

Im looking for some help/tips for figuring out what takes up so much space in my Motoko WASM output. The main issue is that I am going over the 2MB cap and I have many MOPS packages referenced and can’t seem to figure out the specific dependencies/code that are the issue or can be slimmed down. Manual inspection has been no luck and inspecting the WASM hasn’t been fruitful, except for one thing that I will mention below

  • I can cut my WASM size from 2.6 MB to 1.8 MB with ‘trimming’ the WASM of ‘local names’ and ‘function names’ using wasm-strip, which seem to be debugging artifacts, but they get generated when i use --release. Not sure if there is a setting to fix this or what I am doing wrong
  • I have tried to use a script to iterate through all the modules to build a WASM for each and find the big ones, but the big ones are ususally just the main files and I can’t tell the size of each imported module because multiple modules might share the same code, so its hard to tell
  • One issue i know of is the multi versioning of the same dependency from having to do version pinning to stop things from breaking, but that means if another library references a slightly different version, i essentially double to size. see Dependecy Hell. I have tried to fix this by getting all of my libraries on the same versioning scheme, but the issue is using other’s libraries along side it
  • Are there any specifics that would be helpful to know? like limitations of tree shaking/optimizations or how to organize modules for leaner/a la carte?
  • Are there any useful tools that exist in the Motoko compiler that could give me insight into some of this data?

@claudio @rvanasa

I think the limit is now 10MB, if you do a zipped upload. But could be wrong.

Section Wasm module limits

I think you may need to do a chunked upload for this, which I believe dfx probably handles for you (but maybe you need to instruct it do that)

See also gzip option Install | Internet Computer

ChatGPT tells me:

Great question — this is a common issue on the Internet Computer (ICP) when dealing with large WebAssembly (Wasm) modules.
Because the default dfx canister install command only supports uploading up to 2 MB per message, you need to split the Wasm and install it in chunks.

Here’s how you can do it properly.


:puzzle_piece: Problem Recap

When you try something like:

dfx canister install my_canister --wasm my_big_module.wasm

and your .wasm file is larger than 2 MB, you’ll get an error like:

The message exceeded the maximum size allowed (2MB).


:white_check_mark: Solutions

Option 1: Use ic-cdk-optimizer (if not already)

First, ensure your Wasm is optimized (this reduces size dramatically).

cargo install ic-cdk-optimizer
ic-cdk-optimizer target/wasm32-unknown-unknown/release/my_canister.wasm -o my_canister_opt.wasm

Then check the size:

ls -lh my_canister_opt.wasm

If still >2 MB, go to Option 2.


Option 2: Install in Chunks via dfx canister install --mode=reinstall --chunked (DFX ≥ 0.14.0)

Recent versions of DFX support chunked uploading for large Wasm modules.

Try:

dfx canister install my_canister --wasm my_canister_opt.wasm --mode reinstall --upgrade-unchanged --chunked

The --chunked flag tells dfx to automatically upload the Wasm in multiple 2 MB chunks.
:white_check_mark: Works seamlessly as long as your canister doesn’t exceed the overall subnet message size limit (≈96 MB total).


Option 3: Manual Chunk Upload Using dfx canister upload wasm-module

If you’re using an older DFX version (<0.14) or want full control:

  1. Upload the Wasm in chunks:

    dfx canister upload wasm-module my_canister my_canister_opt.wasm
    
    

    This command automatically handles chunking and uploading to the management canister.

  2. Install the uploaded module:

    dfx canister install my_canister
    
    

Option 4: Use ic-wasm + Compression (advanced)

You can sometimes compress Wasm further using ic-wasm:

cargo install ic-wasm
ic-wasm my_canister.wasm -o my_canister_comp.wasm shrink

Then upload the compressed version.


:brain: Summary

Method Description Works When
ic-cdk-optimizer Reduces size by stripping debug symbols Size slightly >2 MB
--chunked flag Automatically uploads in parts DFX ≥ 0.14.0
upload wasm-module Manual chunk upload Older DFX versions
Compression (ic-wasm) Shrinks further When close to 2 MB limit

If you tell me your DFX version (dfx --version) and how large your .wasm file is, I can give you the exact command that will work for your setup.

Would you like me to do that?

Of course it could be hallucinating. I coudn’t not see a chunked option with dfx canister install –help

I’ll try the ic-wasm but my 2.6mb is for an actor class that is being deployed from my canister, so it’s not really an option with dfx

Actually I don’t think that’ll work because it seems like the actor class wasm was just embedded as data. Not sure how actor classes get built

That’s exactly how they are built, embedded as wasm (without further optimization, e.g. no wasm-opt or other shrinker) and deployed on instantiation.

We could probably add an option to drop the name section if that helps but it will only get you so far and produce bad stack traces.

1 Like

A long time ago I came up with a pattern that avoids embedding the wasm. YMMV GitHub - crusso/alt-actor-classes: An alternative approach to actor classes, avoiding large binaries

1 Like

I think we could easily drop the names section, or the part for locals only that won’t affect stack traces.

Slight more ambitious would be to shell out to a user specified optimizer before embedding the optimized wasm.

1 Like

How does this avoid embedding the wasm? Is it because it is an actor class? I seemed to think it still did an embedded wasm?

I almost never use an actor..always do actor classes…it still seems to swell.

1 Like

Probably not as “easy” of a solution, but if we do a couple of things, this is what the icrc-118 and 120 suite was made for.

Ideal scenario:

  1. Maybe work with @ZenVoich to set up a way for mops to publish a wasm to your chosen namespace on 118 wasm registry canister(or maybe a seperate cli?)
  2. We need a thin shim utility class that can create a canister with cycles, set a 120 wasm orchestration canister to be the controller and then instruct a 120 wasm orchestrator to deploy the class with args and poll for it to be finished.
  3. Fire an event that your canister is installed—remove the 120 controller if you want or leave it for easy upgrades if you deploy new versions with migration.

@yrgg has Prometheus using this stack.

Yeah, icrc120 and icrc118 have been really great to work with tbh.

The prometheus app store allows a user to upload their wasm (chunks it into the 1MB limit via the app store cli). Instead of a shim though I’ve just put the canister provisioning and cycles management on the icrc120 (orchestrator) canister. Everything else (polling canister installation etc.) are already implemented in the icrc120 code that @skilesare created.

Has actually been incredibly simple. Currently I’m using it specifically for MCP servers, but it would be fun to build something like simple canister “kubernetes” or something. Write the code and test it with picjs, then build the wasm, and publish it via the command line, and then you can do things like rollbacks or deployments. There is a lot that can be done. Also, with blob storage the registry (icrc118) will be able to store wasms (and probably canister backups?) without the current canister storage limits and cost considerations.

1 Like

How much do you think can be removed?

Couple things

  • @claudio is there a restriction that would not allow an actor class being deployed with motoko (in the normal way) to be deployed in chunks if its too large
  • Looks like im going to evaluate these options and take a different approach for now
  • Anyone have thoughts on the other end to figure out what is taking up space/cut things that arent needed? Still unsure on what iptimatuons/tree shaking things there are

For now i am just going to directly deploy my actor class, but the idea is that I want a DAO to be able to call a factory canister to deploy the actor class canister as the owner/controller and be able to update the canister from that factory canister if it wants
But if thats an option maybe i have to look into just manual WASM deployment via governance or something? but not sure if thats feasible with a large WASM

But probably something the ICRC-118/120 can help with

That makes sense. Starting with a direct deployment is a good approach. Using a DAO through a factory canister sounds promising and ICRC-118/120 could definitely help manage ownership and upgrades later on.