 So, shall we get started? Are we ready for this? Okay, let's do it. Welcome to Kansas. I have always been very excited about this RailsConf because I always want to come to Kansas. I have always heard about this movie called Wizard of Ours and as you can tell, it's a little bit ahead of my time but I watched it before. I come here in preparation for a conference but as we learned in Jeremy's keynote the other day, it turns out these lines on the maps are state lines and Kansas is on the left and the other half is Missouri and turns out Kansas City is actually not in Kansas. So, I guess we're not in Kansas anymore. Thank you for that. I had to redo all my slides to add colors to them so if they don't look very good, that's probably why. I am Godfrey. You can find me on the internet says Chankin code and I'm very excited to welcome you to your employee, new employee orientation. I hope you're at the right place. Yeah, yeah. If you're looking for RailsConf, I'm afraid they've moved. So, just kidding. This is RailsConf. Welcome to RailsConf and thank you for coming. I always say there's a very personal thing about RailsConf to me and I love coming back. This is actually my fifth RailsConf. Five years ago in Austin, that was my first RailsConf and actually I attended RailsConf Austin as with on the student scholarship. So, if you were there, thank you for chipping in for my ticket and thank you for giving me the opportunity to be part of this community and next year I went back to RailsConf Portland and for some reason I had suddenly the courage to go bug Aaron and say, hey, I have a poor request and you merge it and for some reason you end up merging it. So, I have my first commit in Rails and the next year, soon after the next RailsConf in Chicago, I joined the Rails core team and the year after I spoke at RailsConf for the first time, which is last year and this year I'm very honored to be part of the program community on RailsConf and helped create it behind the magic and new in Rails five tracks. So, if you went to those talks and like those tracks, that's where they came from and like Jeremy said the other day, Rails exists because of its community so thank you for being part of this community. So, following Aaron's lead, I would like to announce some new Rails five features. As you learned today, Rails five will come with PHP support and following his lead, I would like to announce JavaScript support for Rails five and in fact, you don't even have to wait for Rails five, you can just get it today by running gem install JavaScripts. Once you have installed gem, all you need to do is require JavaScript on the top of your Ruby file and there we go, you can just wrap anything in JavaScript block and write your JavaScript code in there even supports things like functions and that's very handy because what is everyone's favorite JavaScript feature? Of course, that's callbacks and what's everyone's favorite Rails features? Of course, that's also callbacks so the JavaScript gem let you combine the best of both worlds like here's the active recommender, you can have your favorite before create callback and you can write that in JavaScript so actually just like the Fubi gem, this is a real thing you can use and an insane amount of engineering when it did jump and if you want to learn more about that I suggest you watch my talk at Garuko called Dropping Down to the Metal, you can find that on YouTube. I have another thing to plug, if you went to the Lightning talk from yesterday you would already know this but I helped coordinate a newsletter called This Week in Rails where you'll find the latest commits and pull requests and things like that that went to Rails that week and here is a sample from last weekend it includes sensational headlines like local scientists discover new method to manipulate time faster code found to perform better on the load JRuby builds to Flakey to be useful and Regic experts to debate left to right or right to left is one better than the other if you haven't already go to bit.ly slash Rails Weekly to subscribe to this newsletter the next issue will be coming out in a few hours and you will probably learn something new from there. Speaking of newsletter, I'm also part of a newsletter which is a product newsletter so if you are not already signed up to Scala you should sign up at Scala.io and select the almost daily insider channel email preference and we'll usually write about our experience building Scalite and a lot of times we'll write about things like oh I had this problem today and we don't know how to deal with it and some of the customers will say hey I had that problem last week and here is a gem I found that does that or sometimes the opposite would happen we'll be like ah we solved this problem today and our customers would write back and say ah that's really great I have the same problem at work so if you're interested you should go sign up and if you have pen and paper you might want to write down that secret URL that's actually my personal referral URL and I heard we have plans to distribute bonuses this year based on referral credits in our account so please help me out. Anyway let's talk about Ruby. So Ruby is great we all love Ruby that's why we're here. Ruby has a lot of nice features it reads really nice metaprogramming is pretty awesome but there's also a problem to Ruby which is that it's pretty slow most of the time it doesn't really matter but occasionally you might hit a wall when you try to do something in Ruby and it's just too slow for use case on the other end of the spectrum or perhaps on the other extreme of the spectrum there's C which is super low level which is super fast basically it's like as close to metal as you can get without writing assembly and that's great but it's also pretty dangerous you can very easily write code that crash your program at run time in an unexpected way and there are also a lot of concepts that are a little bit hard to grasp. So in Ruby we have this feature called native extensions that give you the best of both worlds. So for example when you run gem install JSON what you're getting is actually two different things for the price of one. So by default you would get a thing called JSON pure which is a pure Ruby implementation of the JSON encoder but if you're on a supported platform you'll also get another thing called JSON colon colon ext which is native extension so it's the same JSON encoder API written in C and so that's like super fast but to you as a user you don't even notice the difference because you just call it like a regular Ruby class and you call this method normally and under the hood it basically is transparently called the C methods that does the work for you and as a user you don't have to care about that and chances are you're already using native version without knowing it. So native extensions they are great why don't we write more of them? Well there is a catch so it's indeed the best of both worlds but it's only the best of both worlds if you're the end user that's using the gem. This is fine. David who created Rails have said something like this to me here's the general value of Rails development we will jump through whatever hoops on the implementation side to make the user facing API nicer and I think I would personally extrapolate that user facing API to developer experience in general and I think that's a good goal to have like it's good to make the experience for your developer users as nice as possible while keeping a beautiful interface on the outside and do whatever you need on the inside to make that work so that's a great goal to have and here is a good example of that so Sam Saffron who you might have heard of he did a lot of Ruby performance work and he was a Ruby hero from last year so he noticed something in Rails so it turns out in Rails in active support in particular there's a method called string.blank so it turns out this method is called a lot both inside of the Rails framework and also in user code so this is also the implementation of the flip side called present so you might have seen things like user or params colon user dot present so this is actually the method it's talking about this is the implementation in active support as of a few weeks ago there are some improvements since then we'll talk about that later so it's pretty short method you basically reopen the string class and it basically check if the string is all white space characters and that's all there is to it it's one liner, it reads beautifully like all of your Ruby code and that's great and apparently this is a very useful method because we use it in our application in Rails to make it a performance hot spot according to Sam so what he did is he made a gem called fast blank that re-implemented the same blank method in C and according to Sam this C implementation is up to 20 times faster than the Ruby version we saw on the other slide and he said in some applications you can get up to 5% performance improvement on the macro now as I said there are some recent improvement to the Rails version but for the purpose of this presentation and this particular number doesn't really affect that much because the optimization we did in Rails is about the in edge case or common case that is when the string is empty so now if we can so as the user you don't really need to know the difference you just get the fast blank gem in your app and all of your code works seamlessly because it provides the same string.blank question mark method with just with a different implementation under it so if you can get the performance you want and the user, the developer experience you want then that seems great we're done here, right well there is a problem though the problem is me me being the developer that knows just enough C to be dangerous if you give me a C program with a variable called PTR if the code doesn't work out the first thing I try is probably add a star to it and if it still doesn't work maybe try more stars if more stars still doesn't fix it maybe try and understand and the problem with this is as I mentioned before if you're like me if you write your C code like this your program can sack thought at run time and that's pretty bad because if you're embedding your C code inside a Ruby processor it will crash the entire Ruby process there's not like a regular exception that you can handle so at Skylight we have a similar problem oh by the way this is I don't know if I mentioned it but this is where I work we are a performance analyzer for Rails apps and to do that we have to have an agent that we put inside your app to collect performance data on production and we want to make sure that our agent is as lightweight as possible we don't want the thing that's supposed to be measuring your performance to become the bottleneck itself so we could write that agent in C or C++ and fortunately most of the engineers are aspiring to meet and they don't randomly add stars and ampersands to variables in C but even then we don't feel very confident that we can get away with writing and maintaining our own C code that goes inside of all of our customers apps there are a lot of native extensions in the Ruby community like NoKoGiri or JSON Jam in their early days they all had various issues of sac folding at runtime and those people are way smarter than me and if even after very careful writing they still occasionally crashes then we definitely won't want to recommend our customers with something like this in their app so what is the alternative? Well at the time when we start this project Rust just announced that they would they have made some very good improvements at the time so we thought we will perhaps give that a try so what makes Rust different than writing your native extension in C or C++? Well let's look at the Rust website Rust is a system programming language that runs blazingly fast, prevents secfots and guarantees threat safety well that's a lot of words that doesn't mean a lot to me yet but they have another slogan that tries to put the same thing in different words which is hacked without fear the goal of the Rust project is allow you to basically make system programming more accessible to more programmers and it does that by having a compiler that knows about a lot of these things so what makes Rust special is that as a compiler that can find most of these well actually all of these errors that could cause your program to crash at runtime and flag them at compile time and if you don't do everything correctly and if you don't satisfy if you cannot convince the Rust compiler that your program is sound it simply won't, it will simply refuse to compile your program and therefore you don't have a thing to run at runtime and so it cannot crash at runtime so there are some features in Rust that makes that possible I can't get into a lot of details of Rust today and this talk is not really about teaching you Rust but I'll try to describe them at a very high level so you can perhaps try to understand them without seeing the actual code the first feature of Rust that makes this possible is they take care of managing the safety of your memory without using garbage collector so Ruby is also a language that offers memory safety guarantee and Ruby you cannot write code that exists random locations of memory and causes your program to crash at runtime that way so it's maybe it perhaps sounds a little bit crazy to say that oh like your program cannot crash at runtime if you write in Rust but in fact you're already used to that because your Ruby program also cannot crash at runtime the difference is that Ruby manages that by using a garbage collector and Rust does that without a garbage collector and what Rust does is basically tracks lifetime of all your variables at compile time so it knows exactly when and where it needs to locate things and when it needs to clean it up so it doesn't need to pause your program and clean up with a garbage collector periodically it also allows you to do concurrency without data raises or raise conditions which I don't have time to get into right now and the final feature that is particularly relevant for us it has this concept of zero cost abstraction so in Ruby or in frankly most other languages you always have to make a trade off between abstracting your code versus performance so like maybe you notice that you are doing you're repeating a bunch of steps in a few places and you want to extract that into a method in Ruby so that's fine and that's what you should do most of the time but unfortunately this incurs the cost of an extra method invocation and when you're talking about really performing sensitive code you have to make the trade off between how much you want to abstract your code between and how performant you want your code to be so in Rails for example we have a lot of modules and we call it super a lot and that's how we decide we want to abstract our code ideally but occasionally there might be cases where we realize this is like really hard path and we really would prefer not to incur that cost and we have to inline some of the things right so in Rust this is one of the biggest feature in Rust where you don't actually have to make the trade off between abstractions and performance because the Rust compiler is smart enough to notice that this method can be inlined into that other thing so I would just do that or in fact a lot of time if you use higher level constructs in Rust like iterators is actually giving the compiler more information about how the compiler can optimize your code so it actually makes the program run faster so for example in Ruby you might want to write things in each loop and we often do and that's fine but that's incurring the extra cost of calling each method and stuff on the other hand on Rust if you use an iterator actually makes it faster than writing a hand rolled loop because the compiler knows this is gonna be a safe iteration so it can remove some of the bound checks inside for each iteration so I can't get into a whole lot more details but Yahuda gave a talk on Rust at RailsConf here last year so if you are new to Rust and you're curious about why you might want to look into this language you can look up his talk from last year so let's get back to Fast Blank and I guess we'll look at the Fast Blank implementation and see quickly so this is the Fast Blank body so you probably cannot read it but it's fine we'll walk through it step by step together so basically at the top you basically have the method signature and some boilerplate to extract some pointers and so you then have a line that quickly check oh if the string is empty then return right away so you can avoid doing a bunch of extra work and then the main part of the method is basically you loop through all the characters inside the string and there's some more boilerplates about pointers and finally you have a switch statement if you encounter a white space character you keep looping basically and if you encounter a non-white space character you know that this string is not blank so we can return false immediately otherwise if you get to the end of the loop you know that all of the characters you have encountered in the string are all white space characters so we can return true so this probably looks a little bit more scary than it actually is perhaps only because it's on a slide but this is like 50 line of code and it's not particularly difficult to reason about so if we can get up to 20 times faster performance writing 50 lines of C code seems worth it so next let's look at the equivalent fast blank implementation in Rust and here it is so this is literally a one-liner function in Rust that does exactly the same thing and handles all the unique code edge cases correctly let's walk through it so basically define an extern C function this basically tells Rust please I know that this code is gonna be called from a C program so please use the C function calling convention for when you actually compile a program that part is not particularly important except to illustrate the point that Rust is a pretty low level system program language that's designed to interrupt with other C programs and the Ruby implementation that you're probably using the MRI or C Ruby implementation is a C program so they work pretty nicely together and because like I said the Rust compiler cares a lot about safety so you have to do a little bit of work to annotate your code to the compiler and give it enough information to know that your code is doing correct things so here we're telling the compiler what the type of the input to this function will be the specific type we use here is not particularly important except to point out that you need to tell the compiler which type each variable is gonna be it also helps Rust compiler to figure out how to allocate memory for all these things because as much as possible Rust try to allocate things on the stack so it doesn't have to which makes cleaning up a lot faster which is another key to Rust performance. So here we're annotating to the, we're telling the compiler that this method is gonna return a Boolean value because the blank question mark method is expected to return either true or false depending on whether the string is blank or not and the actual body of the method is perhaps gonna remind you about the Ruby code that you're used to writing so here we are getting all the characters from the string as an iterator and we're using these high levels combinators like .all you're probably familiar with this in Ruby it's equivalent to the array .all question mark method that loops through the array and check if each of those items in the array matches a certain condition and you have things like block which is also a thing we like to use in Ruby and finally you are for each character in the buffer you're checking whether it is a white space character or not and this is white space is a method provided by the RustStand library that knows how to do the big switch statement we have in the C implementation. So this is pretty terse and perhaps surprisingly so because we're now programming a system programming language and surprisingly it looks kind of similar to what you would write in Ruby, right? So is this gonna be sacrificing a lot of performance compared to the C version because it's so terse and so high level? Well we ran some benchmarks and if you look closely this is iterations per second by the way so the higher the number the better so at the bottom you have the pure Ruby implementation and on top we have Rust and next to it is the C implementation if you look closely the Rust version is actually a little bit faster than the C version even though we're using high level things like iterators and .all so of course I'm not being very scientific here I'm not trying to convince you that Rust is faster than C I'm just trying to convince you that you can get away with writing the high level code that you used to without sacrificing performance at least you'll get code that is in the same ballpark as the C equivalent. So let's look at this code again turns out there's a catch I didn't tell you about the catch is there is a lot of glue code that I didn't show you here if you look at the full Rust versus C extension you notice that on the left the red part is the one liner I showed you and on the right that's the very big C function body that I showed you and indeed the C function body is much larger than the one liner on the left however if you count all the boilerplate code around it they're roughly the same size in terms of line of codes not all is lost though I should point out that on the Rust side a lot of those are common shared abstractions like the buffer implementation and things like that that could be extracted out whereas on the C side that's literally your business logic so you have to write that amount of code every time you want to write is the extension like this whereas on the left that's actually the only the one liner is the only thing that's specific to your extension still this is not very nice so Yahuda and I have been working on this project called Helix and our goal is to make most of those boilerplate go away and take advantage of Rust features like zero cost abstraction to help you focus on writing the thing you want to write without having to roll all the boilerplate all the time so here is the entire fast blank extension written in Helix so we are using Rust macro feature which lets you write things that's pretty similar to Ruby DSLs and so here we are at the top we're importing our library and here we're declaring a bunch of Ruby types and the first thing we do is we try to reopen the Ruby string class and we add a method called is blank and you might find this syntax somewhat familiar and finally in the code we have the one liner that we saw earlier and this is all the code you need to write to write an extension like fast blank and Rust and Helix so my personal goal for this going forward is I would like to be able to experiment implementing some of Rails features in terms of in Helix so we already have an extensive test suite in Rails and there are a lot of modular parts that is like string.blank that can be swapped out into a Rust extension without affecting any of the user facing API so we can continue to iterate quickly on the Ruby implementation on the sorry on the pure Ruby implementation on the Rails side while taking advantage of the existing Rails test suite and write these experiment with these lower level implementation and as long as we keep running against Rails test suite we can be fairly confident that things are in parity and if so it would of course be an optional thing that you can install just like fast blank but if your platform supports it and if you trust it enough it might give you some substantial performance gain and there are a lot of low hanging fruit for smaller piece like for example the active support duration class like when you do one dot day that's the active support duration class that's created and so there are probably a lot of pieces small pieces like that that we can experiment with using Helix and eventually we can probably work on writing bigger pieces like perhaps the core routing library in Rust someday so that's all great but what about my app? Well so while we're working on this project we have a friend who works at Sesty and they have a pretty interesting problem for us so they for the most part the stack is a Ruby stack and they are catering company in San Francisco that tries to deliver organized views for companies based in the Bay Area and one of the problems that they have is they have a meal matching algorithm so for example there will be some constraints like some of my employees have these allergies so they cannot have meals with these items in it or sometimes there will be the opposite some of my employees prefer these features in the meals and you should make sure the meals have have these like vegan or gluten free and et cetera so at the end of the day there the problem boils down to something like this you have some tags for each meal and you have some tags for different preferences your users might have and what you want to find out is whether the meal the tags in the meal fully satisfy all the requirements in the second array right so this is the tags in the meal and what you want to see is if this first array fully contains all the numbers in the other array and that basically boils down to what we call set containment problem and there is a known trick for that oh I should mention the reason they care is they have written this algorithm in Ruby and it's taking a long time to run like sometimes like measured in minutes maybe up to 30 minutes right and they measured it and a lot of time is being spent in this set containment algorithm so it would be interesting to see what you can do if you implement that algorithm in Rust so there's a known trick for setting testing set containment if you go to Stack Overflow and look for it you'll probably find a solution similar to this basically to check whether an array fully contains another array you can do an intersection between the two arrays basically make a new array that only contains items that is in both of the original and the other array and then you can check if that array is equal to the preferences array so this would tell you whether your meal fully satisfied your preferences so this is really nice to read like of the Ruby code that you're used to writing and usually it's fine right like I think it's good to be able to start with something like this and so they have this in their code base and they have an existing test suite to test that this is working so we replicated that test suite and replicated this method and we confirmed that it indeed works as advertised but we can do better than that because we happen to know that all of the numbers in the array is sorted and all of the numbers in the array are unique so there are no duplicates in the array so after thinking really really hard we realized we can do better so here is a different algorithm in pure Ruby to do the same thing I will walk through it quickly so first of all you check some edge cases if either of the array is empty then you can very quickly decide one way or the other without having to do a bunch of extra work then there's some boiler plates to checking tracking the position into the index of the other array and basically you loop through all of the items on the longer array and keep a pointer into the other array and try to advance the pointer as you find a similar item and finally if you get to the end of the loop you know that there are some items that is in the other array that's not in the original array so it took us a while to figure this out so if you don't immediately understand it don't worry about it but the point is we wrote this and we run it against our spec test we have and it passes and according to a benchmark it's up to seven times faster and the usual case is more so it depends on the particular test case right the usual case is more like two X and but it goes all the way up to seven times so that's pretty great you basically figure out the Google interview question you should give yourself a pat on the back at this point but what if we write that in Rust so that's what we tried next again we're using Helix so we are using the declare types macro we're reopening the array class and we are defining the same fully contained method with the type annotations and here is the same code that checks for the edge cases and the same boilerplate for tracking the array index interestingly here in Rust as I mentioned earlier if you use iterators the compiler is actually able to do smarter things so here we're actually using an iterator instead of writing a hand rolled loop because we're using an iterator the loop body end up looking slightly better than the Ruby version in my opinion but otherwise it's exactly the same algorithm and finally if you get to the bottom your return false and we benchmark this and it runs up to 173 times faster than the Ruby version depends on depending on the workload and I plotted this on a chart you probably can't really see all the details but each basically we ran a bunch of different test cases and the blue line is the pure Ruby implementation that's the one liner the green lines are the fast algorithm written Ruby and the yellow lines are the Rust implementation of the same algorithms so you probably can't see a lot of blue and greens because they're mostly at the bottom and I think the numbers are roughly the Rust implementation is usually tend to up to like 173 times faster than the Ruby implementation so that's pretty great and here is we are almost out of time so I'll wrap up quickly so here is where you can find the code for the Helix project is major work in progress but we are on a pretty good track and if you would like to help us you can come talk to us afterwards and finally I would like to close with this historically scripting languages were slow and they're still relatively slow today and because they're slow they were mainly historically used as a coordination layer that delegates to heavier tools written in system languages like C so for example you might write your shell script and bash that delegates to Unix utilities like sword, grab or work counts to do the heavy lifting however it turns out that a lot of the time we are actually doing IO bound operations like reading and writing to the hard drive or waiting for network or talking to your database because IO operations are usually so much slower the performance difference between a scripting language and a system language doesn't actually matter so much a lot of the time but because scripting language is so much easier to write and so much more pleasant to use we end up moving a lot more of the code into the scripting layer and instead of writing everything in C so since web applications are so IO heavy that worked out wonderfully for things like Rails and the fact that you can have 16 or 32 Unicorn process on like an eight core machine it's probably a good empirical evidence to say that that's true however at the end of the day we still have some occasional computationally heavy operations and now application turns out business logic is what defines your application what set it apart from other apps so I think we're entering a new era where we have taken a lot of the advancement from scripting languages especially on the ergonomic side and move that into system languages like Rust so with Helix you can readily move any competitionally heavy part of your app back into Rust without having to switch your entire stack to something like Goal and in my opinion, Rust is particularly suited for this task in Rails teams thanks to the safety guarantee offered by the Rust compiler more of the team members will be able to tanker and experiment with the Rust code without worrying that it would cause problems at runtime the worst you can do is not compile make the program not compile so like you probably try to get help at that point in our team, everyone end up picking up enough Rust to be able to fix bugs or make minor tweaks to our Rust agent in roughly half a year or so and we think that will continue to be true no pressure Rocky so our goal with the Helix project is to make this even more accessible to more Ruby teams so we can keep writing most of your code in the language you love without fearing that it will be too slow eventually so because you can always drop down to Rust if and when you need to do that so once again, that's all I have today and you can find me on the internet as chenkin code thank you for your time let's make Ruby great again, thank you