Much of the difference in optimizability is that C/C++ allows you to do fairly extreme copy elision. This is important because memory bandwidth tends to be a major performance bottleneck.
C/C++ code can trivially reach into some remote bit of state and mutate it directly. The compiler rarely objects, no matter how ugly the access method or pattern. It is up to the programmer to make sure the invariants hold and the compiler can’t always see through it. These elided copies add up, though it usually has a maintainability and safety cost.
It is related to why functional languages tend to be mediocre for supercomputing in practice despite having nice properties for massive parallelism in theory.
Clojure creator Rich Hickey describes traditional imperative programming as "PLace Oriented Programming (PLOP)" when talking about how Clojure's approach is better for certain kinds of problems.
I agree with his point, and Clojure is my favorite programming language, but the computer itself is place-oriented. It runs on reading from and writing to locations in memory, and extracting maximum efficiency from it requires direct control over that memory.
Modern computers are exquisitely sensitive to spatial and temporal locality because many of the hardware features that speed your code up (for free!) take advantage of data or instructions that are “next to each other.” This can be a challenge in a functional program, because not only do you often have little control about where things go, the abstractions that make cool things possible (eg, closures, or lazy evaluation) naturally introduce indirection that hurts locality.
You can have great abstractions on the upper levels that lower to fast primitives. See the articles [1] Push your "ifs" up and "fors' down and [2] Vectorized interpreters for more details.
You could similarly argue that the computer is in fact fundamentally functional, since the core instruction cycle (Fetch Decode Execute) does not modify any memory.
The key insight into that system is that we've embedded a functional system within a mechanism that makes it imperative. The functional cycle can output commands to fetch it more data, that it can then use in the next cycle. It becomes imperative by the addition of time, which it needs because it's limited in the amount of input and output.
The execute stage very much modifies state; in fact it always modifies state. Any non-memory instructions modify registers, and any memory instructions either write to registers or to memory. Even a noop will increment the instruction pointer, so you're always modifying a register regardless.
Are there any common resources for this kind of access methods and patterns that specifically aim to bridge the soft-/hardware gap? The best one I can come up with is Performance Analysis and Tuning on Modern CPUs[0] by Denis Bakhvalov.
it seems like in this case SIMD is one of the biggest reasons libdeflate beats miniz-oxide. at that point you are either using assembly or a library of functions that maps in names and functionality to the assembly. I think C isn't really better than other languages in that regard. Its not like its using auto-vectorization to get that SIMD support.
> lean-zip beats every single compression level miniz_oxide
Miniz_oxide is an intentionally simple implementation. It would be more fair to compare it to the newer zlib-rs[1], which even beats zlib-ng (fastest implementation in C)[2].
So, lean-zip "outperforming an equivalent Rust package that crates.io says has been downloaded over a billion times". Yes, zlib-rs has only been downloaded 200 million times... But it is way faster!
Yes, but it also has many conditionals, for example:
- The faster package may have been available for a shorter amount of time and so downloads have not had the time to catch up yet.
- Many people may not be aware of the faster package, but they would switch if they knew.
- Maybe people are aware of the faster package, but there are switching costs that prevent them from adopting it right now that are greater than the costs incurred from the slower package.
Recent downloads per day show the opposite trend, with 2500k vs 1600k per day (with zlib-rs leading). The issue is zlib-rs is far younger so looking at total downloads is nonsense. And there is inertia to switch over as well.
Early Rust had a ‘pure’ function annotation, too, but it was removed as unnecessary as the borrow checker evolved and became central to Rust’s safety story.
I appreciate that about D. I've always wondered though, if the convention isn't backwards, like how Lua forces you to specify local instead of global, TS isn't strict by default, most languages have default mutable rather than default immutable, etc.
I always thought having a working program should be opt-out, not opt-in.
(For example, my ideal language supports dynamic typing, with the compiler directive #jammode, or alternatively #explodeinprod)
It's a great question. The answer is `pure` came along after D was already established with a lot of existing code. Making it the default would break pretty much all of it.
Part of the problem is `pure` is transitive, i.e. a pure function calling an impure one is not allowed. This makes it hard to incrementally make the code pure.
But when one succeeds at it, it is very satisfying.
There is an escape hatch, however. Prefixing a statement with `debug` allows the dependent statement to be impure. This is very handy when one needs to insert some print statements to debug a program. (Calls to printf are impure!)
That `debug` trick is clever! We gave Roc a dedicated `dbg` language keyword which does this - it's allowed to be used in pure functions even though, as you said, logging debug output is a side effect.
By the way, hi again! We chatted briefly in person after Handmade Seattle 2021, and I remember being really impressed to learn that you're one of the few people (maybe the only one?) who has ever implemented a working C++ compiler solo.
Anyway, hope all is well, and let me know if you're ever interested in recording a podcast episode sometime - oss@rtfeldman.com
AFAIK I am still the only person who has implemented a complete C++ compiler, compliant with C++98, including preprocessor, optimizer, and code generator.
Anyhow, I'll be speaking at NWCPP soon:
THURSDAY, Nov 19, 2026 at 6:30 PM to 8:00 PM
Elegant D - There’s More to Programming than Getting Code to Work
Bellevue City Hall, Room 1E-121
450 110th Ave NE
Bellevue, WA 98004
does this mean if you want to add some print statements for local debugging inside a pure function that you will have to remove pure from the whole callgraph? or is there a way to treat it as just a warning?
I wonder if Roc will eventually have high-level abstraction to handle SIMD support. Perhaps by bringing some ideas from Futhark [1], a functional language focused on data parallelism. IIRC, the author has already been interviewed on Richard's podcast.
I don't know about roc specifically, but the best projects often come out of something someone built for a particular need. The open source / public version will often need work to strip out stuff specific to the original project, as well as refinement to make it more suitable for generic public consumption, such as adding support for edge cases, features that the original project didn't need but others likely will, support for multiple platforms for deployment, etc.
Have you considered exposing these optimisations through a library so programmers can request optimisation passes and have the compiler check the preconditions?
Asking cuz I'm preparing an article on how fast JS can get and found the same thing, you can get in 10% of optimised C-code with basically any code if you torture V8 enough (excepting SIMD where there seems to be a bug in V8 where it doesn't optimise to SIMD even when beyond obvious). Figured out I could extract it in a lib and do it more streamlined but not sure how well manual transforms generalise.
Also, when working on the roc-deflate, did you find recurring transformations that could fit that model or were most improvements specific to compression?
Yes, it does beat -O3 C sometimes. I took code from diverse 'language speed' benchmarks from the internet and had to rearrange them to get -O3 to beat equivalent-looking JS (Node 25.8.1 w\ V8 14.1 and clang 21 on MBP3).
i've tried various options, Firefox prefers ` for (let i = n; i--;) out[i] = a[i] * b[i] + 0.25;`
Its already at peak optimization so no form is significantly better.
We should probably all be programming in a FP language, with behind the scenes an AI translating it to something faster using correctness preserving transforms.
I suspect this will be the best way to delegate things to AI while still staying in driver's seat.
jandrewrogers 1 day ago
C/C++ code can trivially reach into some remote bit of state and mutate it directly. The compiler rarely objects, no matter how ugly the access method or pattern. It is up to the programmer to make sure the invariants hold and the compiler can’t always see through it. These elided copies add up, though it usually has a maintainability and safety cost.
It is related to why functional languages tend to be mediocre for supercomputing in practice despite having nice properties for massive parallelism in theory.
Zak 21 hours ago
I agree with his point, and Clojure is my favorite programming language, but the computer itself is place-oriented. It runs on reading from and writing to locations in memory, and extracting maximum efficiency from it requires direct control over that memory.
raddan 19 hours ago
giovannibonetti 17 hours ago
[1] https://debasishg.github.io/blog/push-ifs-up-fors-down/ [2] https://venge.net/graydon/talks/VectorizedInterpretersTalk-2...
delusional 19 hours ago
You could similarly argue that the computer is in fact fundamentally functional, since the core instruction cycle (Fetch Decode Execute) does not modify any memory.
The key insight into that system is that we've embedded a functional system within a mechanism that makes it imperative. The functional cycle can output commands to fetch it more data, that it can then use in the next cycle. It becomes imperative by the addition of time, which it needs because it's limited in the amount of input and output.
ben-schaaf 15 hours ago
bad_haircut72 18 hours ago
Zak 18 hours ago
fuzztester 9 hours ago
whilenot-dev 1 day ago
Are there any common resources for this kind of access methods and patterns that specifically aim to bridge the soft-/hardware gap? The best one I can come up with is Performance Analysis and Tuning on Modern CPUs[0] by Denis Bakhvalov.
[0]: https://github.com/dendibakh/perf-book
warangal 20 hours ago
I also like this very readable paper https://xnning.github.io/papers/perceus.pdf, about reducing the costs of reference counting in context of functional languages.
MobiusHorizons 10 hours ago
VorpalWay 22 hours ago
Miniz_oxide is an intentionally simple implementation. It would be more fair to compare it to the newer zlib-rs[1], which even beats zlib-ng (fastest implementation in C)[2].
[1]: https://lib.rs/crates/zlib-rs
[2]: https://trifectatech.org/blog/zlib-rs-is-faster-than-c/
pepa65 17 hours ago
VorpalWay 7 hours ago
bawolff 17 hours ago
im3w1l 14 hours ago
WJW 11 hours ago
- The faster package may have been available for a shorter amount of time and so downloads have not had the time to catch up yet.
- Many people may not be aware of the faster package, but they would switch if they knew.
- Maybe people are aware of the faster package, but there are switching costs that prevent them from adopting it right now that are greater than the costs incurred from the slower package.
bawolff 12 hours ago
eviks 3 hours ago
VorpalWay 7 hours ago
WalterBright 15 hours ago
It's fun trying to reorganize your code to make it pass the `pure` checking.
cpeterso 4 hours ago
https://news.ycombinator.com/item?id=6940624
andai 12 hours ago
I always thought having a working program should be opt-out, not opt-in.
(For example, my ideal language supports dynamic typing, with the compiler directive #jammode, or alternatively #explodeinprod)
WalterBright 11 hours ago
Part of the problem is `pure` is transitive, i.e. a pure function calling an impure one is not allowed. This makes it hard to incrementally make the code pure.
But when one succeeds at it, it is very satisfying.
There is an escape hatch, however. Prefixing a statement with `debug` allows the dependent statement to be impure. This is very handy when one needs to insert some print statements to debug a program. (Calls to printf are impure!)
rtfeldman 11 hours ago
By the way, hi again! We chatted briefly in person after Handmade Seattle 2021, and I remember being really impressed to learn that you're one of the few people (maybe the only one?) who has ever implemented a working C++ compiler solo.
Anyway, hope all is well, and let me know if you're ever interested in recording a podcast episode sometime - oss@rtfeldman.com
WalterBright 3 hours ago
Anyhow, I'll be speaking at NWCPP soon:
THURSDAY, Nov 19, 2026 at 6:30 PM to 8:00 PM
Elegant D - There’s More to Programming than Getting Code to Work Bellevue City Hall, Room 1E-121 450 110th Ave NE Bellevue, WA 98004
nwcpp.org
Please come! All are welcome!
zeendo 10 hours ago
So glad to have been writing production Haskell for almost a decade
MobiusHorizons 10 hours ago
WalterBright 3 hours ago
giovannibonetti 16 hours ago
[1] https://futhark-lang.org/
andai 16 hours ago
It then links to a video where a consultant mentions using in production (for several years already?), which confused me.
How can you justify using immature technologies for client work? Or is it an extra benefit when it breaks and you have to fix it?
I'm genuinely curious, maybe it's not as bad as I think. But I've used immature hosting platforms, and that certainly didn't go well for me!
zdragnar 16 hours ago
RobotToaster 15 hours ago
Ask everyone that's using generative AI?
gobdovan 21 hours ago
Asking cuz I'm preparing an article on how fast JS can get and found the same thing, you can get in 10% of optimised C-code with basically any code if you torture V8 enough (excepting SIMD where there seems to be a bug in V8 where it doesn't optimise to SIMD even when beyond obvious). Figured out I could extract it in a lib and do it more streamlined but not sure how well manual transforms generalise.
Also, when working on the roc-deflate, did you find recurring transformations that could fit that model or were most improvements specific to compression?
countWSS 21 hours ago
>where it doesn't optimise to SIMD even when beyond obvious What was that code?
gobdovan 20 hours ago
> What was that code?
literally:
``` function numericKernel(a, b, out, n) {
} ```countWSS 19 hours ago
michaelteter 14 hours ago
amelius 17 hours ago
I suspect this will be the best way to delegate things to AI while still staying in driver's seat.