I'd like to get some numbers on what kind of build times people are seeing when building things with Roc.
This is very open-ended! I'm just trying to get a sense of what people are seeing in practice, since I care a lot about build times being fast but since I'm focused on compiler work right now, I don't have a sense of what people are experiencing on their projects. :smile:
Feel free to post any numbers you have in this thread! It's helpful to know if they're cold builds (--no-cache) or rebuilds, and also ideally what system you're running on and what roc version outputs.
Building Joy TodoMVC:
roc build --target=wasm32 --no-cache app.roc
roc build
✓ Resolving Dependencies 484ms
✓ Parsing 8ms
✓ Name Resolution 3ms
✓ Type Inference 50ms
✓ Compile-Time Evaluation 3ms
Monotype Lowering 1ms
LIR Generation 0ms
LIR Passes 0ms
ARC 0ms
Static Data 0ms
x64 Instruction Generation 0ms
Execution 1ms
Store Results 0ms
✓ Monotype Setup 0ms
✓ Root + Wrapper Setup 0ms
✓ Specialization Lookup + Reservation 1ms
✓ Dispatch Evidence 5ms
✓ Body Graph Setup 6ms
✓ Body Traversal + Binding 58ms
✓ Body Type Graph 0ms
✓ Body Call Dispatch 0ms
✓ Body Draft IR Construction 0ms
✓ Body Reachability + Proofs 0ms
✓ Body Source Mapping 0ms
✓ Body Local Procedure Context 0ms
✓ Body Sealing + Commit 5ms
✓ Procedure Completion 0ms
✓ Layout Requests 0ms
✓ Static Data Requests 0ms
✓ Monotype Finalization 0ms
✓ Closure Lifting 0ms
✓ SpecConstr 15ms
✓ Lambda-Set Solving 7ms
✓ Inline Planning 0ms
✓ LIR Generation 1ms
✓ LIR Passes 2ms
✓ ARC 1ms
✓ Static Data 0ms
✓ LLVM IR Generation 6ms
✓ LLVM Optimize + Emit 406ms
✓ Linking 7ms
roc build 1.1s
0 errors and 0 warnings found in 1.1s while successfully building:
app.wasm
Edited source with cache:
roc build --target=wasm32 app.roc
0 errors and 0 warnings found in 552ms while successfully building:
app.wasm
NixOS, x86, AMD 9950X, Roc 94cbed386c51a8739ced3be76e7ab7b84dd22852.
Check and test times too? Test times in particular I noticed are a bit on the slower side, especially for the relatively small number and libraries I run these for.
Using Roc build from nix flake, revision 38c51448b837ae3fd1b606805ef2b254b196596e
# roc check --no-cache rvn/main.roc
No errors found in 627ms for main.roc
# roc test --no-cache rvn/main.roc
All (82) tests passed in 9832.2 ms.
# roc check --no-cache gregorian/main.roc
No errors found in 501ms for gregorian/main.roc
# roc test --no-cache gregorian/main.roc
All (48) tests passed in 4508.5 ms.
those test times are very surprising! I'll look into that, thanks :+1:
Linux x64 AMD Ryzen 7 9700X 8-Core Processor
Screwbot on a gaming rig
Makes me think we should add a flag for build times in a machine readable format like JSON
| $ roc build --output="./bt" --opt=speed apps/burntranscribe/main.roc
roc build
✓ Resolving Dependencies 3ms
✓ Parsing 9ms
✓ Name Resolution 18ms
✓ Type Inference 295ms
✓ Compile-Time Evaluation 1.2s
Monotype Lowering 1.1s
LIR Generation 78ms
LIR Passes 2ms
ARC 3ms
Static Data 1ms
arm64 Instruction Generation 11ms
Execution 11ms
Store Results 1ms
✓ Monotype Setup 0ms
✓ Root + Wrapper Setup 0ms
✓ Specialization Lookup + Reservation 8ms
✓ Dispatch Evidence 0ms
✓ Body Graph Setup 37ms
✓ Body Traversal + Binding 785ms
✓ Body Type Graph 0ms
✓ Body Call Dispatch 0ms
✓ Body Draft IR Construction 0ms
✓ Body Reachability + Proofs 0ms
✓ Body Source Mapping 0ms
✓ Body Local Procedure Context 0ms
✓ Body Sealing + Commit 590ms
✓ Procedure Completion 2ms
✓ Layout Requests 0ms
✓ Static Data Requests 0ms
✓ Monotype Finalization 0ms
✓ Closure Lifting 1ms
✓ SpecConstr 854ms
✓ Lambda-Set Solving 209ms
✓ Inline Planning 0ms
✓ LIR Generation 27ms
✓ LIR Passes 287ms
✓ ARC 72ms
✓ Static Data 0ms
✓ LLVM IR Generation 1.4s
✓ LLVM Optimize + Emit 10.0s
✓ Linking 285ms
roc build 15.9s
Same timings with --no-cache on the second run. App is 3.5k LoC, Platform is 3k LoC Roc + 71k LoC Rust, Macbook M1
$ roc build --no-cache --output="./bt" --opt=speed examples/realworld/main.roc
roc build
✓ Resolving Dependencies 3ms
✓ Parsing 6ms
✓ Name Resolution 8ms
✓ Type Inference 105ms
✓ Compile-Time Evaluation 12ms
Monotype Lowering 5ms
LIR Generation 2ms
LIR Passes 0ms
ARC 1ms
Static Data 0ms
arm64 Instruction Generation 1ms
Execution 2ms
Store Results 0ms
✓ Monotype Setup 0ms
✓ Root + Wrapper Setup 0ms
✓ Specialization Lookup + Reservation 3ms
✓ Dispatch Evidence 4ms
✓ Body Graph Setup 12ms
✓ Body Traversal + Binding 258ms
✓ Body Type Graph 0ms
✓ Body Call Dispatch 0ms
✓ Body Draft IR Construction 0ms
✓ Body Reachability + Proofs 0ms
✓ Body Source Mapping 0ms
✓ Body Local Procedure Context 0ms
✓ Body Sealing + Commit 43ms
✓ Procedure Completion 1ms
✓ Layout Requests 0ms
✓ Static Data Requests 0ms
✓ Monotype Finalization 0ms
✓ Closure Lifting 2ms
✓ SpecConstr 54ms
✓ Lambda-Set Solving 56ms
✓ Inline Planning 0ms
✓ LIR Generation 137ms
✓ LIR Passes 92ms
✓ ARC 27ms
✓ Static Data 1ms
✓ LLVM IR Generation 641ms
✓ LLVM Optimize + Emit 5.1s
✓ Linking 96ms
roc build 6.6s
Example is 1.2k LoC, platform is 1.3k LoC
@Karl @Jasper Woudenberg @Niclas Ahden out of curiosity, do you normally use roc build when building for development? or do you use roc without build to just build and run? or something else?
I've been using the CLI as listed without the --no-cache just because 5-15s doesn't really bother me (Rust...) but I mean to switch over to just running it in roc for the hot reload. I haven't done it yet because it's not picking up the changes. I think it's the main loop not terminating and it'll work if I tweak it but I haven't gotten around to it. I've been very platform focused on both projects so I haven't been doing a lot of app work and that would change the motivation.
The 7min timing for rebuilding the Roc compiler (which is like 5:50 LLVM) does annoy me but that's a matter of Apple silicon getting zig incremental builds.
ok maybe we should change the default...roc build is actually doing optimized builds, which is not necessary - if you do roc build --opt=dev it will not use LLVM and will be way faster
the reasoning for making --opt=speed the default for roc build was the assumption that roc without build would be used for normal development, and once you wanted to do roc build it was probably because you were ready to actually release, so it would be helpful to make it so you couldn't forget to do --opt=speed but maybe that's actually not the right default after all :sweat_smile:
I think that was the right decision. I do it this way because I run compiled languages in release unless it's slow enough to really disrupt things and I'm not annoyed to the point of making the change. As mentioned, I do plan on switching over to the base roc for dev once I figure out why the hot reload isn't getting picked up on my platforms which is my issue and not roc.
oh you shouldn't need to do anything
it might be the way it's structured though - basically what happens is that when roc detects a change to the entrypoint function(s) your app provides to the platform, it just swaps them out for the new ones so that the next time the platform calls them, it'll get the new ones
so if you're not seeing any hot loading, I'm guessing it's because the platform only calls those functions once on startup?
They're both on an event loop so I think I have to trampoline the loop or something.
does the event loop call them repeatedly? :thinking:
Richard Feldman said:
Karl Jasper Woudenberg Niclas Ahden out of curiosity, do you normally use
roc buildwhen building for development? or do you userocwithoutbuildto just build and run? or something else?
I haven't used roc build much yet because I've been working on libraries. Now I'm back to working on my platform jay, there I don't expect to use roc build much either because the nature of jay is that you run it as a script.
Richard Feldman said:
the reasoning for making
--opt=speedthe default forroc buildwas the assumption thatrocwithoutbuildwould be used for normal development, and once you wanted to doroc buildit was probably because you were ready to actually release, so it would be helpful to make it so you couldn't forget to do--opt=speedbut maybe that's actually not the right default after all :sweat_smile:
I’m using it exactly as you describe, so I’m happy with the current situation :ok:
ok then I'll leave it as-is for now!
Tested on a Mac:
MacBook Pro M1 Max: Full TodoMVC build
MacBook Pro M1 Max: build --watch on a small change
nice!
Here's an example from roc-pg:
Roc version: nightly-2026-August-05-24f0b47
Source: https://github.com/sandprickle/roc-pg/blob/migrate-zig-compiler/examples/query.roc
MacBook Air M1
HyperFine output
roc build output
Dev build is just a hair over 700ms
I built the robot arm example on Windows on Thinkpad T14 with Ryzen 7 PRO 4750U
38.7s for --no-cache
31.4s with cache
opt=dev 12s, but it crashes
also, the program itself takes 120mb of RAM, I don't know if that's a lot to be honest, but it seems quite a bit for a raylib app. When I wiggle the arm fast, it grows to above 200 sometimes. I wanted to take a screenshot, but now it just hovers around 130-140 no matter what I do :)
so I don't think that the cache is doing much on Windows, and the compile times are a bit long for my taste - I hope that's just a quirk of the almost numbered version :)
https://github.com/lukewilliamboswell/terrocotta/blob/screwbot-showcase/examples/counter.roc
roc --opt=dev .\examples\counter.roc takes 3s, almost 18s for the speed build
also, the counter app itself uses 70mb of ram, which is more in line of what I would expect - C# and WPF app with a counter would probably stay under 30mb though
Thank you @Krzysztof Skowronek would you mind sharing roc version
PS C:\Users\KrzysztofSkowronek> roc --version
Roc compiler version nightly-2026-08-12-606470f
counter.cs
counter_winforms.cs
out of curiosity I made a counter example in C#, if you want you can run it with dotnet run -c release counter.cs (only on Windows)
wpf version uses 60mb, winforms is 10mb :D
thanks, I'll take a look at these!
I'm surprised lss and monotype lowering are taking so long. The linking thing should be an easy fix.
FWIW, here's the execution time of 110 exercises using roc test --opt=dev versus roc test --opt=speed. I had to use a log-log scale because the timings vary wildly. There are a few outliers on both sides:
![]()
Since I'm using roc test, this includes both the build time and the execution time.
which ones are the outliers? I can look at those first
Thanks @Richard Feldman . Here are the exercises that take over 1 second with --opt=speed and that are over 20 times slower than with --opt=dev:
pov: dev 36 ms -> speed 85,801 ms (manipulates a binary tree)gigasecond: dev 500 ms -> speed 12,796 ms (uses the isodate library)meetup: dev 414 ms -> speed 11,024 ms (uses the isodate library)micro-blog: dev 324 ms -> speed 6,874 ms (uses the unicode library)two-bucket: dev 63 ms -> speed 1,315 ms (includes a recursive search)affine-cipher: dev 39 ms -> speed 1,825 msforth: dev 50 ms -> speed 1,158 ms (includes a recursive search)go-counting: dev 48 ms -> speed 1,070 ms (includes a recursive search)Note: the exercises that are much slower with --opt=dev than with --opt=speed are the compute-heavy exercises, so it makes sense, no issue there.
Lastly, there are two exercises that work fine with --opt=dev but crash with --opt=speed: robot-name and pov.
I'm curious about the compute-heavy ones too, actually!
Sure, here they are:
alphametics: dev 10,506 ms -> speed 1,221 msperfect-numbers: dev 7,044 ms -> speed 649 msnth-prime: dev 4,770 ms -> speed 659 mssum-of-multiples: dev 3,921 ms -> speed 691 msHere's the full table if you're interested:
dev_speed_comparison.tsv
nice, thank you! I'm gonna dig into these and see what room for improvement there is
if you're curious to try it, the --specialize=no flag is an experimental thing that may help with roc test times specifically
might also make them worse right now though, not sure :laughing:
I tried --specialize=no with roc test, but I got a lot of crashes, and a few freezes:
accumulate, acronym, affine-cipher, all-your-base, allergies, alphametics, anagram, armstrong-numbers, atbash-cipher, binary, binary-search, binary-search-tree, bob, bowling, change, circular-buffer, clock, collatz-conjecture, complex-numbers, connect, crypto-square, custom-set, darts, diamond, difference-of-squares, dominoes, eliuds-eggs, error-handling, etl, flatten-array, flower-field, food-chain, forth, gigasecond, go-counting, grains, grep, hamming, hello-world, hexadecimal, high-scores, house, isbn-verifier, isogram, killer-sudoku-helper, kindergarten-garden, knapsack, largest-series-product, leap, list-ops, luhn, matching-brackets, matrix, meetup, micro-blog, minesweeper, nth-prime, nucleotide-count, ocr-numbers, octal, palindrome-products, pangram, pascals-triangle, perfect-numbers, phone-number, pig-latin, poker, pov, prime-factors, protein-translation, proverb, pythagorean-triplet, queen-attack, rail-fence-cipher, raindrops, rational-numbers, rectangles, resistor-color, resistor-color-duo, rest-api, reverse-string, rna-transcription, robot-name, robot-simulator, roman-numerals, rotational-cipher, run-length-encoding, saddle-points, say, scrabble-score, secret-handshake, series, sgf-parsing, sieve, simple-linked-list, space-age, spiral-matrix, square-root, strain, sublist, sum-of-multiples, tournament, transpose, triangle, two-bucket, two-fer, variable-length-quantity, word-count, word-search, wordy, yacht, zebra-puzzlechange, high-scores, luhn, poker, prime-factors, yachtall-your-base, binary-search, circular-buffer, collatz-conjecture, difference-of-squares, food-chain, grains, hello-world, isogram, knapsack, leap, matrix, proverb, queen-attack, raindrops, resistor-color, resistor-color-duo, say, space-age, square-root, triangle, two-fer, wordy, zebra-puzzleThe execution times for the ones that worked are a bit suspicious, with a vast majority equal to 130.1 ms:
One last thing: for two exercises, roc test --opt=speed crashes while roc test --opt=dev works fine. I'm trying to reproduce the issue with a minimal code example, but it's not easy.
I've got some really fat apps in the roc-pdf package... I'll do a more structured survey and share the results here.
All the results and configuration is in this JSON file, and I included an analysis also from codex
https://gist.github.com/lukewilliamboswell/07b088aebb7d1ec25fa663f0800c06eb
![]()
This is using yesterday's nightly
roc build tests/gate3_pdf_facade_chunks_negative/main.roc --opt=dev
Something is a bit fishy with the speed of roc test --opt=dev. Here's a graph comparing the test time last week (August 16 in NZ) with the test times today (August 24 in NZ) across all Exercism exercises:
![]()
Notice that the times are comparable above 100ms (although a bit higher today than last week), but there's no longer any test time below roughly 130ms.
Here's the same graph but with --opt=speed (for both dates).
![]()
Edit: I updated the graph because I had made a mistake. Fixed now.
Lastly, here's --opt=dev versus --opt=speed today:
![]()
This looks significantly worse for most exercises, except 5.
How are you collecting the time, is this time roc test ...?
Definitely smells like a regression somewhere is putting a floor on the test time. My guess is we broke the caching for the builtins somehow (totally wild guess)
I'm just grabbing the times displayed in the test outputs, such as: All (14) tests passed in 1032.6 ms.
Another example of where we should add that machine readable output flag ... did we ever make a GH Issue for that?
I'll have a look
Is there a better format for machine readable stats than JSON?
Oh ... sqlite3? :grinning_face_with_smiling_eyes:
Now we're cooking with gas
Btw, the two exercises that used to crash with --opt=speed but not with --opt=dev now work fine with both. However, the sgf-parsing exercise now causes a segfault when run with --opt=speed, I opened issue #10912.
I think my head just exploded with ideas ... I've been sitting on this general theme of a performance observatory and I think we just hit on a goldmine for how to actually do it properly.
Like is there any better binary encoding stable widely supported data format than sqlite?
@Luke Boswell sqlite-everything ^^ https://fzakaria.com/2026/08/23/your-executable-is-a-sqlite-database
Matthieu Pizenberg said:
Luke Boswell sqlite-everything ^^ https://fzakaria.com/2026/08/23/your-executable-is-a-sqlite-database
Circling back on this here is an idea using sqlite for roc-ray https://github.com/lukewilliamboswell/roc-ray/issues/178
mhh what exactly does the "specializing" step survey, so in case it takes quite long, what's the likely root cause to investigate?
![]()
In specialization generic code is transformed into specific faster version(s) based on known information like fixed arguments and known types.
guess it running for 14 minutes would indicate something being off that I need to investigate :sweat_smile:
what's the likely root cause
A bunch of generic functions that get called from a bunch of different paths.
Are you using the release a compiler or a debug build?
nightly-2026-08-13-2fdd90e
Feel free to toss the source code in an issue, we can minimize and investigate
will first try to update and see if I can locate the issue
Yeah, we've landed a lot of fixes since 08-13
yeah 14 minutes is prob some pathological compiler thing that needs to be fixed, not a problem with your code :smile:
It looks like roc test is significantly faster today than 3 days ago with both --opt=speed and --opt=dev:
![]()
![]()
Side-note: please let me know if these plots are not useful, I don't want to spam everyone. :sweat_smile:
I'm loving the plots!
Curious that dev has a floor on it? oh that was the 24th version only and we fixed that?
Thanks @Luke Boswell, I'll continue posting those then. :+1:
Yes, the 24th had an issue where the time was at least ~130ms. This has been fixed, apparently.
Yeah I saw that fix roll through https://github.com/roc-lang/roc/pull/10916
yeah I had something based on your previous plots
but I've been moving so I'm very disorganized right now :sweat_smile:
I'm not keeping track of what fixes are in what state haha
I'm trying to migrate a large app and found this quadratic behaviour: https://github.com/roc-lang/roc/issues/10978
My server takes 1m 33s to build, so I've been troubleshooting on that. The client hasn't finished after 30 min, so I think there may be more issues lurking, which I'm trying to reduce now!
Troubleshooting the client (wasm) build led me to this:
https://github.com/roc-lang/roc/issues/10979
https://github.com/roc-lang/roc/issues/10980
These two together seems to balloon the wasm size which makes LLVM take way too long (more than 30 min in my case). dev builds give me 100+ MB of wasm, so I technically can ship, but ... maybe, maybe :sweat_smile:
phew! I've been pulling my hair trying to get this app to compile, but it's been hanging in compilation and/or OOM:ing, and the issues were difficult to minimally reproduce. I've finally narrowed down these two culprits :raised_hands:
https://github.com/roc-lang/roc/issues/11072
https://github.com/roc-lang/roc/issues/11073
There may be more hiding in there, but I'm throwing in the diagnostics towel at this point :sweat_smile:
wow, thank you for the repros - will be super helpful to fixing! :smiley:
One for Roc and Roc for all! :D
Thanks Richard for solving both the performance issues and the compilation bugs! I'm heading back out on my adventure! :)
Fixing the issues above made a huge difference! The compilation times are way faster, from 16 min down to 82.5s, 7 min down to 37.9s, and the clients used to stall/OOM and now they're at 11-14 seconds!
Here are some new numbers:
┌─────────────────────────┬──────────────────┬───────────┬─────────────┬─────────────┐
│ entrypoint │ roc check (cold) │ --opt=dev │ --opt=speed │ speed ÷ dev │
├─────────────────────────┼──────────────────┼───────────┼─────────────┼─────────────┤
│ server/main.roc │ 0.75s │ 82.5s │ 249.5s │ 3.0× │
├─────────────────────────┼──────────────────┼───────────┼─────────────┼─────────────┤
│ jobs/main.roc │ 0.34s │ 37.9s │ 197.9s │ 5.2× │
├─────────────────────────┼──────────────────┼───────────┼─────────────┼─────────────┤
│ client/Portal/main.roc │ 0.28s │ 11.7s │ 135.8s │ 11.6× │
├─────────────────────────┼──────────────────┼───────────┼─────────────┼─────────────┤
│ client/Website/main.roc │ 0.44s │ 13.3s │ 184.2s │ 13.9× │
└─────────────────────────┴──────────────────┴───────────┴─────────────┴─────────────┘
Now that I can build the project I was able to identify and minimally reproduce two more perf issues:
https://github.com/roc-lang/roc/issues/11100
https://github.com/roc-lang/roc/issues/11103
This progress is so exciting! :smile:
Thank you Richard! Fixing those gave another huge boost, making it between 1.18 and 3.9x faster! Here are the numbers:
┌─────────────────────────┬───────────┬─────────────────────┬───────────────────────┬─────────────┐
│ Entrypoint │ roc check │ --opt=dev │ --opt=speed │ speed ÷ dev │
├─────────────────────────┼───────────┼─────────────────────┼───────────────────────┼─────────────┤
│ server/main.roc │ 0.78s │ 36.4s (2.3× faster) │ 202.0s (1.24× faster) │ 5.5× │
├─────────────────────────┼───────────┼─────────────────────┼───────────────────────┼─────────────┤
│ jobs/main.roc │ 0.32s │ 9.6s (3.9× faster) │ 167.7s (1.18× faster) │ 17.5× │
├─────────────────────────┼───────────┼─────────────────────┼───────────────────────┼─────────────┤
│ client/Website/main.roc │ 0.43s │ 12.9s (unchanged) │ 182.9s (unchanged) │ 14.1× │
├─────────────────────────┼───────────┼─────────────────────┼───────────────────────┼─────────────┤
│ client/Portal/main.roc │ 0.29s │ 12.0s (unchanged) │ 135.5s (unchanged) │ 11.3× │
└─────────────────────────┴───────────┴─────────────────────┴───────────────────────┴─────────────┘
To make it more interesting I thought we could start tracking "lines/s", so here's a breakdown of the project structure:
┌────────────────┬─────────┬─────────────┬─────────┬──────────┐
│ entrypoint │ modules │ total lines │ in-repo │ external │
├────────────────┼─────────┼─────────────┼─────────┼──────────┤
│ server │ 230 │ 55,123 │ 42,942 │ 12,181 │
├────────────────┼─────────┼─────────────┼─────────┼──────────┤
│ jobs │ 143 │ 32,821 │ 20,464 │ 12,357 │
├────────────────┼─────────┼─────────────┼─────────┼──────────┤
│ client/Website │ 96 │ 23,196 │ 15,695 │ 7,501 │
├────────────────┼─────────┼─────────────┼─────────┼──────────┤
│ client/Portal │ 56 │ 19,014 │ 11,513 │ 7,501 │
└────────────────┴─────────┴─────────────┴─────────┴──────────┘
Here's how Roc performed before fixing the issues above:
┌────────────────┬────────┬───────────────┬─────────────┐
│ entrypoint │ lines │ dev │ speed │
├────────────────┼────────┼───────────────┼─────────────┤
│ server │ 55,123 │ 668 lines/s │ 221 lines/s │
├────────────────┼────────┼───────────────┼─────────────┤
│ jobs │ 32,821 │ 867 lines/s │ 166 lines/s │
├────────────────┼────────┼───────────────┼─────────────┤
│ client/Website │ 23,196 │ 1,744 lines/s │ 126 lines/s │
├────────────────┼────────┼───────────────┼─────────────┤
│ client/Portal │ 19,014 │ 1,627 lines/s │ 140 lines/s │
└────────────────┴────────┴───────────────┴─────────────┘
And here's a summary table of how it's doing now (same data as up top, but now with lines/s):
┌─────────────────────────┬────────┬───────────┬───────────────────────────────┬─────────────────────────────────┬─────────────┐
│ Entrypoint │ LOC │ roc check │ dev │ speed │ speed ÷ dev │
├─────────────────────────┼────────┼───────────┼───────┬─────────┬─────────────┼────────┬─────────┬──────────────┼─────────────┤
│ │ │ │ time │ lines/s │ improvement │ time │ lines/s │ improvement │ │
├─────────────────────────┼────────┼───────────┼───────┼─────────┼─────────────┼────────┼─────────┼──────────────┼─────────────┤
│ server/main.roc │ 55,123 │ 0.78s │ 36.4s │ 1,513 │ 2.3× faster │ 202.0s │ 273 │ 1.24× faster │ 5.5× │
├─────────────────────────┼────────┼───────────┼───────┼─────────┼─────────────┼────────┼─────────┼──────────────┼─────────────┤
│ jobs/main.roc │ 32,821 │ 0.32s │ 9.6s │ 3,422 │ 3.9× faster │ 167.7s │ 196 │ 1.18× faster │ 17.5× │
├─────────────────────────┼────────┼───────────┼───────┼─────────┼─────────────┼────────┼─────────┼──────────────┼─────────────┤
│ client/Website/main.roc │ 23,196 │ 0.43s │ 12.9s │ 1,792 │ ~ │ 182.9s │ 127 │ ~ │ 14.1× │
├─────────────────────────┼────────┼───────────┼───────┼─────────┼─────────────┼────────┼─────────┼──────────────┼─────────────┤
│ client/Portal/main.roc │ 19,014 │ 0.29s │ 12.0s │ 1,581 │ ~ │ 135.5s │ 140 │ ~ │ 11.3× │
└─────────────────────────┴────────┴───────────┴───────┴─────────┴─────────────┴────────┴─────────┴──────────────┴─────────────┘
Now that iteration time is faster it's easier to find more and more! I've been guiding a bunch of agents on this for the past five hours and this seems like a good place to stop. Hope this helps!
:smiley:
https://github.com/roc-lang/roc/issues/11127
https://github.com/roc-lang/roc/issues/11128
https://github.com/roc-lang/roc/issues/11129
https://github.com/roc-lang/roc/issues/11130
https://github.com/roc-lang/roc/issues/11131
https://github.com/roc-lang/roc/issues/11132
https://github.com/roc-lang/roc/issues/11133
https://github.com/roc-lang/roc/issues/11134
I feel like count and running total for allocations could be good metric to track too
This kind of stats work is why I built the roc ray performance observatory which dumps basically every metric we can find into a sqlite db
https://github.com/lukewilliamboswell/roc-ray/blob/main/docs/observatory.md
I'm not sure if you could do the same for Joy -- but maybe it's possible to have a second host impl (so production builds aren't too large) that bundles sqlite and produces that in-memory?
Also I would say this is idea very experimental -- I think I'm the only person who has used it at all so far.
That looks cool! Right now I'm just focused on getting the general low-hanging fruit chopped down, and then I can get into more Joy-specific instrumentation later :)
Two more:
https://github.com/roc-lang/roc/issues/11143
https://github.com/roc-lang/roc/issues/11144
I bet my favorite pair of gloves that solving the issues reported hitherto will bring about 1-second compile times!
Comparing August 27 vs September 7 speed with roc test --opt=dev:
![]()
Looks the same (or very slightly worse) for <50ms exercises, slightly better for 50ms to 1s exercises, and much better for the four exercises that used to take 4 to 10 seconds, and which now take 2-3 seconds.
There's no obvious change when comparing the same dates with roc test --opt=speed:
![]()
yeah --opt=speed isn't something I'm planning to try and improve until like 2027 :sweat_smile:
other than trying to keep it in the range of "doesn't take a gazillion years"
because llvm is so slow that basically nothing else will be noticeable until we are doing so many of our optimizations that we can drop llvm as a dependency, but that's a very big project
Richard Feldman said:
drop llvm as a dependency
I would not have guessed this was a goal. That sounds extremely ambitious.
Does that mean we'll eventually get an „advent of roc compiler optimizations“ series in the spirit of what Matt Godbolt did? :D
no concrete plans...I just have a general sense based on how optimizations have gone so far that at some point it will make sense
we already know how to emit machine instructions for all the targets we support (because of dev builds), and several Roc-specific optimizations already overlap with what LLVM does (because LLVM's version can't take into account information Roc's compiler has access to), and I think that's going to increase...at some point it becomes reasonable to look at "if we closed the gap, what would that do to our optimized build speed and binary size?
and I suspect it will turn out to make sense overall. But I could be wrong!
Richard is a beast! All of the issues above have been solved and it's now between 1.4x to 3.2x faster, again!
┌─────────────────────────┬────────┬───────────┬───────────────────────────────┬──────────────────────────────────┬─────────────┐
│ Entrypoint │ LOC │ roc check │ dev │ speed │ speed ÷ dev │
├─────────────────────────┼────────┼───────────┼───────┬─────────┬─────────────┼────────┬─────────┬───────────────┼─────────────┤
│ │ │ │ time │ lines/s │ improvement │ time │ lines/s │ improvement │ │
├─────────────────────────┼────────┼───────────┼───────┼─────────┼─────────────┼────────┼─────────┼───────────────┼─────────────┤
│ server/main.roc │ 57,901 │ 0.16s │ 16.9s │ 3,421 │ 2.2× faster │ 141.4s │ 410 │ 1.4× faster │ 8.4× │
├─────────────────────────┼────────┼───────────┼───────┼─────────┼─────────────┼────────┼─────────┼───────────────┼─────────────┤
│ jobs/main.roc │ 33,670 │ 0.10s │ 7.1s │ 4,768 │ 1.4× faster │ 75.4s │ 446 │ 2.2× faster │ 10.7× │
├─────────────────────────┼────────┼───────────┼───────┼─────────┼─────────────┼────────┼─────────┼───────────────┼─────────────┤
│ client/Website/main.roc │ 23,382 │ 0.09s │ 7.1s │ 3,275 │ 1.8× faster │ 70.5s │ 332 │ 2.6× faster │ 9.9× │
├─────────────────────────┼────────┼───────────┼───────┼─────────┼─────────────┼────────┼─────────┼───────────────┼─────────────┤
│ client/Portal/main.roc │ 19,515 │ 0.06s │ 6.7s │ 2,926 │ 1.8× faster │ 42.3s │ 462 │ 3.2× faster │ 6.3× │
└─────────────────────────┴────────┴───────────┴───────┴─────────┴─────────────┴────────┴─────────┴───────────────┴─────────────┘
I must apologize, however. I aimed to produce enough issues to bring about 1-second compile times, but it seems I failed. My gloves are available for pickup in Stockholm, Sweden, at Odenplan's metro station. I'm a man of my word :smile:
Here's some more potential:
https://github.com/roc-lang/roc/issues/11321
https://github.com/roc-lang/roc/issues/11322
https://github.com/roc-lang/roc/issues/11323
https://github.com/roc-lang/roc/issues/11324
https://github.com/roc-lang/roc/issues/11325
https://github.com/roc-lang/roc/issues/11326
https://github.com/roc-lang/roc/issues/11327
Cool, server.main looks like it's gone from 16 min down to 82.5s down to 16.9s now :grinning_face_with_smiling_eyes:
Indeed, it’s a tremendous improvement! And the clients went from hanging/OOM:ing to success in ~7s :partying_face:
love seeing the progress, thanks for measuring and reporting all these @Niclas Ahden! :smiley:
out of curiosity, are these all --no-cache builds? And are the lines/s measurements including dependencies (e.g. the platform and any app or package imports) or just the lines in the entrypoint file itself?
Hey this is cool https://github.com/roc-lang/roc/pull/11368
![]()
Richard Feldman said:
out of curiosity, are these all --no-cache builds?
They're not using --no-cache, but here's what that looks like:
┌─────────────────────────────┬──────────┬──────────┬─────────┬──────────────┐
│ Target │ no-cache │ cached │ saved │ cached speed │
├─────────────────────────────┼──────────┼──────────┼─────────┼──────────────┤
│ check server │ 0.66s │ 0.14s │ 0.52s │ 4.7× faster │
│ check jobs │ 0.37s │ 0.09s │ 0.28s │ 4.1× faster │
│ check client/Website │ 0.31s │ 0.08s │ 0.23s │ 3.9× faster │
│ check client/Portal │ 0.25s │ 0.05s │ 0.20s │ 5.0× faster │
├─────────────────────────────┼──────────┼──────────┼─────────┼──────────────┤
│ build server dev │ 18.20s │ 17.61s │ 0.59s │ 3.3% │
│ build jobs dev │ 7.64s │ 7.41s │ 0.23s │ 3.0% │
│ build client/Website dev │ 7.63s │ 7.40s │ 0.23s │ 3.0% │
│ build client/Portal dev │ 7.11s │ 6.92s │ 0.19s │ 2.7% │
├─────────────────────────────┼──────────┼──────────┼─────────┼──────────────┤
│ build server speed │ 152.75s │ 152.71s │ 0.04s │ 0.0% │
│ build jobs speed │ 81.70s │ 81.27s │ 0.43s │ 0.5% │
│ build client/Website speed │ 77.14s │ 77.01s │ 0.13s │ 0.2% │
│ build client/Portal speed │ 45.93s │ 45.71s │ 0.22s │ 0.5% │
└─────────────────────────────┴──────────┴──────────┴─────────┴──────────────┘
Surprisingly small difference :thinking: Could this be right?
Richard Feldman said:
And are the lines/s measurements including dependencies (e.g. the platform and any app or package imports) or just the lines in the entrypoint file itself?
Yes, they include everything: app code, deps, and the Roc part of the platform. In the table below you see "in-repo" which is app code, and "external" is deps+platform:
┌────────────────┬─────────┬─────────────┬─────────┬──────────┐
│ entrypoint │ modules │ total lines │ in-repo │ external │
├────────────────┼─────────┼─────────────┼─────────┼──────────┤
│ server │ 230 │ 55,123 │ 42,942 │ 12,181 │
├────────────────┼─────────┼─────────────┼─────────┼──────────┤
│ jobs │ 143 │ 32,821 │ 20,464 │ 12,357 │
├────────────────┼─────────┼─────────────┼─────────┼──────────┤
│ client/Website │ 96 │ 23,196 │ 15,695 │ 7,501 │
├────────────────┼─────────┼─────────────┼─────────┼──────────┤
│ client/Portal │ 56 │ 19,014 │ 11,513 │ 7,501 │
└────────────────┴─────────┴─────────────┴─────────┴──────────┘
it could be right, especially if we're invalidating too much cache...which file(s) are being the changed during the cached builds?
I'd also love to see --timings on those dev ones if you have the chance, to see where all the time is going after type-checking :smile:
oh also, we are currently only caching up through type checking - so actually those cache numbers make sense: big percentage improvement on type-checking, but since type-checking is a small percentage of total build time, it has a small impact overall
I have a design for caching later steps of compilation but the implementation will take awhile to land
Just thought I'd compare the roc test time using the latest nightly (2026-09-12) versus the first one I recorded back on August 8:
![]()
Virtually all exercises that used to take more than 100ms run much faster today.
https://gist.github.com/lukewilliamboswell/eea43a8c3e057a3c1da5c0b9e0312d76
~/roc_nightly-macos_apple_silicon-2026-09-18-1d982dc/roc build --no-cache --timings examples/package-stress.roc
Resolving Dependencies 9msType Checking 7_894msShared Lowering and Compile-Time Evaluation 7_116msSpecializing 3_067msStatic Data 0msLLVM IR Generation 157msLLVM Optimize + Emit 38_128msLinking 35ms~/Documents/GitHub/roc main $ ~/roc_nightly-macos_apple_silicon-2026-09-18-1d982dc/roc build --timings examples/package-stress.roc
Resolving Dependencies 3msType Checking 6_308msShared Lowering and Compile-Time Evaluation 5_161msSpecializing 2_533msStatic Data 6msLLVM IR Generation 142msLLVM Optimize + Emit 38_705msLinking 21ms~/roc_nightly-macos_apple_silicon-2026-09-18-1d982dc/roc build --opt=dev --timings --no-cache examples/package-stress.roc
Resolving Dependencies 3msType Checking 5_498msShared Lowering and Compile-Time Evaluation 4_763msSpecializing 6msStatic Data 0msarm64 Backend 38msLinking 5ms Linking 26msMaybe we need to do more with these packages to stress it harder ... :shrug:
the last one is encouraging :smiley:
what happens if you modify the app file and rebuild? Still sub-second?
Last updated: Sep 24 2026 at 15:59 UTC