J

Josh Haberman

blog.reverberate.org · Personal blog · on HN since 2008

Josh Haberman, a systems programmer who works on Protobuf, writes about parsing, interpreters, JITs, and low-level performance.

What HN saysCommenters cite his posts on tail-call interpreters, the bubble sort comeback, and LL/LR parsing, calling the parsing explanations helpful for people without a CS background.
105HN people shared it
63linked it in comments
80stories submitted
3.6Kstory points
3 months agolast shared
2008people per year2026

Most-shared posts

  1. 1
  2. 2
    Parsing C++ is literally undecidable (2013)
    15 people · 119 points · first shared 2013 · 142 comments on HN
  3. 3
    LL and LR Parsing Demystified (2013)
    13 people · 180 points · first shared 2013 · 35 comments on HN
  4. 4
    LL and LR in Context: Why Parsing Tools Are Hard (2013)
    10 people · 228 points · first shared 2013 · 95 comments on HN
  5. 5
    Hello JIT World: The Joy of Simple JITs (2012)
    10 people · 194 points · first shared 2013 · 14 comments on HN
  6. 6
  7. 7
    Hoare’s Rebuttal and Bubble Sort’s Comeback
    5 people · 250 points · first shared 2020 · 53 comments on HN
  8. 8
    Bloaty McBloatface: a size profiler for binaries
    5 people · 245 points · first shared 2016 · 43 comments on HN
  9. 9
  10. 10
    No-Panic Rust: A Nice Technique for Systems Programming
    3 people · 204 points · first shared 2025 · 138 comments on HN
  11. 11
    The overhead of abstraction in C/C++ vs. Python/Ruby
    3 people · 54 points · first shared 2014 · 40 comments on HN
  12. 12
    Deep Wizardry: Stack Unwinding
    3 people · 9 points · first shared 2013 · discussion on HN
  13. 13
    Bloaty McBloatface 1.0
    3 people · 5 points · first shared 2018 · discussion on HN
  14. 14
    Printf() debugging in assembly language
    3 people · 2 points · first shared 2013 · discussion on HN
  15. 15
    On "The Future of JavaScript MVC Frameworks"
    2 people · 73 points · first shared 2014 · 54 comments on HN

What HN people wrote when they linked it

