00:00:06.960
Our next talk is by Richard Schneeman. When I told him I used something he built called Run Dock, he expressed his joy in a rather unique way, which I thought was quite amusing. On the other hand, he was teaching a master course in programming and made all the material available for free online. I think that's a very nice move on his part.
00:00:15.280
Well, without further ado, please give a warm welcome to Richard.
00:00:28.720
It is time to set sail! Hello everyone, my name is Richard. Thank you for the great introduction. On the internet, I go by the name 'schneems.' Some people who know me are aware that I love Ruby so much that I actually married her. This is my wife, Ruby, and we have two wonderful children. I'm not going to talk about them, but I will mention one of my dogs. His name is Hans Peter Von Wolfe the Fifth, and we just call him Cinco for short. I maintain a service called CodeTriage, which helps people get started in open source. It sends issue contribution ideas as well as documentation ideas so you can contribute to a Ruby project.
00:00:58.960
I work for a small startup based out of San Francisco that you might have heard of; it's called Heroku. They were nice enough to pay for my flight, which is fantastic, and they also pay me to work on the Ruby build pack as well as some other open source projects. Before I get started with my talk, I want to share a bit of background. When I started working on this presentation, I honestly hurt my hands pretty bad, so much so that I had to take a couple of weeks off. Around the time I began writing this presentation, I was limited to using an eye tracker and voice controls as my input devices on a computer. Here's what that looks like.
00:02:10.800
So, how did I manage to create such an amazing presentation without the use of my hands? You can give a hand to my hands, or rather, to Caleb Thompson! Besides Caleb, I also have to thank a voice-over artist, Yuri, whose work you will experience later.
00:03:03.760
Now, I've been told that we need boat jokes since we are on a boat. Let me ask you this: why do we have a Ruby conference on a boat? Because everyone knows that Ruby is written on top of C!
00:03:40.560
In all seriousness, I have a couple of fun facts to share. For example, there is an RSS submarine named Bodie McBoatface. While it has a silly name, it has made serious contributions to climate science. I also maintain Puma, and we are about to release Puma 4 soon. Before I can release it, I need to get back to port.
00:04:11.280
This talk is primarily about performance. I have found something that I believe can make Ruby faster. I realized that the problem is simply that we need to take the sleep out.
00:05:09.360
Now, who knows who this is? This is Marie Kondo, a world-famous organizing expert. She has a Netflix show and several fantastic books. I want to share a quick clip of her working on object allocations.
00:05:56.080
Unfortunately, Marie couldn't be here with us today, so instead she sent her pen rabbit, Con Hair, to join us!
00:06:06.400
Hello and welcome, everyone. In English, my name translates to 'love it,' and I truly love colors, movies, and performance. Today, you're here to hear about the concrete method of tidying your Ruby applications.
00:06:40.240
To tidy up, first, collect your objects into a pile where you can see them clearly. Next, consider each object: how do you know if it sparks joy? If an object is very useful and keeps your code clean without causing performance problems, then it sparks joy. If an object is necessary for your code to function and removing it causes it to crash, then it also sparks joy.
00:07:01.960
This process will help us determine which objects we can consider getting rid of. We are going to use two tools for this purpose: Memory Profiler and Derailed Benchmarks, which will help us benchmark our Rails applications. We'll start out with a little quiz to test our understanding.
00:07:46.480
Here is a benchmark comparing two different methods of determining the largest value between two inputs. One method allocates an array and compares its values, while the other method uses a direct comparison without any allocation. I want to know, who thinks the array method is faster?
00:08:00.400
And who thinks the other method is faster? Correct—the second method is faster! But does anyone know how much faster? Shout out a number. The answer is that it is two times faster. Both methods execute the exact same logic, making this a significant performance difference.
00:09:07.760
In general, touching memory will slow your program down more than performing comparisons or calculations. This is not only true in Ruby but in every programming language. For instance, if you're writing C, calling malloc is slow, and you want to avoid it. Since we know that Ruby's object allocation is expensive, we’ve seen that we can optimize some scenarios by removing allocations and opting for comparisons instead.
00:09:40.480
If we discover where many objects are being allocated, this discovery shows us where our program is likely to have performance hotspots. When we're optimizing, reducing the percentage of bytes allocated by about 1% can usually speed up our program by about that same percentage. This is typically true but not always. This simplifying assumption helps us benchmark faster as well.
00:10:13.440
Typically, when writing benchmarks, we cannot just run and time two pieces of code. There’s a lot of variance, and benchmarking may require running them thousands or even millions of times. With memory comparisons, a fixed value like 100 bytes will always equate to 100 bytes, which is consistent. This consistency also means we only need one measurement instead of many.
00:11:14.400
Next, we will look at the Memory Profiler gem, which will allow us to view all allocations. Behind the scenes, it uses a wrapper for object space allocation tracing. If you’re profiling a Rails app, you can use Derailed Benchmarks. The gem can hit an endpoint on your application directly from the CLI, which is beneficial because you don’t have to restart the server between changes, nor do you have to refresh your browser.
00:12:05.440
For our case study, I call this section 'Inadequate Record.' First, we will run Derailed against the target application, CodeTriage, to gather memory allocations. The output will show the most memory allocations at the top, which is where we will start looking.
00:12:43.440
Once we pick a file from the list, we must zoom in on it and filter our results. The filtered output is much cleaner and shows that the majority of allocations come from line 270. Let’s see what the code does by opening that file.
00:13:06.720
On line 270, we can see it’s allocating strings. To understand this better, we must know what the code is doing. This part of the code is inside the respond_to method. When we call respond_to on an object, we need to know if a method by that name exists. Since ActiveRecord is backed by a database, we also need to know if a column by that name exists.
00:14:19.200
Typically when you call respond_to, you pass in a symbol, while ActiveRecord stores columns as strings. The code converts the symbol to a string for comparison and then iterates over the column arrays. For example, if we call respond_to on a user with the email as a symbol, we convert it to a string and check for matches against the column names.
00:15:00.799
Now that we understand this code, we can ask ourselves: does this object allocation spark joy? It is in use, which makes it useful, but it does a lot of allocations, making it less performant. It doesn't specifically help keep our code clean either, and we don’t know yet if it is absolutely necessary.
00:15:29.120
To figure this out, we should refactor the code to be faster while maintaining correctness. Since the name variable must be a string, we’ll need to make a conversion somewhere. My hypothesis is that instead of converting the symbol each time, we can check for a column by name as a symbol.
00:16:33.440
This allocation does not spark joy, so we should eliminate it. We never want to allocate a string if we can avoid it. We can store our column information as a hash, mapping the symbol as a key to its corresponding string, and then check for column existence without unnecessary allocations.
00:17:12.160
This patch reduced object allocations by about 1% of total memory before CodeTriage, leading to a rendering time that was approximately 1.01 times faster. So, I think we’re basically done here.
00:18:09.920
However, we need to verify that these results are statistically significant. Time to teach them that. The Student's t-test helps us determine if the performance results are statistically significant.
00:18:49.680
We use an advanced statistical tool, Excel, which has a function that generates a t-test. If the result is below a certain threshold, it is statistically significant. And yes, your changes were statistically significant, so they got merged! While achieving a 1.01x speed increase may not seem like much, it suggests that fewer servers will be necessary. In theory, if your application previously required 100 servers, after this patch it may only need 99.
00:19:45.600
This increase might seem modest, but we can showcase another example of using this method to tidy up. Let’s remember the steps: first, collect all your objects into one place, then evaluate whether they spark joy.
00:20:28.720
Spotting object allocation hotspots indicates areas we can perform optimizations, and now you're ready to take some of these techniques into your own code.
00:21:17.360
Thank you very much! My name is Richard, and I go by schneems. A special acknowledgement to Caleb Thompson for his help with the slides. Without him, I wouldn't be here today. Thank you all for coming and for making Euruko what it is!