 I'd say that before slow-mo, I became the typical, institutionalized, educated, western man. In other words, I was driving a BMW to work, I was working long hours, I was paying my taxes and doing everything by the standards of society and so on, just like everybody else in the workforce. And frankly, I intended just to work myself on into oblivion and get old and die. That was pretty much the scenario, but now I experienced myself like the tip of a great iceberg of consciousness. Special thanks to Caleb Thompson for that glorious entrance and to Evan Phoenix, who I heard mumble something about insurance risks. Also special thanks to slow-mothemovie.com. That clip and some more of these clips are going to be, I did not take them, I did not make them, but I got permission from the creators. You can go and see the whole movie there. It's like, it's a short film, they won some awards and stuff, but it's pretty cool. Check it out. Man, I forgot my laptop back at the hotel like 10 minutes before I started this and had to run back, so I'm legitimately hyped up and ready to go. Hope you are as well. When we're supposed to be going slow and breathing. So first of all, I'd like to introduce you to Dr. John Kitchen. He is a neurologist who we saw on the screen previously. A very, very successful doctor. He has a huge house, all the trappings of a traditionally successful life, has many, many fancy cars, and at one point in time in his life was very deeply unhappy. So Dr. John Kitchen evaluated his life and said to himself, I don't need that excess, I can live a more focused life, and I can live by my rules. I don't have to live by a traditional society's rules that says more money means more happiness, more cars mean more happiness, more houses mean more happiness. And so he became Dr. John Kitchen the skater. He quit his job. He sold his house. He sold his cars, I assume. He never actually says that in the movie. But he skates around a boardwalk at a very insanely slow pace. And that's what he wanted to do. That is basically the pinnacle of life for him. It is what makes him happy. So today, and right now, maybe you can think to yourself, what is your goal in life? What do you want to achieve? Do you want to grow up, pick a career, buy a sports car, retire? If you want to do those things, typically the faster the better. If you can retire early, if you can get a sports car at 19, if you can, you know, when you're like five, everybody's like, I want to be a grown up now. So life is all about getting to where you want to go faster and the faster the better. Slomo decided that moving quickly, very quickly towards the wrong goal, was the wrong thing to do. So he turned around, slowed down, and refocused in order to get there faster, get to happiness faster. I would also like to introduce you to Schneem's. He will be your narrator today. It sounds like schnapps. It's based on part of my last name. The narrator said, explaining his own introduction. Schneem's works for a small company based out of San Francisco. He may have heard of it. It's called Peroku. He maintains sprockets. He's on the Rails contributors team, top 50 or something, I think. And we're going to be talking a little bit about sprockets. So this talk is about speed. Why don't I just cash it in and start a whole new life? Like be another person, reinvent myself. I don't have to be a doctor to the last minute and then kill off until they blew me in that way. And I remember that old guy saying, do what you want to. So I started counting down on it. And I found out all I really wanted to do was the basic things and skate. And every day I would come out and just skate as long as I could. And sometimes I would skate like even through the night. I just loved to skate. I loved the feeling and the more attention I could put to it, the more enjoyable it got. But it was only really spiritual part of my life. I'd be out there thinking about God and subjectivity. And every night I just went back to it like some sort of religious thing. I think what I'm doing with all due modesty is a type of flying. So the fastest way to get where you're going largely depends on where you're going. In programming, we can potentially get there faster by slowing down. This might seem a little counterintuitive. Why would we want slower code? Why would we want things to take more time when the purpose is to go faster? Well, one of the primary reasons is to measure it. It takes time to run programs. And if we also spend time measuring those programs, our programs are going to be a little bit slower. As I mentioned, we're going to be talking about speeding up sprockets. Is the code I'm going to be focusing on? And we're going to be using a couple of tools. Now is the extremely technical portion of my talk. So sprockets is slow. Compiling assets is slow. The first thing I did whenever I wanted to make it faster is wrap these sprockets compile tasks in stack prof. So this is stack prof. We are invoking our assets pre-compile task. And then later on we can use the stack prof command and get information about that. We're going to dump running data into a file and then we can execute stack prof to open up that file. So if you're not familiar, this is what a stack prof output looks like. What it does is it, as your program is running, it says, hey, Ruby, what are you doing right now? And Ruby's like, well, here's the stack I'm currently calling. And stack prof records that. And that's what this is. Over here we can see the class name and the method name that Ruby is currently executing. And stack prof is going to do this over and over and over and over. And instead of explicitly wrapping everything in a ton of timers, by just querying Ruby in this way, it doesn't add that much overhead. But you need to understand how it pings Ruby in order to understand the rest of the information, which is the total number of samples. Every time stack prof says, hey, what are you doing? It shows up on the stack that percentage of the total number of samples. And then the number of samples at the top of the stack as well as the percentage of that. So based off of that information, the two things that take the vastly longer than anything else in our program are calling set include and sprockets, utils, digest utils. So much time in compiling assets that it is 26% of our execution time. This is useful information, but it doesn't really help us directly. We have to know what is calling set include so much. In order to find that out, luckily stack prof has this ability to dig into just one method. We can call stack prof your dump file and then method set include. The information this is returning is where set include is being called. So the valid processor metadata value, question mark, is calling it 312 times versus DFS paths is only calling it once. So maybe we need to look into this method a little bit more. We want to go keep on going deeper and deeper and we want to know what exactly is calling this method so much and what is that method doing. So if we run this again, it turns out that it's actually recursive. So it is calling itself and it is in the call ease. Stack prof also is really nice in that it shows us the method right there. This is that method that we're looking at that is really slow and we can zoom in here and see exactly what's going on. So we have a couple constants and those are sets and what we're doing is we're passing a class into it and we're validating that class. It has to be a string, it has to be an array and if that's the case, it returns true. If it's a compound class like an array, then it has to iterate through each of them and call that same method on itself. So it's recursive. If you look into, you might say, okay, well, we're spending a bunch of time in set include. I want to see how that's written. Maybe we can make set include faster. If you open up the file, set is a Ruby file. It's written entirely in Ruby and you might notice something kind of interesting here. We're calling a hash variable called atHash and just passing in our argument. So as it turns out, set is actually powered by hash under the hood. If you thought set was a magical performance enhancing class, it's kind of just a nice wrapper and it does some really nice things. But in our case, we can just skip the set and use a hash directly. Thank you, Van Wilder. So it turns out that Ruby has optimized instructions for hash calls. Also, that wasn't from Van Wilder if you notice that. Don't worry about it. And to figure out what exactly is going on, this is, it's really cool if you've not seen it. We have, it's kind of old hat. We can ask Ruby directly. We can generate a string of code and use our Ruby VM instruction sequence and pass it in and then actually see what Ruby is doing under the hood and how it is compiling that code. So I kind of grayed out a bunch of stuff that's going on. And on 20, we can see opt-aref is being called. And this is the actual, this is the actual hash call that we're looking for. We then want to compare it to set. And when we do that, we see, instead of opt-aref, we get opt send without block. And at this point in time, you're all like, yeah, oh, I totally get it. Right? Okay. So yeah, neither did I. I need to make sure I am not going to accidentally, okay, on silent. I need a little, I need to call in a little bit of help here. So, hey Siri, can you show me how to optimize hash calls? Let me think. All right. So it turns out that hash calls are optimized in this file called in's def. You can open it up, look at it and see this opt-aref method. Which if you're not super familiar and see, don't freak out and be like, ah, this doesn't make any sense to me. The really important part that we want to look at is right here. Whenever we call the brackets method, Ruby is checking and saying, hey, is the class that we're calling this on, is it a hash? If so, we're going to call this other method called RB hash-aref, which is the C code for that a hash call is actually written in. So that is directly using the C code. We don't have to go through layers of Ruby, versus if we look at the way set include is defined, it's using opt send without block and it has to search through all of your classes in order to find the method that it wants to call. There's a little bit more, there's like some cache classing and some other things, but we need to check the singletons, we need to check anything that is inheriting from that class we're currently calling and seeing if that has the method and that's slow. So we know that if we're calling brackets on a hash, then we can just directly call the C code responsible for pulling that value and it's much, much faster. So Ruby is optimizing hash calls by skipping the method lookup. And by the way, do not subclass hash. Do not subclass hash, do not subclass hash. One of the things I did not say is I did not say this looks to see if you are a subclass of a hash. This looks to see if you are a hash and then it calls that method, otherwise it falls back on this kind of call simple method. So you might be thinking to yourself, I don't subclass hash or I don't even know what that means. It's okay. Chances are you might be using hash within different access or you might be using hashi or you might be using rack utils header hash. If you don't know, yes you are. So I opened up this issue about two years ago where I did some benchmarking and just replaced utils header hash with a just straight up hash and it turns out that my program that I was running, looks like a micro benchmark but like end to end, I'm sending the server a request, that's the word I'm thinking of. It turns out that that is 23% faster. Don't subclass ashes, please. Unfortunately, we can't make that optimization because it would break a lot of things but I did waste a large portion of my life trying to get that to work without breaking. So thank you, Siri. Just doing my job. Okay, so back to our code we were looking at. We can switch our set to a hash. We can do it pretty easily. We are going to loop through our set. We're going to use each with object. We're going to pass in a hash. We're going to build a hash and then you can see down here we're calling it. We're calling our hash directly and this is going to skip that method lookup. So the big question is did this help? Is this going to make your code extremely crazy super faster? Well my benchmark that I was running on, I have a website called CodeTriage.com. I was generating assets. It took about 18 seconds with no cache and after this we bumped that up to an impressive 17.9 seconds. Yes. Yes my friends, 1.8% faster. Yeah, thank you for the clap. So I've been called every night. My first thought was look at that homeless guy with those nice rollerblades. Where did he get those? I used to laugh at him but then I would see him riding around really think man that guy really is barely even pushing himself down the boardwalk yet he seems to be gliding with us. He's definitely tapped into something. He's really good you know. I mean for his age it's great. Okay so we didn't get what we wanted when we tried the first time. We need to keep gliding. We're going to go into round two. We have our performance data already and this is the new stack prof and we can see that the set include is no longer our number one thing because it got changed to the method above it. Well now we want to focus on the next most expensive thing and just see if we can find some low hanging fruit. We're going to do the same thing. We're going to dig in with the dash dash method and it's going to give us this really long method which I couldn't actually quite fit on a slide so I'm going to shrink that down for you. There we go. So we are creating a digest of whatever you pass in if you pass in a string or an array and so let me paraphrase what's going on. We check if it's a string or if it's a symbol or a fix num or a big num or a true class or a false class or a nil class or an array or a hash or a set or an encoding. That's it. There's also an else and it just raises. It's fine. So if we pass a set object into here then it's going to take ten comparisons. We have to check that to ten other things just to see if we're running the right thing. Not only that, but here we also have array, hash, and set. It's similar to the same problem where we have to iterate over each of those so if you have a set that has a set that has a set each of those is going to take ten iterations just to even get to the set. So something I like to point out is if else if is essentially hidden iteration. If you're always passing in a string and you're always going to hit that first if you know you're not iterating too much but in this case we're using a lot of sets. We're using a lot of arrays. How are we going to reduce that iteration and reduce the number of the checks? So we can go faster by getting rid of iteration. We can do this with case statements or we can do it with hash lookups. So previously we had this code. I made some modifications and it is now this code. What do you prefer? Of course this is, you're like what's going on here? What's going on is we're storing all of our logic inside of a hash inside of prox and then we are getting that, oh by the way I use prox and lambda and block interchangeably if you're offended. I'm sorry. So the logic is going to live in a lambda we're going to get it and then we are going to call that logic and pass in our object digest object. If you're confused about what that means it's fine we're going to go through it. So basically you can imagine if we passed it an array, an array class, well the first thing it would do is it would return back whatever is in the array value and in this case we have this lambda. We're going to pass in our value and our digest and then we are going to digest and tell it hey this thing is an array and then for each of the elements in that array we are going to also add that to the digest. So you might notice something here is that we are calling back into the same hash. So if you weren't totally clear on that I made it a little simpler by slowing it down. It makes sense right? Okay so you can check the code out on GitHub I just wanted to do that. What was this faster? Well we also improved some other things so we're going to work in this one and our baseline is 14.9 seconds. After this optimization now we're down to 12.5 seconds which is 16% faster. Awesome. And then as I was writing this talk the interesting thing about writing a performance talk is you keep on getting new pieces of data and you keep on having to rewrite your talk based on those new pieces of data and then as I was writing this talk this tweet came up from Nick Sutterer he's the trailblazer maintainer and creator and he says did you know that calling Ruby objects is actually faster than calling procs? You know, hashtag WTF Ruby which should totally be a thing. Okay well to understand why we have to take a look at how a block or a lambda proc actually functions. So in this example we have a variable called foo and we're setting it to a string and then we're creating our lambda and we are putsing that string. In order for this code inside of the lambda to actually run it has to be able to find the variable. So when it runs it's going to say oh okay foo oh well there's no foo defined inside of this block. This block doesn't define foo so we need to look for it. Well what was the context of when we were originally created? Okay it turns out that that has a local variable called foo and this gives procs and blocks and lambdas the ability to access objects at the time that they were created which is a pretty useful feature but the downside is that we have to look up that scope every single time. Every single time we call that block or that proc we have to say okay where were we when we originally created this and that scope lookup is overhead and that overhead is slow. So do we have any solutions available to us? So this might be the only time I've ever seen this recommended as a performance improvement at a RubyConf talk but the way to make our code faster is to use refinements. Yes if you're like what's a refinement? I'll get to that in a second. If you're laughing thank you. So they're not known for being fast and basically what a refinement is is it allows you to monkey patch a class but without affecting all of the global scope you can say oh I just only want to want to affect say you know string I want to add a method to string but only in the context of my code not I don't want to affect the Rails code or you know whatever library is you're using. So it looks a little bit like this it's a bit daunting I'm going to walk you through it first thing we do is we have this module called add value to digest and inside of it we say okay I want to monkey patch string just in this context and inside of there we add a method called add value to digest we do the work we want to do and then later we have used this using keyword to apply that to another module of refinement digest utils. So now anytime you run something within the context of refinement digest utils if you pass in a string it's going to have this new method as opposed to just adding that new method to every string in all of your program forever. So I have some code down at the bottom and you can see we also have a digest method on the actual module itself and all it does is you pass it an object and it calls add value to digest on that object that's all it does. Below we are creating a string just as I'm a string we're creating digest object and then we are passing that object into this module. The object we call add value to digest it says oh hey look I'm inside of the refinement digest utils module and so do I have this method oh it turns out I do it's right up here it's defined right here then we can repeat for all supported types we refine array we refine hash we refine set and so we're doing a couple of things one we're skipping up having to figure out what class we're using so previously we had a little bit of overhead where we had to call object.class and that takes a you know it's really fast but it takes a little bit of time and then we had to have that hash lookup which is really fast but again it takes a little bit of time and we are replacing both of those things with one method lookup every single class knows how to digest itself sounds not pleasant so did we get faster? okay 16% faster than our already 16% faster example so yes quite a bit faster I do have a little bit of a caveat here I put it in kind of legalese it turns out that doing this in a case statement was actually faster than the recursive hash but that's why you should maybe have a performance buddy and maybe two of you can work on optimizing the same code at the same time and you get different solutions and you can compare those solutions I wrote the recursive hash thing and I thought it was cool so I wanted to present on it but I also didn't want to change all my slides and thank you I didn't write the refinements code that was by a coder on GitHub by the name of Buk and works for Shopify so thank you very much one of the things while I was doing this and I was talking to Nick and Charlie who's presenting over in the other room right now is wouldn't it be really cool if in my case my lambdas are not actually using any of the scope from where they were created they're only calling methods on things you pass in so they don't need that scope lookup so why should they be charged for that scope lookup so I made a feature request for anonymous functions without scope lookup overhead and the general idea is we can use this for performance and the really cool thing about coming to RubyConf and presenting here or just being here is there's a lot of people from MRI from RubyCore, from JRuby and you can just go up to them and be like hey did you read my feature I mean don't bug them or anything but you can get a little bit more real world feedback and I was talking to some of them and by the way would you ever use a transition other than flames I think there's no reason and the feedback I got was the feature is good but needs a different syntax currently when I wrote it I just made up a syntax on the top of my head so we could just start talking about it right now and I was like oh proc new pass scope false or something and they were like yeah you know that doesn't really fit in that well with the way the VM and everything works so who likes audience participation okay I have all of your attention if you have an idea for a different syntax of how to do that yes we have one more audience participation like or in the back thank you what? deaf deaf I think so I was talking to Koichi about it and I think he actually might have mentioned that but said that there was a problem with it we can get into that in just a little bit so it's also really cool if you went to Koichi's talk he's talking about doing guilds as he mentioned he was like oh in order to pass a proc object between guilds we need immutable procs which is basically this so I'm pretty excited about hopefully it may be happening sometime so alright I do want to slow things down and go back like so far you might be like okay great sprockets asset file time is cut down like awesome how do I actually make my code faster we can break down what I did and apply it to any problem first we want to identify a goal in the case of slo-mo he said I want to do the basic things in skate sounds pretty simple but it's important for us we want to make sprockets faster after that you can gather useful tools slo-mo said he's going to grab some skates those are really nice skates wrist guards and iPod personally I would have gone for a helmet but it's not like I'm a neurologist yeah so for us that means profiling tools so it can mean in this case stack prof another one I've had a lot of success with is derailed benchmarks Nate is going to be presenting about fixing memory problems in here I think in the same room right after this so there are more tools you can check out once we've done that we can iterate on the problems for slo-mo that's going to be not only did he want to skate he wanted to do less work and coincidentally that is what makes a good performance optimizing programmer the desire to do less work when programs do less work they actually run faster and we can do this as programs by better understanding how our programs are actually running and what is going on under the hood for this I recommend checking out the microscope if you're using CRuby if you're using MRI the better you understand what the VM is doing the more you can optimize it one of the tricks I used earlier is this VM instruction sequence and decompiling code and actually looking at what's going on or you can just like screenshot this I mean there's a bunch of other stuff in there as well but I use this all the time if you are kind of daunted by that or don't like reading or for whatever reasons you just want to get started pretty quickly there's this repo called fast repo where there's a whole bunch of examples of it's like instead of this do this for example instead of calling .reverse.each on an array you can call .reverse each and it's got the benchmark you don't even need to know why it's faster you can just use it so you can go there scan it and or you can wait until you have some slow code and read through your slow code and then come here and see if you can just match anything the important part is the more you understand about how your code works the faster you can make it also find a speed buddy I mentioned that earlier it's great once you get into performance stuff you find other people who like performance stuff and then they share their performance things and you share their performance things and then you meet up at conferences and get high fives and it's amazing Nate right here is like yeah performance friends that should be a thing or multiple buddies so one of the things I wrote on the abstract was C how fast your programs will go if you came here for the C portion of programming this is it we're going to be adding this gem to our project SAS C Rails so SAS C uses Lib SAS right now if you have SAS in your library it's all written in Ruby if you do this you're going to get a very minor 31% performance boost you know if you need it if you want it it's a lot of work I know but Sprockets 4 is going to support SAS C out of the box but it doesn't do it yet and you know one of the reasons why it's such a large performance boost if you're using SAS SAS was slow it's using a lot of processor power to do what it does and it turns out if you drop down to a lower level to see it runs a lot faster and Lib SAS is not only is it just written in C it's also written in C and then they optimized that C so if you came here hoping for a like yeah we're going to abandon Ruby and write everything in this other language don't please I beg you to stay I've written Ruby for about 10 years and I love it it's very deeply it's very near and dear to my heart and you can also optimize all of the previous optimizations I did were just in Ruby and just understanding Ruby and making it faster and when that wasn't quite good enough and we did need a lower level language we could still drop there drop down to that language without having to rewrite the entire project in C or assembly so you can just find that slow part and tag team with another language use Rust with Helix if you want to use Rust use Go with Gorb this was like a super new thing that was just announced you should totally use it and submit pull request and it's 100% production ready is that yeah okay awesome also have you tried JRuby for your application depending on what you're doing that might be an option if you are accessing a lot of things that have already optimized Java libraries maybe that might be a way to go and I have another little disclaimer here my refinements are the refinements performance example was actually 200% slower on JRuby and Charlie's not really happy and doesn't maybe want me to do that so we still need to think about that okay we can also talk a little bit about cached compile sometimes this is you've run rake assets pre compile you already have your assets on disk how can we make that faster well it turns out the slowest part for co-triage was booting up the application and when I stack profit we're spending the most time in require so require is kind of slow when you have a lot of gems on your system so here's a gem you can add your application called boot scale I just imagine it's Canadian it's a boot scale now you can remember it my wife's Canadian so what it does is it caches require lookups and without it your system require takes longer especially if you have a lot a lot a lot of gems on your system because it has to iterate through more things on my program on my system on my MacBook we went from a 6 second cache compile time to a 2 second cache compile time which is 300% faster which is not too terribly bad frankly at this point I'm just trying to get to the end of my life without becoming an asshole again I like the look I like it how's it going hello purple you're weighing the people that love slo-mo are cheering for one person that got away that escaped and got to real freedom where he escapes all day doesn't apologize he's simply doing what he wants to this is your good ol' days it only gets worse from here on okay the good ol' days are these so if you take away nothing else from this talk please don't be an asshole I think it's the most important thing in programming along with that I ask you to not apologize for the programming language that you love every year it's I come to RubyConf and every year they release a new version of Ruby and it's getting faster and faster and also along with that more and more people are learning how to performance optimize their own Ruby code and so libraries are getting faster and faster it's kind of like exponential effect in my head anyway you can you can set your own goals and I want to make this Ruby code as fast as possible without having to use another library we can go at our own speeds and towards whatever direction that we want to in our lives and these are the good days of our programming lives we can really live them to the fullest and focus on the things that we want to focus on not what somebody else tells us is important so sometimes today I want you to take a little bit of time to stop and breathe and think about what you want from your career and your life and your code I want you to slow down for a bit make sure you're going in the right direction please if you will with me everybody just take a deep breath and now we are ready to skate think about liability we have a question I have five more minutes if you want to get up and go get coffee or something else please be mindful and respectful of the peaceful space around you and of those of your attendees are there any questions? was the movie filmed? yeah I believe this was California oh it's Venice okay I guess yeah Venice Beach and yes sorry the lights are really bright I tried it I did that you mean just like the fine method yes I'm still in the process of a bunch of benchmarking I probably want to get somewhere an intermediate where we're not so slow on JRuby and Charlie recently recommended a method that was a little bit better the problem is well if you do just like yeah I don't know I'm looking into it okay so the question was why won't sprockets die already? sprockets die already so I have a talk at a blog post you can check out online called Saving Sprockets there's another great counterpoint to that it's a a little inflammatory but that's his style called Don't Save Sprockets and that blog post walks through how to hook up rails with webpack and if you're using a green field an app please like try that check it out as a so sprockets can't just go away we have a lot of legacy apps we have a lot of people that do rely on it if it turns out that in the future 100% of everyone is using sprockets you know rails and webpack and just getting rid of sprockets there is a little bit of of prior art for when we got rid of like scriptaculous prototype and then jQuery was the thing that was brought in so it's kind of a the cart needs to lead the horse sometimes like if the entire community is doing something and it's like clearly the best thing I would probably say if Basecamp ever starts doing that like I don't know if you know this is kind of like an open secret but like rails is basically just basecamp in a like library format it's true you know it's like we didn't websockets wasn't important until basecamp needed websockets so and then we have action cable like actually sprockets like an asset generation wasn't a thing until they needed an asset management solution and sprockets was a separate thing that they included in and they were like this is now the asset pipeline framework the other potential solution besides just totally abandoning it is maybe a middle ground where we say this is how sprockets hooks into webpack or this is how sprockets hooks into that and I mean that's I guess up to me or somebody else who wants to convince me of that but before we can get there I think we need a good idea of what is the what is the best practice there's a lot of library churn in the JavaScript land right now like before people were asking me can we please use webpack everybody uses webpack it's the thing it was bower can we please use bower everybody's using bower I can't wait till sprockets uses bower and like I don't even think of job like people coming out of a bootcamp know what bower is anymore so it's yeah it's a complicated question but a very good question thank you thank you all very much for coming