Stream: show and tell

Topic: roc-regex


view this post on Zulip Karl (Sep 07 2026 at 22:10):

roc-regex does what you'd expect. It's very fast.

pattern Roc ns Rust ns Roc / Rust what it exercises
Holmes 28350 27027 1.05x literal search, dense hits
Moriarty 10250 9969 1.03x literal search, rare hits
Sherlock|Holmes|Watson|… (8 names) 479750 425248 1.13x Teddy multi-literal scan
[A-Za-z]+ 1613400 2205824 0.73x one class repeated: no reverse automaton
[0-9]{2,4} 64250 108872 0.59x the same, over a sparse class
\bthe\b 209300 204994 1.02x prefix search plus word boundaries
\w+\s+\w+ 2055750 1544425 1.33x bare automaton, no accelerator
(\w+)@(\w+) 67350 68611 0.98x prefix search on a rare byte
\p{L}+ 2007550 2061462 0.97x Unicode class, non-ASCII decoding
.*Holmes 313550 644285 0.49x prefix search plus newline skipping

view this post on Zulip Karl (Sep 07 2026 at 22:13):

It pre-compiles the patterns at build time as a side product of Roc's constant folding, which I think is neat. I also tried to get it to beat the fastest HTTP parser+route matcher combo I know about, which was useful for finding startup overhead, but it's about 5 times slower. There just aren't enough bytes in a HTTP request for the higher speed matching to beat out the startup overhead for a highly tuned domain.

view this post on Zulip Karl (Sep 07 2026 at 22:17):

As a warning, these numbers are using my patched local Roc compiler so getting full performance for the scan heavy benchmarks will require this issue to be solved. These are also being run on a machine with 128bit SIMD vectors and Rust can take advantage of 256bit vectors while this cannot.

There's a comparatively hefty amount of overhead in these numbers from using checked array access so if those get elided it'll go faster. The two-word match at 1.33x and the multi-literal scan at 1.13x are the most direct apples to apples Roc vs Rust comparisons.

view this post on Zulip Richard Feldman (Sep 08 2026 at 01:35):

woooooow, super impressive!!!

view this post on Zulip Richard Feldman (Sep 08 2026 at 01:36):

I landed a fix for that issue btw - lmk if you can still get this perf on the new main!

view this post on Zulip Richard Feldman (Sep 08 2026 at 01:36):

this is really exciting stuff, @Karl! :smiley:

view this post on Zulip Karl (Sep 08 2026 at 04:15):

Things mostly hold up but I'm seeing a ~25% regression in the sparse literal row (Moriarty) which is the most directly affected by SIMD. If things were just not working then I'd see a regression across the other scanner rows but those numbers are stable. Looking into it.

view this post on Zulip Karl (Sep 08 2026 at 04:19):

Only shows up in the full benchmark binary, not in isolation so the fix you landed is working fine. I'll be reporting bugs related to the regex code over the next few days as I get them isolated with repros.

Claude says: "It is loop alignment. The code did not change at all. This is not a SIMD regression and not a codegen-quality regression. It is the alignment sibling of the register-pressure effect your notes already describe, where an unrelated edit moves a row that never runs the changed code."

"There is a genuine upstream item in this. LLVM can align loop headers, and Roc is evidently not asking it to."

I'll do another pass at slimming down the core of the matcher regardless but that's the current state of things.

view this post on Zulip TeaDrinkingProgrammer (Sep 08 2026 at 05:08):

I have a hobby project writing a regular expression language in Roc, so this will be usefull reference material :eyes:. Great work!

view this post on Zulip Krzysztof Skowronek (Sep 08 2026 at 14:36):

https://github.com/ieviev/resharp-dotnet

Have you heard about this? It was faster than rust in many cases (dotnet simd got really good in dotnet 10)

https://iev.ee/blog/resharp-how-we-built-the-fastest-regex-in-fsharp/ very nice article about the details and what it does well

The part about (a|ab)+ vs (ab|a)+ in a string like aababaabab is very interesting

view this post on Zulip Karl (Sep 08 2026 at 14:37):

This uses their algorithm.

view this post on Zulip Krzysztof Skowronek (Sep 08 2026 at 14:38):

Awesome!

view this post on Zulip Krzysztof Skowronek (Sep 08 2026 at 14:43):

I also have a noob question - why is roc not able to use the bigger simd vectors? Is it just for now or is it some fundamental issue?

view this post on Zulip Karl (Sep 08 2026 at 14:45):

Library support. Roc only implements the 128 bit SIMD set which has broad hardware support while 256 bit is more limited and Richard is worried about long term support/complexity.

view this post on Zulip Anton (Sep 08 2026 at 14:49):

I believe the 256 bit worry was mainly about different behavior on different devices.

view this post on Zulip Richard Feldman (Sep 08 2026 at 16:27):

that was a different thing

view this post on Zulip Richard Feldman (Sep 08 2026 at 16:27):

there are certain instructions we just have to be careful not to use (they're really niche though, fortunately) because they give different answers for the same inputs depending on whether you're using aarch64 vs x86-64 vs wasm

view this post on Zulip Richard Feldman (Sep 08 2026 at 16:33):

the 256-bit thing is basically:

so for now, 128-bit it is :smile:

view this post on Zulip Karl (Sep 12 2026 at 03:37):

Karl said:

I'll do another pass at slimming down the core of the matcher regardless but that's the current state of things.

I cleaned up the code today (the original quality was...not good, this is readable to me) and added another optimization for leading x+ patterns which is a bit golfing for my benchmark set but I think is reasonably general.


Last updated: Sep 24 2026 at 15:59 UTC