00:00:19.359
Hello everyone, it's my pleasure to introduce Jemma Issroff.
00:00:22.439
Jemma is a Ruby core committer and works at Shopify as a senior developer on the Ruby infrastructure team.
00:00:26.960
I have to say it’s been a pleasure working with her. She is also a co-founder of wb.rb, the Women and Non-Binary Ruby Community.
00:00:32.880
You can applaud! Additionally, she's a co-host of the Ruby on Rails podcast. Please give her a warm welcome as she presents her talk titled "Popping Into CRuby."
00:01:00.559
Thank you! Good morning everyone. A little while ago, I was looking through a file in CRuby's codebase, specifically at Ruby compile.c.
00:01:04.239
As the name suggests, that’s where a lot of the compiler code resides. If that’s not familiar to you, my goal today is to help you understand what I mean by that by the end of this talk.
00:01:10.159
I was looking specifically at a method called IC_compile_each, and something caught my attention—a parameter named 'popped'.
00:01:18.760
The usage of 'popped' was sprinkled throughout this method, which is quite large, and I found its functionality intriguing.
00:01:25.360
Today, we're going to dive into the concept of 'popping' in CRuby. If we haven't met yet, I’m Jemma, she/her pronouns. I would love to meet you, so please come say hello at any point during the conference.
00:01:41.760
As my manager mentioned, I work at Shopify on the Ruby infrastructure team, and I'm also a co-founder of wb.rb, the Women and Non-Binary Ruby Community. We're having a little dinner tonight at Cafe Coyote from 6:00 PM to 8:00 PM.
00:01:58.120
If you're a woman or a non-binary person, please come join us. You can find more information in the Slack if you'd like to join.
00:02:01.599
So, we can think of our Ruby applications as cars. We might have seen metaphors like this in the past, and like most car metaphors, we may not think much about what goes on under the hood.
00:02:11.440
The smoke here isn't due to something broken but rather that there are some really interesting processes occurring under the hood that we may not have considered regarding our Ruby programs.
00:02:30.160
While we know our Ruby code exists, underneath that there's also bytecode and parse trees that we should understand. Today, we'll discuss all three elements and their relation to the concept of 'popping'.
00:02:45.639
When we run our Ruby code, the first step is that it goes through the parser and becomes what's called an abstract syntax tree (AST). This is a representation of our programs in tree form.
00:02:54.959
So, how do we represent our programs as a tree? Let’s look at an example. If we have a very simple class named Conference, it has one method.
00:03:10.080
At a high level, we can see the structure of this tree, and you can check this for any Ruby file you have by passing a parse tree to Ruby's 'd-dump' command.
00:03:19.800
You'll see the entire parse tree for any Ruby code. Looking again at our Conference class, the first node we get will typically be the class node.
00:03:39.520
It will have several child nodes depending on the type of node, including locations for instance variables. A class node has a few children; one of them is its name, 'Conference', which Ruby refers to as a constant node.
00:03:55.159
Another child node is the method definition, containing additional child nodes that include things like arguments for methods and various data types.
00:04:08.680
This abstract syntax tree, or parse tree, represents all the code—every line of code needs to be in this tree.
00:04:22.120
You might wonder why we can’t optimize out parts of the code at this first step and simplify it to make the parse tree smaller or faster.
00:04:41.800
The answer is, running the code isn’t the only function being performed on this parse tree; there are other consumers of the parse tree whose goals aren’t solely to run the program.
00:04:56.000
For example, tools like LSPs and linters like RuboCop critically require the full tree to operate correctly.
00:05:12.080
Additionally, you may have heard of Prism, a new Ruby parser which was once called YARP. It offers similar functionality but represents the data in a different manner.
00:05:27.760
Prism creates its own abstract syntax tree, using the same information but with a slightly different representation. An example of this is that an empty args node in the old parser translates to nothing in Prism.
00:05:44.320
There are clearer naming conventions in Prism, for instance, changing 'colon node' to 'constant read node'.
00:06:01.000
You may wonder why we created a new Ruby parser when the old one works perfectly fine. The motivation for Prism includes improvements in error tolerance, which allows the parser to recover from errors without failing.
00:06:16.720
To illustrate, here’s an example of a Ruby class with several syntax errors. Rather than crashing, Prism would provide clear error messages telling you what's wrong, rather than generic syntax error messages.
00:06:35.560
In Prism, each error message explicitly states what was expected, making it easier for developers to understand how to fix them.
00:06:55.239
Prism is available as a gem and many projects are already integrating with it, hoping it will be included in Ruby 3.3.
00:07:12.720
The code then progresses from the parser to becoming bytecode by running through the compiler.
00:07:28.239
Prism uses its own AST, meaning that the existing compiler needs modification, as it expects different node types.
00:07:35.680
My recent work has focused on developing a compiler for Prism that generates bytecode that the virtual machine can execute.
00:07:50.239
The virtual machine is known as YARV, or Yet Another Ruby Virtual Machine, which explains the origin of Prism's name.
00:08:00.559
YARV is a stack-based virtual machine, and to understand that, we can run a simple Ruby program, such as 2 + 3.
00:08:19.639
The instructions for this will involve putting numbers on the stack and calling methods that pull values off the stack, illustrating the popping concept.
00:08:36.960
You might have heard me mention the word 'pop' frequently; we put objects onto the stack and then pop them off. This will tie back to the talk’s central theme—popping.
00:08:49.680
In the Conference example, you see instructions like 'put nil', which represents putting a nil value onto the stack, followed by other 'put' instructions related to placing values on the stack.
00:09:08.080
I’d like to focus on one instance where a string is put onto the stack and immediately popped off, thus not contributing anything to the instruction sequence.
00:09:26.960
When we enhance the program to include additional lines, the unused string is effectively removed during the compilation, contributing to optimized performance.
00:09:44.640
This concept of popping suggests that unnecessary instructions can be skipped, thus improving efficiency. The compiler can omit instructions like putting a string if it’s determined as not needed.
00:10:00.440
In my example program, after adding an integer, we observe that the compiler skips instructions for 'put string', effectively enhancing performance by not generating unnecessary bytecode.
00:10:14.960
The optimization occurs because once the program determines a value won’t be used, it pops it off the stack before it can become part of the bytecode.
00:10:30.560
To emphasize this, if we're developing with Ruby and an instruction can be determined as unused, we can eliminate it before compilation.
00:10:44.960
It's essential to recognize the distinction between things that can or cannot be popped. For instance, if the program or method is structured to return a value, it can't be popped.
00:11:05.720
Examples include strings within return statements, method definitions needing to return values, or local variable assignments that require preserving original data.
00:11:19.160
On the other hand, literals, symbols, or objects that aren't tied to any return behavior can, indeed, be optimized out.
00:11:30.720
To demonstrate this further, I’ll provide additional instances of what can successfully be popped—like strings, numbers, or arrays with constants.
00:11:45.800
The key behind them is that they are contextually unneeded after the execution; thus, the compiler optimizes them away.
00:12:00.160
Overall, I hope that gives you a well-rounded understanding of the concept of popping in C Ruby.
00:12:16.760
It turns out there’s a lot going on behind the scenes, making programs faster and more efficient—knowledge worth engaging with and developing further.
00:12:35.680
If you're interested in furthering your understanding, please connect with folks who can guide you, including those involved in various projects and discussions.
00:12:50.160
I have sprinkled several resources throughout this talk for you to engage with these concepts further. Thank you for your attention!