gavinray · 2026-04-05
It doesn't make sense to me that an embedded VM/interpreter could ever outperform direct code You're adding a layer of abstraction and indirection, so how is it possible that a more indirect solution can have better performance? This seems counterintuitive, so I googled it. Apparently, it boils down to instruction cache efficiency and branch prediction, largely. The best content I could find was this post, as well as some scattered comments from Mike Pall of LuaJIT fame: https://sillycross.github.io/2022/11/22/2022-11-22/ Interestingly, this is also discussed on a similar blogpost about using Clang's recent-ish [[musttail]] tailcall attribute to improve C++ JSON parsing performance: https://blog.reverberate.org/2021/04/21/musttail-efficient-interpreter…
dkbrk · 2023-08-21
> there is never a good reason to use bubble sort Not true. Bubble sort is excellent for sorting very small arrays. See "Hoare’s Rebuttal and Bubble Sort’s Comeback (2020)" under "Fallback for sorting short arrays" and the performance results presented therein [0]. Discussion [1]. [0]: https://blog.reverberate.org/2020/05/29/hoares-rebuttal-bubble-sorts-c… [1]: https://news.ycombinator.com/item?id=23363165
haberman · 2023-01-09
> I wonder if wasm3 is using musttail with opaque function calls in the instruction handlers. It will demolish performance, which is why I am only using computed gotos in mine (when available). Even switch-case is faster than musttail when you have to leave the tco-jumps. This doesn't match with my experience. After working on this problem a lot, I came to the conclusion that musttail with opaque function calls is one of the best ways of getting good code out of the compiler: https://blog.reverberate.org/2021/04/21/musttail-efficient-interpreter…
sillycross · 2021-08-30
LuaJIT's interpreter loop is similar, except that it's written in assembly, and the motivation is that the compiler could not optimize the C version to the best possible extent. Kind of related, this post [1] mentioned some ideas to break it down into smaller modules without losing performance with tail calls. [1] https://blog.reverberate.org/2021/04/21/musttail-efficient-interpreter…
adito · 2018-07-31
It's often I stumble upon discussion mentioning the term LL and LR and other stuff related to parsing[0]. Without a proper CS background, it's quite hard to follow along. That first link[1] and the wikipedia page[2] mentioned there are really great. Many thanks for posting those. It really shed some light about those terms. [0]: https://jeffreykegler.github.io/personal/timeline_v3 [1]: http://blog.reverberate.org/2013/07/ll-and-lr-parsing-demystified.html [2]: http://en.wikipedia.org/wiki/Tree_traversal
SonOfLilit · 2017-11-20
C++ is also undecidable, by the way: http://blog.reverberate.org/2013/08/parsing-c-is-literally-undecidable… Perl was a great language design lab experiment. They gave people 20 ways to do any simple thing, and then Matz and Guido looked to see which ways became popular and designed great languages that allow only those ways and maybe 1-2 more that are highly frowned upon. I'm almost as glad Perl exists as I am that I never needed to learn it.
cbHXBY1D · 2016-12-03
My favorite blog post of all time is Haberman's "Hello, JIT World: The Joy of Simple JITs" which covers using DynASM. [1] I wish there was better documentation for DynASM. I wonder what cool programming projects would be created if DynASM was better known and better documented. [1] http://blog.reverberate.org/2012/12/hello-jit-world-joy-of-simple-jits…
devty · 2016-09-21
I see a follow up to the article which provides further motivations - http://blog.reverberate.org/2013/09/ll-and-lr-in-context-why-parsing-t… - but I don't see a blog post diving into the algos.
jdf · 2014-05-05
Good point, although it's not like the stock protobuf implementation (rather than protocol) is the best that can be had. For example, this implementation of protobuf deserialization is quite a bit faster: https://github.com/haberman/upb http://blog.reverberate.org/2011/04/upb-status-and-preliminary-perform…
nkurz · 2014-05-01
I think it's often the right tool. My personal flavor involves wrapping the printf() with a conditional that depends on the current function name and the value of an environment variable. I've been trying to figure out how best to write a generic macro for this in x64. You might be interested in Josh Haberman's post on using GDB's "breakpoint command lists" as an alternative to actually putting the variadic calls into the assembly: http://blog.reverberate.org/2013/06/printf-debugging-in-assembly-langu…
mbrubeck · 2009-10-05
My friend Josh is working on a big project in Lua, and part of his wishlist is a Lua-compatible language with "classical" OO (language-level classes and interfaces): http://blog.reverberate.org/2009/02/09/disappointment/ http://blog.reverberate.org/2009/02/09/static-lua/
burntsushi · 2025-03-08
I'd have to look more closely at those examples, but I find it hard to believe that every runtime invariant violation manifests as one of those error codes. It certainly isn't true for PCRE2. > Rust brings safety by forcing (safe) code to use dynamic checks when a safety property cannot be statically guaranteed, which addresses (1). But there's still a degree of freedom for whether failures are reported as panics or as recoverable errors to the caller. Sure, you can propagate an error. I just don't really see a compelling reason to do so. Like, maybe there are niche scenarios where maybe it's worthwhile, but I do not see how it would be compelling to suggest it as general practice. You might point to C libraries doing the same, but I'd have to investigate what exactly those error codes are actually being used for and _why_ the C library maintainers added them. And the trade-offs in C land are totally different than in Rust. Those error codes might not exist if they had a panicking mechanism available to them. > I wrote down some of my thinking in this recent blog entry, which actually quotes your excellent summary of when panics are appropriate: https://blog.reverberate.org/2025/02/03/no-panic-rust.html Yes, I've read that. It's a nice blog, but I don't think it's broadly applicable. Like, I don't see why I would write no-panic-Rust outside of extremely niche scenarios. My blog on unwraps is meant to be more broadly applicable: https://burntsushi.net/unwrap/ (It even…
svat · 2020-02-03
The immediate context for this is this post https://v8.dev/blog/v8-release-80 on the v8 blog, where they announced that in v8 version 8, they saved an average of 40% memory, and (unlike usual memory-time tradeoffs) also got good performance improvements. (Design doc: https://docs.google.com/document/d/10qh2-b4C5OtSg-xLwyZpEI5ZihVBPtn1xw… So pointer compression is clearly a good thing. I don't know much about the history of pointer compression, but I know the following. In 2008, Don Knuth posted on his then "news" page (https://cs.stanford.edu/~knuth/news08.html): > A Flame About 64-bit Pointers > It is absolutely idiotic to have 64-bit pointers when I compile a program that uses less than 4 gigabytes of RAM. When such pointer values appear inside a struct, they not only waste half the memory, they effectively throw away half of the cache. > The gcc manpage advertises an option "-mlong32" that sounds like what I want. Namely, I think it would compile code for my x86-64 architecture, taking advantage of the extra registers etc., but it would also know that my program is going to live inside a 32-bit virtual address space. > Unfortunately, the -mlong32 option was introduced only for MIPS computers, years ago. Nobody has yet adopted such conventions for today's most popular architecture. Probably that happens because programs compiled with this convention will need to be loaded with a special version of libc. > Please, somebody, make that…
panic · 2018-07-31
Yeah, they both solve the same problem -- turning text into a structured "parse tree". At the core of each parsing tool is a particular parsing algorithm. The details of this algorithm determine what kind of text the parser can parse, how efficiently it can parse it, and what kind of error messages you get if something goes wrong. There's a huge variety of these algorithms, but nearly all of them are variations on two basic types: "LL" and "LR" parsers. For example, ANTLR uses an "LL(*)" parsing algorithm. This is a great blog post about the difference between the two basic types: http://blog.reverberate.org/2013/07/ll-and-lr-parsing-demystified.html… Owl is unique because its algorithm isn't a variation of either of these. It works more like a regular expression parsing algorithm. This makes it more limited than a tool like ANTLR, but it can solve some problems that have been difficult with the existing algorithms. For example, most programming languages allow expressions like "1 - 2" to be used as statements. If you don't use a semicolon or anything to separate the statements, there's an ambiguity -- is "1 - 2" a single expression, or is it the two expressions "1" "-2" in sequence? ANTLR (at least with the default settings I tried) doesn't tell you about this ambiguity at all. It just picks one of the two options ("1 - 2" in this case). This may or may not have been what you were expecting! The problems with ambiguity checking are quite deep (this followup to…
katastic · 2017-12-14
I haven't used the CryEngine but often times if you think you "need" a higher bit coordinate system (don't you mean 128-bit? ala long double? Doubles are already 64-bits and 80-bits internally.) you may be thinking of the problem in the wrong way. There's no reason you can't (in most games) have a segregated coordinate system. For example, where each star is the center, the (0,0,0) point. And when you warp from one star system to another, the new star is the new (0,0,0) point. Floats are great for huge distances, and tiny distances. They are horrific at BOTH at the same time... like say... adding a tiny fractional velocity vector per frame to an object millions or billions of units away. (There is a minimum amount you can add to a float without rounding down to the same original number, and the minimum change gets bigger the bigger the number is.) http://blog.reverberate.org/2014/09/what-every-computer-programmer-sho… IIRC, KSP does the same thing and coordinates are to the nearest "planet of influence"--which has the affect of removing Lagrangian points for orbits but otherwise works great without needing custom math types which are much much slower than native types, and it doesn't require ripping out all of the assumptions that an entire engine (Unity in the case of KSP) runs on. When I created my own 2-D KSP, I ended up encountering all of these issues first-hand, and simply adding more precision didn't help nearly as much as segregating my coordinate system…
noelwelsh · 2026-08-10
> What practical patterns are enabled by TCO in C? It's important in interpreters. Here's an example: https://blog.reverberate.org/2021/04/21/musttail-efficient-interpreter…
willvarfar · 2026-04-20
Exciting! I have been on a similar odyssey making a 'zero copy' Java library that supports protobuf, parquet, thrift (compact) and (schema'd) json. It does allocate a long[] and break out the structure for O(1) access but doesn't create a big clump of object wrappers and strings and things; internally it just references a big pool buffer or the original byte[]. The speed demons use tail calls on rust and c++ to eat protobuf https://blog.reverberate.org/2021/04/21/musttail-efficient-interpreter… at 2+GB/sec. In java I'm super pleased to be getting 4 cycles per touched byte and 500MB/sec. Currently looking at how to merge a fast footer parser like this into the Apache Parquet Java project.
shoo · 2026-03-30
Josh Haberman has a good 2013 blog post discussing LL & LR parsers, theory vs practice, context-free grammars & Parsing Expression Grammars (PEGs): https://blog.reverberate.org/2013/09/ll-and-lr-in-context-why-parsing-…
pedrocr · 2025-12-02
I'm curious if that's still the case generally after things like musttail attributes to help the compiler emit good assembly for well structured interpreter loops: https://blog.reverberate.org/2025/02/10/tail-call-updates.html
throw0101c · 2025-10-30
See perhaps various "What every programmer / CSist should know about floating-point arithmetic" papers and articles: * David Goldberg, 1991: https://dl.acm.org/doi/10.1145/103162.103163 * 2014, "Floating Point Demystified, Part 1": https://blog.reverberate.org/2014/09/what-every-computer-programmer-sh… ; https://news.ycombinator.com/item?id=8321940 * 2015: https://www.phys.uconn.edu/~rozman/Courses/P2200_15F/downloads/floatin…
dammaj · 2025-02-20
To read about the basics of tail calls optimization: https://blog.reverberate.org/2021/04/21/musttail-efficient-interpreter…
swolchok · 2024-11-02
> C++ templates have a similar issue TIL! I went and dug up a citation: https://blog.reverberate.org/2013/08/parsing-c-is-literally-undecidabl…
UncleEntity · 2023-10-20
I found this[0] around the same time I found the paper from TFA during a recent rabbit hole journey, very similar methinks. Combined with a meta-compilation system I believe it would be a fairly efficient way to implement a tree-walking interpreter. [0] https://blog.reverberate.org/2021/04/21/musttail-efficient-interpreter…
metadat · 2023-08-29
In case you're like me and rusty on LL grammars vs LR, I found this quick refresher: https://blog.reverberate.org/2013/07/ll-and-lr-parsing-demystified.htm…
kragen · 2023-08-22
the vast majority of computers still aren't superscalar, much less ooo; for every i7 there are 100 cortex-m0+s, which don't even have caches. gpus aren't superscalar or ooo either, just heavily multithreaded, simt, and simd even if insertion sort reliably beats bubble sort on your ssd ftl chip and your wristwatch, though, it seems like there are cases on modern high-performance hardware where insertion sort is worse than bubble sort (cf. https://blog.reverberate.org/2020/05/29/hoares-rebuttal-bubble-sorts-c… which is very surprising to me
xKingfisher · 2023-01-26
An interesting application of this is massaging the branch predictor using tail calls to speed up a parser/interpreter: https://blog.reverberate.org/2021/04/21/musttail-efficient-interpreter…
vgatherps · 2022-06-15
Being able to guarantee tail calls is a useful optimization in far more cases than code replaceable by loops. I’ve used it myself where dispatch targets themselves are dynamic (so can’t be trivially made into a loop) for significant performance gains. Some other folks who’ve done the same thing (and can post publicly) for code that’s not just “must recurse because loops are for lowly imperative serfs”: * https://blog.reverberate.org/2021/04/21/musttail-efficient-interpreter… * http://lua-users.org/lists/lua-l/2011-02/msg00742.html * https://cantortrading.fi/rust_decimal_str/ Now does JavaScript really need/care about any of this? Probably not.

Keyboard shortcuts

/Search
j kNext / previous blog
EnterOpen selected blog
vVisit the blog site
fFollow / unfollow
lSwitch grid / list view
dSwitch dark mode
g hHome
g pTop posts
g tTopics
g fFollowing
g lLiked
g yHistory
g qFAQ
EscBack / close