That's the magic of YJIT, and what I'll describe in the rest of Chapter 4. YJIT uses a "wait-and-see" approach, and often defers compilation of the block/function until the actual types are provided by your program. And YJIT then keeps track of separate block versions based on the operand types, and can call the appropriate block version as needed.
This basic algorithm is called "Basic Block Versioning." Maxime Chevalier-Boisvert from Shopify has some great presentations online about this; for example [1].
I believe ZJIT, the newer JIT engine, uses a different approach. I'm exploring that now.
Yes that's something I want to dig into and explore in this chapter... when exactly does Ruby's JIT compiler activate and optimize our code? And you're right: since Ruby will JIT blocks as if they were separate function many loops will be optimized using this simple heuristic.
I tried that while researching the article, but found the call to "puts" doesn't link without the standard library code. And without a "puts" or similar call to produce output LLVM optimized the entire program away :)
I suppose this could work if, as you suggest, I manually called out to a lib_c function like printf instead.
Yeah, puts is part of the part of the standard library, and uses crystal's evented io framework, fiber scheduler and libevent. This is what most of the extra code in the asm output will be doing.
(author here) ...FYI I actually learned assembly language for the first time back in the 1970s and 80s using a 6809 inside a Radio Shack "Color Computer." I was super-fun at the time. I don't remember much of it now but I'm sure x86 isn't as clean or fun as 6809 assembly was.
Oh absolutely. I thought about saying that, and just ran out of energy before posting the article this morning.
The reason I used this particular SQL statement as an example is that ActiveRecord (a Ruby ORM) generates it from a fairly simple, common Ruby expression. I suppose ActiveRecord could be improved to drop the sort when you know there's one one possible match.
Unless "name" is unique, there could be multiple matches. Asking for the "first" demands an ordering.
Perhaps (a) the user doesn't care what ordering is used, and/or (b) (such as in your example in the preceding post) hasn't specified an order (and apparently PK ordering is defaulted to PK - a model option?). It would be error-prone for ORM to take (b) as implying (a).
Yes, good point. Rails has a lot of conventions like this - that "first" implies ordering by primary key (by default). I just imagine that most Rails developers don't think about sorting and its implications when they ask for the first record.
This basic algorithm is called "Basic Block Versioning." Maxime Chevalier-Boisvert from Shopify has some great presentations online about this; for example [1].
I believe ZJIT, the newer JIT engine, uses a different approach. I'm exploring that now.
[1] https://www.youtube.com/watch?v=zO9_uTaELCw — RubyConf 2021 - YJIT - Building a new JIT Compiler inside CRuby by Maxime Chevalier Boisvert.