Opening Keynote by Patterson
This is a modal window.
Das Video konnte nicht geladen werden, da entweder ein Server- oder Netzwerkfehler auftrat oder das Format nicht unterstützt wird.
Formale Metadaten
Titel |
| |
Serientitel | ||
Teil | 33 | |
Anzahl der Teile | 89 | |
Autor | ||
Lizenz | CC-Namensnennung - Weitergabe unter gleichen Bedingungen 3.0 Unported: Sie dürfen das Werk bzw. den Inhalt zu jedem legalen und nicht-kommerziellen Zweck nutzen, verändern und in unveränderter oder veränderter Form vervielfältigen, verbreiten und öffentlich zugänglich machen, sofern Sie den Namen des Autors/Rechteinhabers in der von ihm festgelegten Weise nennen und das Werk bzw. diesen Inhalt auch in veränderter Form nur unter den Bedingungen dieser Lizenz weitergeben. | |
Identifikatoren | 10.5446/31538 (DOI) | |
Herausgeber | ||
Erscheinungsjahr | ||
Sprache |
Inhaltliche Metadaten
Fachgebiet | ||
Genre | ||
Abstract |
|
00:00
Reelle ZahlSoftwareComputersicherheitProdukt <Mathematik>Metropolitan area networkComputerspielElektronischer FingerabdruckSoftwareentwicklerZahlenbereichMomentenproblemProzess <Informatik>Dienst <Informatik>PunktUmwandlungsenthalpieBitTypentheorieKartesische KoordinatenHilfesystemRechter WinkelMultiplikationsoperatorEinfach zusammenhängender RaumBefehl <Informatik>CASE <Informatik>InternetworkingAnalysisGebäude <Mathematik>Overhead <Kommunikationstechnik>Minkowski-MetrikPhasenumwandlungComputerunterstützte ÜbersetzungWort <Informatik>TemplateMinimumRechenschieberHyperbelverfahrenDefaultPhysikalischer EffektDifferenteVirtuelle MaschineSuite <Programmpaket>Bildgebendes VerfahrenSoftware EngineeringPrimidealMailing-ListeSpeicherabzugJust-in-Time-CompilerYouTubeTwitter <Softwareplattform>CodeComputeranimation
08:39
MultiplikationsoperatorGradientAggregatzustandWeb-SeiteBitForcingServerNP-hartes ProblemMereologieProgrammierumgebungSelbstrepräsentationHochdruckUnternehmensmodellVersionsverwaltungInformationHomepageMAPInstantiierungMathematikGamecontrollerKonditionszahlTemperaturstrahlungSoundverarbeitungt-TestElektronische PublikationKartesische KoordinatenVirtuelle MaschineThreadReelle ZahlEndliche ModelltheorieVerzeichnisdienstVererbungshierarchieOffene MengeGruppenoperationGüte der AnpassungRechter WinkelKlasse <Mathematik>Prozess <Informatik>DatensatzEin-AusgabeWrapper <Programmierung>BitrateAutomatische IndexierungMailing-ListeUltraviolett-PhotoelektronenspektroskopieAutomatische DifferentiationZeichenketteWasserdampftafelCoxeter-GruppeTemplateRechenschieberVariableFortsetzung <Mathematik>Lie-GruppeApp <Programm>HackerFigurierte ZahlSpeicherabzugDelisches ProblemVorlesung/Konferenz
17:09
GeradeBitMetropolitan area networkProzess <Informatik>MultiplikationsoperatorPunktGlobale OptimierungTaskMereologieFaserbündelATMHypermediaAutomatische HandlungsplanungRechter WinkelHalbleiterspeicherWeb-SeiteWellenpaketCoxeter-GruppeReelle ZahlNebenbedingungAblaufverfolgungFreewareSoftwaretestProdukt <Mathematik>UmwandlungsenthalpieBrennen <Datenverarbeitung>Reverse EngineeringQuelle <Physik>Güte der AnpassungCodeQuick-SortEchtzeitsystemDatenparallelitätLaufzeitfehlerElektronische PublikationComputerunterstützte ÜbersetzungKomplex <Algebra>Arithmetisches MittelLoopBootenMaßerweiterungGruppenoperationQuellcodeLastDialektObjekt <Kategorie>IterationPhysikalisches SystemStatistikSystemaufrufSkriptspracheWurzel <Mathematik>DistributionenraumDifferenteEliminationsverfahrenSoftwareentwicklungRechenschieberEinflussgrößeCachingHochdruckEichtheorieServerBinärdatenLesen <Datenverarbeitung>
25:38
Patch <Software>Komplex <Algebra>SoftwareentwicklungGlobale OptimierungDifferenteGeradeDatenverwaltungElektronische PublikationKontrollstrukturFaserbündelQuellcodeBinärdatenInstallation <Informatik>LastStatistikDifferenz <Mathematik>VersionsverwaltungStichprobenumfangBootenEinfache GenauigkeitGüte der AnpassungZahlenbereichGruppenoperationBinärcodeComputervirusFunktion <Mathematik>Mini-DiscKlasse <Mathematik>MereologieEin-AusgabeSampler <Musikinstrument>MultiplikationsoperatorCodeCachingTranslation <Mathematik>BitKreisdiagrammNormalvektorSpeicherbereinigungWort <Informatik>Pi <Zahl>Prozess <Informatik>Kartesische KoordinatenInformationByte-CodeSystemaufrufPunktSoftwarewartungDialektMathematikCASE <Informatik>Overhead <Kommunikationstechnik>ProgrammiergerätPhysikalisches SystemMaßerweiterungZeichenvorratHochdruckPaarvergleichRechter WinkelBenchmarkZentrische StreckungMessage-PassingSkriptspracheMAPVorlesung/Konferenz
34:08
InstantiierungKlasse <Mathematik>MultiplikationsoperatorGlobale OptimierungZahlenbereichTypentheorieLaufzeitfehlerKartesische KoordinatenZählenSubstitutionCodeSoftwaretestSystemaufrufWeb SiteObjekt <Kategorie>Rechter WinkelValiditätMultiplikationCASE <Informatik>DateiverwaltungCachingInformatikHilfesystemModifikation <Mathematik>SchlüsselverwaltungComputerunterstützte ÜbersetzungInhalt <Mathematik>TabelleNP-hartes ProblemStreaming <Kommunikationstechnik>BitPatch <Software>Figurierte ZahlNatürliche ZahlBootenPunktRechenschieberBenchmarkMathematische LogikProzess <Informatik>Quick-SortLastSchlussregelElektronische PublikationCluster <Rechnernetz>ZweiTopologieGraphInformationsspeicherungMedianwertGemeinsamer SpeicherArithmetisches MittelURLKomplex <Algebra>EinsHalbleiterspeicherVirtuelle MaschineEreignishorizontProgrammbibliothekInformationMusterspracheDifferenteMeta-TagCompilerHook <Programmierung>VersionsverwaltungComputervirus
42:37
Web-SeiteBitObjekt <Kategorie>HalbleiterspeicherPhysikalisches Systemp-BlockDefaultProzess <Informatik>VererbungshierarchieNetzbetriebssystemSpeicherbereinigungSpeicheradresseRechter WinkelDiagrammURLFreewarePunktMultiplikationsoperatorSpeicherverwaltungCoprozessorService providerKomplex <Algebra>RoutingMereologieSkalarproduktGreen-FunktionGemeinsamer SpeicherMetropolitan area networkKlasse <Mathematik>Patch <Software>WasserdampftafelGüte der AnpassungLaufzeitfehlerGlobale OptimierungCoxeter-GruppeStrategisches SpielMehrrechnersystemKonstanteEinsCASE <Informatik>ZweiEinflussgrößeServerBenutzerbeteiligungProdukt <Mathematik>BetriebsmittelverwaltungGarbentheorieFlächeninhaltCodeKopula <Mathematik>MultiplikationSchreiben <Datenverarbeitung>VideokonferenzComputeranimation
51:06
Metropolitan area networkComputeranimation
Transkript: Englisch(automatisch erzeugt)
00:03
Our first morning keynote this morning, and that is Aaron Patterson. Come up, Tinder love.
00:26
Oh, one sec, I got to do this real quick. All right, for posterity, thank you, thank you.
00:41
No, I'll give you a hug, but only in person. OK, OK, OK, time for me to turn into a nervous wreck.
01:03
Should I start dropping f-bombs now? Is this it? Ah, my race car. OK, go, all right.
01:28
So Ted Cruz and John Kasich both dropped out, so we have to come to grips with reality now, which is to make rails great again.
01:40
Brad said he wouldn't make any PHP jokes, but I will. OK, all right, hello, hello, hello, my name is Aaron Patterson. I'm going to introduce myself, because I notice many people here are new, so in case you don't know who I am, my name is Aaron Patterson. I go by Tenderlove on the internet. That is my name everywhere, mostly.
02:04
And maybe you've seen this name or my avatar. I don't look like my avatar, that is me. That's actually me, it's really me, I promise. Anyway, I'm on the Ruby core team and the Rails core team. I'm also on the Ruby security team and the Rails security team,
02:23
so that's fun. I thought I'd try this out for all of these security nerds here. This is my PGP fingerprint, I'm not going to read it to you. I am the number two Rails committer.
02:40
There's the list right there, I'm number two. The reason I'm number two is because I took my commit points and I traded them in for stuffed animals and those plastic soldiers with the plastic parachutes. I love those things. Anyway, I work for a small startup company
03:01
that is in San Francisco called GitHub. I recently started working for this company, so thank you for your patronage using GitHub, I really appreciate it. The company is very legit. I like to give hugs on Friday, I'm not going to do that here
03:32
with so many people, but if you come up to me later, I will give you a Git hug. Anyway, so I started working at GitHub a little over a month
03:43
ago, and I was looking through all the stuff we have and they're like, yeah, we've got slide templates and they're really cool. So I started using the slide templates and they are very cool, they're way too cool for me. So I'm going to switch to something more my style, which is just default slides.
04:02
So just in case, you might not know this, but this is actually a sponsored talk. It is brought to you by one of my co-workers, I have no tea. Please tweet at her because she's paying me for this slot in emoji. And she said she wouldn't pay up unless I proved that it was actually in my slide deck.
04:22
So I want you to tweet at her and say hello. This is also brought to you, this slide deck is brought to you by my cats. This is what, this is, I love this face. It's just, this is the face I make when I'm staring at code. I'm like, ah.
04:42
This is Sea-Tac Airport Facebook YouTube. We call her Choo-Tear for short. That's her resting face. This is Gorbachev, he thinks he is hiding. Here's a better picture of him. It's Gorbachev Puff Puff Thunder Horse.
05:01
And I have a bunch of stickers of my cats, so if you, I also have GitHub stickers too, so if you want to come up and say hello to me, I will give you stickers of my cats and GitHub stickers if you would like them. Also, this talk is brought to you today by my wife. So thank you, wife.
05:22
Thank you. So I want to talk a little bit about my job at GitHub. Or, I like the way I like to say it, Jit-Hub, legit hub.
05:41
Anyway, GitHub, I noticed, so one thing that really, really inspired me is when I was reading these, I was looking through their slide templates and I noticed at the bottom it said, GitHub, how people build software, and I was very inspired by that keynote footer. Anyway, so I actually, I take that statement very seriously
06:02
and that is what I am doing at GitHub, like we develop software, you all develop software, so what my job is at GitHub is basically bringing GitHub application development to everyone and that means taking things like our development style, our anything we use for performance features, security features, anything that is not core
06:22
to GitHub itself, anything that's not core to our product, I am trying to take that and push it upstream into Rails or Ruby, any of those types of things, trying to make them public so that we can all benefit from that and all of us can build better applications. So essentially what my job is to do, Rails core development and also Ruby core development.
06:43
So please, buy our products, help support me. So I want to talk a little bit about my career goals. I've been thinking about my career goals for quite a while now and I'm a 35-year-old software engineer and I don't want to admit that,
07:00
but we're not in San Francisco, so I think it's okay. So when I first started my career, I thought to myself, you know what, I want to get rich. Thought about this, I'm like, I want to get rich, I'm going to retire by the time I'm 30, I won't have to work anymore, and then I got a little bit older and realized that that's going to be very difficult
07:22
and I started thinking, okay, why do I want to get rich? Like, what is the point? Why do I actually want to do this? And the reason, after much introspection, the reason I wanted to get rich is so that I could do what I want to do all day, which is making Rails great again. So I came up with another solution for this.
07:42
There is a different solution. So think about this. If you're completely independently wealthy and you could do whatever you wanted to do all day and what you wanted to do was make Rails great again, what would the difference between just drawing a salary on that, like being a cog in the machine,
08:00
versus being totally rich? So I, what I want to do with my career is I want to become a cog. I want to be the lost cog in the jit hub so that I can work on making Rails. So this is actually my career goal, is to become a cog. Unfortunately, I'm giving a keynote so you can tell I'm failing at this job.
08:25
My career is not going so well. Anyway, let's talk a little bit about what's new in Rails 5, all right? So new in Rails 5. First off, Rails is in its prime. This is the first time that Rails has been in its prime since Rails 3.
08:49
We no longer do XML sit-ups. We do JSON burpies. Thank you, Justin. I want to talk a little bit about threading,
09:02
the new threading features in Rails 5. And you may have noticed, unfortunately, that DHH is not here. We have had a race condition. He could not make it. But I guess that's okay. He's racing in the Le Mans and I heard that that is essentially just NASCAR for Europe.
09:21
So, all right. I know many of you did not laugh at the previous threading joke. And you're thinking to yourself, I came to the opening keynote and I heard that joke there. Well, let me tell you, we have a little bit of Ruby drama today.
09:43
I am extremely angry at Jeremy. I want to take you all back to a time, back to a time of previous plays, back to Friday, April 22nd, 2016 at 7.57 p.m.
10:00
I am going to take you to my chat history. Right there. Now you see, you see my normal shtick is essentially to, you know, DHH gives his keynote and then later on I give mine
10:21
and for the beginning portion of my presentation I basically just give him a hard time. And I thought to myself, this is going to be really difficult this year because, well, I really like Jeremy and I don't want to give him a hard time on stage. So I was trying to figure out what am I going to do with the opening part of my presentation. And then this happens. Even back, so this is, what is this, 2011,
10:43
five years ago he did a double dream hands on stage with me. I got everybody in the Rails core team at the time to do that except for DHH. So I had to give Jeremy a lot of respect, but then he does this to me. All right, so I'm going to give him a little bit more hard time. Five.
11:17
Five dollar foot loss.
11:22
What even is this? Where did he get all these slides? Come on. He's not giving 110%. He's just repeating the same thing over and over again. I feel like he got this from the surplus of slides. I'm just kidding.
11:40
I love you. I love you, Jeremy. You're the best. All right. So I want to talk a little, I want to talk about some major changes in active record. We're going to talk about some major changes. So to talk about this, I want to talk about SQL. I want to talk about SQL so I have prepared a statement.
12:00
Please notice my advertisement slots. All right, actually we got a new thing called application record. If you generate a new application today, you'll see that we have this new class. It's application record in your new application. It basically inherits from active record base, and you can put methods in that that you want to have in all of your models.
12:23
The other thing that we have started doing is I want to appeal to startups. I heard that, I heard in Justin's talk that we're giving way to Node.js, so that worries me, so I want to start appealing to startups, people who want to start their businesses on Rails. So what we've done is, if you take a look
12:41
at the new application generation, so here I am, I know you can't read it, it's fine. I'm generating a new application. I've named this application OMGLOL what 2, and that is because I have multiple OMGLOL what applications on my machine. And you'll see here there's a new file, which you can't read, but it's okay,
13:01
because I'm going to zoom in here, and if you look at it, under the models directory, we have a business.rb, and this comes with all new Rails applications, and if you open this up, it's actually a business model for you. So this is how you can make tons of money. So we generate a new business model, and this is actually a Hacker News compliant business model,
13:22
so there you go. All right, so I love surprises. Surprises are great. I mean, they're never bad, right? When you get surprised, it's always, always, always a good thing. So you go home and there's a surprise party for you, you're super happy. They never turn out to be terrible things.
13:41
And last year we had some surprises, like we had a surprise action cable announcement. It was a brand new surprising thing to even me. So I want to take the opportunity this year to also announce a surprise. This is going to be in Rails 5. And that is PHP template support.
14:00
And I figure with this, so we're appealing to startups, and I think, well, maybe if we combine forces with the PHP community, we could take out Node.js. So what I've done today, I want to give you an example here. This is, if you look under app views, we have index.php, and we print out phpinfo, and if I run the Rails server,
14:23
and then access it, you'll see, okay, here's our nice Rails 5 intro screen, and then we go to the users page, and there's our phpinfo. I want you to look around you. What do you see?
14:41
Rails 5, running php5. So this isn't enough. You're like, oh, whatever. He probably just shelled out to php and is printing out the thing. This is no big deal. Well, let's go a little step further. So here's our phpversion thingy. I'm gonna change this to access some variable, hello world.
15:01
You'll see that at hello world. And if we go into the controller, go on here and work your vim. You can do it. You'll see we have an instance variable named hello world with the time now, and we print that out from php. And then hopefully I go over here and reload the page, and you'll see the actual time that was made in Ruby
15:22
and goes into your php template. This is not a joke. But we need to go further. This isn't enough. You're thinking, oh, he's probably just printing that out. It's just a string. Who cares about that?
15:40
He's printing that out and sending it, you know, shelling out to php, and it's doing some magic trick. No, no, no, no, no, no, no, no. So what we're gonna do is we're gonna access our active record models here. I'm gonna go in and access users.all, get rid of that instance variable, and then to my horrible, horrible php, you can see here this is a traditional php template.
16:02
This is what you'll find in most php files, including my terribly broken HTML. Look at that. Look at that indentation. Good job, Aaron. So we reload it, and of course there's nothing there because we didn't put anything in our database, so I'll go over to the users new and add some users.
16:21
I think I sped this up a little. I'm just adding users. It's normal, you know, normal stuff, so add some users. And then we go back to the index, and there they are with my horrible HTML. And then I think I fixed it. I don't know why I needed to show you this. I was like, I can't get my lists right.
16:51
Add the traditional indentation. There it is. Look at that. Wow. So yes.
17:00
php5 in your Rails 5. You'll have php templates. I don't think I mentioned this at the beginning of my talk, but some of the things I am saying to you might be a lie. It is up to you to decide which is true and which is not. All right, so let's get down to business. The actual title of my talk,
17:22
we're done with the fun part of the presentation. Let's get down to business. So I typically do the reverse mullet style presentation where we have fun in the front and then business in the back. So let's get down to business. The title of my talk is What Have You Done For Me Lately? And I know you're thinking to you,
17:41
I have done a lot of stuff for you lately. I really have, but I know that you're all thinking to yourself, yes, but what have you done for me lately? So that's the real title of my talk. What have you done for me lately, really lately? So today I want to talk about performance. Surprise, performance, I'm talking about performance. I love performance, I love fast code,
18:02
and the reason I love it is because it's part of my plan to become a cog. Essentially, if all of your code gets faster, you feel happy, right? It's like I am Patrick Swayze from Ghost coming over and helping you do programming, and you're like, everything's faster, it feels good, I'm not sure what's going on.
18:20
You look over your shoulder and nobody's there. Right, that's me, I want to be that cog. So we're going to talk about boot time performance, run time performance, and memory efficiency. And when I do performance work, I always have to think that performance is, it's always about trade-offs.
18:41
When you're doing performance work, it's always about trade-offs. You're trading, you're always trading something. Whether it's memory for speed, or anyone who caches something understands this. You want to have a faster page, you cache it. Cache the page, but that cache had to go somewhere. So you're paying some sort of price for that speed. Or maybe concurrency for memory.
19:00
This one's an interesting one that isn't so common. You give up concurrency so that you can save memory. Or complexity for memory, like maybe you want to reduce the amount of memory that you're using, so you increase the complexity of your code. And I'll look at some of that later. But the point here is that performance is never free. It's never free, you can't just have free performance.
19:23
So it's very important for you, when doing performance work, to understand the constraints that you're working under. How fast does the code have to be? How much memory should it consume? All these particular constraints, you need to figure out those before you do any performance work. So I'm gonna talk about boot time performance. And I think this is interesting,
19:41
because you're reading my advertisements. I think it's interesting because it impacts running tests, it impacts server deployment, restarting a production. I like restarting stuff, that is happy for me.
20:00
I'm not a real Spring user. So boot time performance is important for me. And when I look at that, basically I think about boot time performance from the very beginning. When I do bundle exec rails, like this is it, I look at this whole thing. And I think about the different parts that are involved here. So if you look at each of these, each part of this command,
20:20
the very beginning we have the bundle command. That bundle command is actually a bin stub. This bin stub is installed and controlled by RubyGems. So if you care about the speed of just that bundle part, you need to look at RubyGems. If you care about the speed of the exec part, the exec part of this command, you probably have to look at bundler. This is, you know, if you care about the speed of that exec part,
20:41
that is where you need to look. With the rails command, you need to look at rails. And also with the S command, you need to look at rails as well. Now, if you think about the boot process, and we extend those lines out a little bit, it's essentially a timeline of what your code is, what your code is doing at any particular point in time. So we can extend that out. And we know that as soon as we hit enter, essentially we're gonna be spending our time
21:02
in RubyGems, then bundler, then rails. That's where, that's the timeline of our boot process. And a lot of people say, okay, well, you know, speeding up Ruby speeds up everything. Why don't you just speed up Ruby rather than speeding up, you know, speeding up those particular things. And I do like working on speeding up Ruby. I think that's a fun task. But just because speeding up Ruby speeds up everything,
21:23
that doesn't mean we need to write slow code. We can write faster code and have speed today. So I'm gonna talk about two optimizations at the very beginning of the boot process. I'm gonna talk about Ruby, sort of, and I'm gonna talk about RubyGems as well. So the other day I was running an empty program. I do Ruby-v, and I do a blank string,
21:42
and I see that it takes 100 milliseconds, and I think to myself, OMG, Ruby is slow. And anybody that runs Ruby and this program and sees that it takes 100 milliseconds, they say it's slow. I can't blame them for saying that. Like, everybody should think this is slow. But if I disable RubyGems and I run it again,
22:01
I see that it takes maybe 50 milliseconds. Now, I wanna make two points of this slide. First is that measuring the amount of time that RubyGems takes is, at the very beginning of the boot process, is a fairly difficult thing. So the way I do this is measurement by elimination. Essentially, what I do is say, okay, well, let's just look at the boot time of Ruby
22:22
and then remove RubyGems and look at the difference between those two, and that's how we can kind of gauge how much time we're spending in RubyGems. So we know that we have to optimize in that particular place. The other point that I wanna make is that placing blame is difficult. When you're running that Ruby-e blank string, how do you know that it's actually RubyGems' fault?
22:45
I would not blame anybody for looking at that and saying, wow, Ruby is slow, right? Even though you don't know under the hood, half that time is spent in RubyGems. Not actually Ruby's fault. So why is it slow? Let's look at, the reason it's slow is this file,
23:02
gemprelude, this is a file that is in Ruby's source code itself. If you go look at Ruby's source code, you'll find a file called gemprelude.rb. When you install Ruby, you won't see this file anywhere. It's only part of the source distribution. So if we look at the inside of this file, this file gets loaded every time you start up Ruby.
23:23
So the first thing it does is it loads RubyGems right here. So why are we loading RubyGems? Why does this thing load RubyGems at the very beginning of your process? The reason is because back in the bad old days of 1.8, you had to require RubyGems before everything. Today, Ruby 1.9+, you don't have to do that anymore.
23:41
It's built in. Ruby does this for us, and that's what the gemprelude provides to us. So we load RubyGems, and then you'll see here, this line loads the didyoumean gem. This is new in Ruby 2.3. So what does the didyoumean gem do? It gives you, if you make any typos, it tries to suggest to you what it should be.
24:01
So here I've made a typo. I wrote object IP, and then it says to me, didyoumean object ID? So it tries to suggest the correct method for you. This is essentially the clippy for Ruby. So didyoumean is a gem. Didyoumean is a gem, and it is shipped with Ruby as a gem,
24:22
which means that RubyGems has to load it. RubyGems is responsible for doing that, so right here, didyoumean, RubyGems require, loads the didyoumean gem. So what exactly does the require method do? This is essentially a TLDR of the require method. You don't need to read it super closely, but essentially what RubyGems require method does
24:40
is it iterates over every gem on your system, and it says, hey gem, do you have this file? If you have this file, I will activate you and then try requiring the file. So that's essentially this loop right here. That loop is o3n or just o-n, and what this means is it's actually testing for three particular files. It's saying, hey, do you have the file that they pass to require?
25:01
Do you have the file with rb extension or the file with so extension? So what this means is that the more gems you have installed, the slower require gets, and you'll notice that we always do require at the beginning of the Ruby boot process, so what this means is that the more gems you have installed, the slower Ruby gets,
25:20
and we can actually see this in action. So this is a DTrace script to watch all the stat calls for a particular process, and what this command is doing is running its root. I have to be root to run it, dash q to make it not print out stuff I don't care about. Here I want to look at all stat calls on my system, and then here I print out the file
25:40
that's being statted, and then down here is the command that I'm actually running. I use rbn for my Ruby management. You'll have to figure out a different command for yours, and then I'm just running the empty program there. So when I run this, we can watch this in action. You'll see, if I run this and just count
26:01
the number of stat calls, with Ruby gems on my system, we see about 298 stats just on boot. Without the DigiMean, if I deactivate the DigiMean gem, we only see 12, and without Ruby gems or the DigiMean gem, we see five file stats, and what's neat is if you look at all these stats, you'll see a printout that looks something like this.
26:21
This is a sample. This isn't the whole 300 of them or so. This is just a sample of them, and what you'll see is that it's trying to go through every single version of every single gem that's on your system, looking for that file. By the way, just a side note, I think this is really interesting. It's statting files that don't have an extension, and Ruby won't actually, if that file exists,
26:42
it won't actually require it. So it's okay. I have some good news. There's good news, everybody, good news. The DigiMean gem starts with a D, and D is pretty early in the alphabet, and these are sorted alphabetically,
27:02
so it'll only go to the Ds. So we've got that going for us. So one good way, one good way that I will propose that we speed up the Rails boot process is that we rename Rails to AAA Rails.
27:29
Reminds me of phone books. All right, so let's make an improvement. We can improve this. We can actually speed this up by using the gem command. There's a gem method.
27:40
So that top program is just a normal require, requiring the DigiMean gem. The next one is a different program that does gem DigiMean and then requires DigiMean, and if we compare the stats on those, you'll see a bare require took about 300 stat calls, where doing gem plus require only took 16. So why is this faster?
28:00
The reason it's faster is because if you look at that gem thing, which I know you can't read, it's fine, the gem command actually mutates the load path, and the first thing that require does is ask Ruby, hey, can you require this file? Is this file in the load path? And if it is in the load path, it'll just require it and continue on with our lives. So we have an 01 look up of the gem spec.
28:24
We know the name of the gem spec. We just look it up, and then we have an 01 require the file. It's not actually 01, it's just very small. So to fix this, we just patch the gem prelude, and this is the patch I applied to the gem prelude. Just two line diff here. We went from a bare require of O N to a gem plus require of O one.
28:42
So in this particular case, we were trading off complexity for speed, okay? We're adding more complexity to the code, but we're gaining speed out of this. Now, complexity does have overhead, and even this patch has overhead. You might be thinking, hey, that's only one line. It's one line. It doesn't matter.
29:00
Like, yeah, we'll add that. There's no cost to that. But if you look at these commands, you'll see, I wanna go back to this command. There is an optimization in RubyGems. When you do gem install bundler, and it installs the bundler bin file, RubyGems knows that, hey, when you run the bundler bin file, we probably want to activate the bundler gem.
29:22
It came from the bundler gem. We know that it came from the bundler gem, so let's activate the bundler gem as soon as you call bundle. And you could see this. And before RubyGems two five two, if you looked at those bin stubs, you would see right here, we have those two lines, gem bundler and then do the load. This is for all bin stubs.
29:42
I'm only picking on bundler because I just use it all the time, and it's great. Anyway, so we have an O one require here. This is what it is. And then if you look in RubyGems two five two to two six two, that gem call's actually gone. So we've gone to an O N time here,
30:01
which means that all of your bin stubs now get slower as you install more gems. So the point here is that even though that was a one line change, even RubyGems maintainers can miss this too. So you do have to think about these trade-offs when you're looking at code complexity. All right, so let's move on.
30:22
We did RubyGems, and now I want to talk about Rails. I don't want to talk about Ruby and Ruby so much. Let's talk about booting up Rails. And I'm excited to talk about this because of new technology that is not actually new because I went to Koichi's talk yesterday and he just told you all of this.
30:43
So if we take a look at startup times, it'll break down something like this. These are not actual times. You have a pie chart here. These numbers are all, oh, should I swear on stage? These numbers are all bullshit.
31:01
I said a swear word. All right, so these numbers, these are just made up. What I'm trying to say here is that these are not actual times. These are just things that you do as you're booting your Rails process, and they take some amount of time, and we don't know what those times are. We don't know what they are. So how can we measure them? One way I do is I use gc.stat
31:21
so I can get information about the GC, or sometimes I'll just disable the garbage collector and compare the benchmarks and see what happens. So we can eliminate GC from the boot time process. We can measure its impact just by doing these simple things. So we can cut that out, and if we cut that out, we see it doesn't really impact boot time so much.
31:43
So we can eliminate that. We have another thing in here, another thing in our pie which is searching, and that's searching the load path for files, and at GitHub we have code for caching these lookups, so we don't actually do load path searches when we boot our Rails application,
32:00
but you can also do this as well. If you get this gem called boot scale, you can also cache these lookups so you don't have to do load path searches on your boot time for your application. So we can eliminate that from the pie as well, and like I said, we eliminated GC earlier,
32:21
so basically all we're really left with is compilation and execution. So the amount of time it takes to compile your Ruby code and then whatever execution we're doing at boot time. So let's tackle compilation, and to learn about this, I want you all to go to Koichi's talk, which was yesterday,
32:41
so I'm gonna talk a little bit about it now. Your normal program flow looks a little bit something like this. We take source code, we compile that source code into byte code, and then we execute the byte code. So what the idea is is we wanna take that beginning part, that source code to byte code translation and cache that in a file somewhere so that rather than doing this translation at boot time,
33:02
instead we'll do something like this where we read the byte code from a file and just evaluate that byte code so we don't actually have to do the compilation step anymore. We can cut that out of the boot process. So here's an example of a compilation script. Some of these methods are new in Ruby 2.3, so all this does is take an input file
33:21
and compile it to an output file, and we can run it, and you'll see when you run this, compile hello.rb, you'll see there's just some binary data in there. So all right, our first half is done. We've completed our first half. The next part is we need to be able to load and execute this byte code, so this is an example of loading it. You'll see we load that byte code,
33:41
evaluate the byte code, and then we assume that the hello class exists, and we knew that it existed in that byte code, so this works. We know that hello is available. If we run it, it works, so we're able to load the byte code. We're able to write it to the disk and load it back in, so the second half is done. We are 100% done, but Rails isn't faster, and this is because I always give 110%,
34:03
so let's integrate with require. We need to integrate with require, and this is something I don't really want to do, but we have to do it to get over this 10% process, so let's look at the require process. We have three different files, A, B, and C,
34:21
and they depend on each other, and if we look at the dependency graph, it looks something like this. We have A, which requires B. B requires C. C goes back to B, so we have this kind of weird dependency tree, and if you look at the actual logic of the require process, essentially B will start to require B, will acquire a lock for B, will start to acquire a lock for C,
34:42
try to use B. B is already locked, so we can't use it. We finish requiring C, release a lock for C, release a lock for B, et cetera, et cetera. So this sucks. The point here I'm making with this slide is that there are too many rules, and I don't want to figure them out, so essentially what I did is patched Ruby
35:02
such that it would call a callback when it needed to look up a file, and we could just have it do whatever we wanted to at that point, so we essentially had a lookup or a require hook here that would say, okay, when we want to load foo, instead of looking through the file system for it, it would actually hit this method,
35:20
and we could do whatever we wanted to with it, and you can go look at that on my fork of Ruby at GitHub, yes. Gotta get that money. So we have an example here that loads compiled code. This is a lazy compiler. GitHub. So if we compare the two,
35:41
if we boot Rails with and without precompiled code, we see this example. So before it took about 1.8 seconds, after it took 1,200, so we're about 30% faster. So I'm actually really excited about 30% faster. Koichi said in his talk, oh, it's not that much faster.
36:00
It's only 30% faster, and I'm like, no, 30%. That's 30% better than what we have today. We should do this, but what I think is really cool about this is the more code that we have to load, the bigger impact that this will make, and on our application at work, we have a lot of code, so this will help us out a lot. So for future work,
36:21
I'd like to upstream this new callback if I can someday. I think it would be interesting if we compiled code on gem install, so we could just have that available as soon as we install a gem, and the other hard problem of computer science cache invalidation, so I'm not sure about that. Need to figure that out. So next we'll look at runtime performance.
36:41
I talked a bit about boot time performance. Now I want to look at runtime performance. I wrote a patch a while ago for doing polymorphic inline caching, and I want to talk a little bit about this. So what is inline caching? There's a cache in your code, and you don't really see it. This cache lives anywhere there's a dot, so that cache is right there,
37:01
and it says, hey, this object is of type hello. Where is the foo method? You have to look up the foo method when this code is executing, and you don't see this cache because this cache is inline, hence an inline cache. Yes. So basically this cache, as soon as that code executes, the cache contents will look something like this table
37:21
where we have a key which is our hello, the class, and the method name, and then the value of that cache will be where that method is located. So the second time this executes, the VM says, hey, oh, I know this is hello, and I know it's for method foo. The cache hits, I can just go look at that, go directly to that method.
37:41
So, our next thing, inline caching, if we have two types here, it sees that first type hello, we get a miss, but it populates the cache. The second variable that comes through is for type world, and that's different. We get a cache miss there. The key is wrong, so it misses.
38:00
And what this means is in MRI, our cache size is one. We can only cache one value, and we call this a monomorphic inline cache. It's one value, one type, stored in this cache. So what if we had a cache size of two? If we had a cache size of two, then we could say, okay, we missed on the first one, we missed on hello, but that gets put into the cache.
38:21
We missed on world, but that gets put into the cache as well. But the subsequent hello hits, and the subsequent world hits as well. So a cache size of two or more, we can call that a polymorphic inline cache. It's caching multiple types. This particular optimization pays off when these call sites see multiple types.
38:40
If you see many, many different classes at that particular call site, it pays off. So we pay off when we see multiple types, and you can go see, I have this polymorphic inline cache implemented, again, on GitHub. Go there. So you can check it out and try it out yourself. But unfortunately, the TLDR is it didn't help us.
39:02
We ran it on our application, and it didn't help our application at all. And the reason is because if you count the number of types that you see in the application, this is the test application that I use, if we counted the number of types, only 3% of the call sites had two or more types,
39:22
which meant that we would only see maybe possibly a 3% performance improvement. So this particular optimization is a trade-off of complexity and memory for speed. We're using more memory, we're adding more complexity, and we're gaining speed with this. But unfortunately, that speed is only 3%, so it's probably not worth it.
39:41
So this patch, for our application, this patch was not worth it, so this probably won't get upstream. Interesting thing we found, though, is that we found a call site that had 1600 types, 16 different types of objects passed through one method. And I thought that was very interesting. And where that actually came from is someone calling instance eval on an object.
40:00
Whenever you call instance eval on something, it creates an anonymous subclass of hello, or anonymous subclass of whatever it is, and it attaches that. You probably know this as the meta class. Now, that means that in this particular case, this foo method right here at object.bar always sees an anonymous class, which means this is always a cache miss. So when I was counting these in our application,
40:21
that's what I was seeing, is anonymous classes coming through, 1600 anonymous classes. So it's an interesting thing, an interesting question to ask. What class is this? When you look at var, what class is var? And you think, oh, well, it's an instance of hello. But I actually like to think of this more as an instance of an anonymous subclass of hello,
40:43
and that anonymous subclass is lazily populated. It's also known as the meta class. And this is actually okay because of the Liskov substitution principle. It's fine. It's a VM optimization that we go around pretending that this is hello most of the time. So when does this singleton class get instantiated?
41:01
Any time we call instance eval or singleton class or def add a singleton method to something, and there's other cases where this happens, but these are like the main ones that I saw. Now I know you're wondering, given I'm a Rails dev presented with this info, then why should I care?
41:22
Well, the reason you should care is that some libraries do this, and unfortunately, they are popular libraries like RSpec or Event Machine. So the solution is don't write code like that. Just don't do that, please. And if you really, really think you have to, don't.
41:43
Seriously, just don't. But if you really, really, really, really must do that, we can speed it up. And what I found interesting in RSpec is that these singleton classes, these singleton classes are classes with no new methods added to them. They didn't add any methods to the singleton classes.
42:01
They just used the singleton class as a storage location. So a singleton class with no methods is exactly the same as its superclass, meaning that those two things can share a cache key. So what I did is I added a patch to Ruby that would, well, I haven't upstreamed this, but I made a patch for Ruby that would actually share those cache keys,
42:21
and that's what it looks like. Again, GitHub. And here's a benchmark for it. This is a benchmark comparing essentially polymorphic call sites between the singleton class and the non-singleton class version. And if we look at it, the original speed is, it took about 2.5 seconds to run this benchmark.
42:41
After sharing those cache keys, we see it go down to 1.3 seconds. So it's about 45% faster. In this case, we're trading complexity for speed. So in this case, it's probably worthwhile. That's a good performance improvement. We saw a good speed improvement. The patch is relatively small and innocuous.
43:01
Another thing I thought was interesting about doing this work is that I felt really, really bad about it because I subscribed to the just don't do this school of performance improvement, so I tried to refactor RSpec to not use singleton classes anymore, and it turned out it was actually easier for me
43:20
to just optimize Ruby. So I don't mean that as a dig at RSpec. RSpec's great. I'm just saying that code, man. All right, so memory efficiency. We've done boot time, we've done run time,
43:42
let's do memory now. I want to talk about copy on write, AKA cow. We use copy on write a lot. You probably use it. So first, I want to talk about impacts to copy on write, and I want to talk about heap layout optimizations. So these are the two topics we're gonna discuss here.
44:01
Copy on write optimizations, essentially what that is is when you have a parent process, and that parent process points at some memory, that parent process is gonna fork a bunch of children, and those children don't get a copy of that memory. They actually just point at the parent process of memory, so they share that memory. When you have 10 processes, it doesn't use 10 times the memory.
44:20
It maybe uses only that parent process of memory. They all point at that one particular bit. Now, if any of these children writes to that bit of memory, the parent process shouldn't see that write happen, so what the operating system does is it actually copies that memory down to the child process. So that's why we call it copy on write. When you write to a particular memory location,
44:41
the operating system copies that for us. So that copy, when that copy occurs, that's what's called a page fault. So there's a page fault that occur, a copy on write page fault, that page gets copied on those faults. So the operating system copies some of the memory. It copy, it doesn't copy that entire block like my diagram showed.
45:01
It actually only copies the bits. It copies in a location where you wrote, but it copies in page size blocks, operating system page size blocks. So if we look at Ruby's memory layout, Ruby's memory layout looks a bit like this, where Ruby will allocate a page, and then all of our Ruby objects are allocated inside that page.
45:22
So as we allocate Ruby objects, those Ruby objects will fill out those pages, and if we need a new page, Ruby will allocate one whole new page, and then we start allocating objects inside of that. Now, when GC happens, if a page is free, the garbage collector will free up any objects,
45:40
and we have these objects that get freed in here. And if you look at this, it looks a little bit, you can think of this as a little bit like Swiss cheese, it has holes in it. So there's these little slots that have free objects. And if any pages are free, the garbage collector will actually remove that page. Now, what would be cool is if these two objects here,
46:00
they moved over to that other page, then we would have a free page here, and we could free that page as well. That would be really nice. Unfortunately, in MRI's garbage collector, objects don't move today, they don't move. So we end up with a heap that looks something like this, where we have a bunch of free slots. These are Swiss cheese, I like to call it Swiss cheese, because it's just got a bunch of holes in that.
46:21
Now, this actually wastes memory in two ways. The first way is, we have a copy-on-write wastefulness. So the parent is pointing at these particular pages, then we fork, the child is also pointing at this same memory layout. Now, the child allocates an object, and it goes there, and we get a page fault, and the operating system writes
46:43
an operating system size page to the child process. But that operating system size page is larger than a Ruby object, which means that we're actually gonna copy multiple things from the parent process to the child process. So, on Linux and OS 10, the default page size, the operating system page size is 4K.
47:02
A Ruby page size, that block I was showing you, is 16K, and a Ruby object is about 40 bytes. So one Ruby page is about 400 objects, one Ruby page is four OS pages, and one OS page is about 100 Ruby objects. So if there's any free slots inside of any
47:22
of that 100 object area, we're gonna actually copy those to the child. So the answer is, we're probably copying too many objects. So, this only impacts Ruby code that forks. So what forks? Unicorn forks, we use unicorn in production. Probably many of you use this.
47:41
Most MRI web servers fork. So, what I was thinking is, this section of my presentation has no code, unfortunately. I've just been thinking about it, because it's fun to think about. If we look at a page sideways, allocation moves something like this, where we're going left to right, allocating new objects, right? We're filling up that page.
48:02
What would be interesting is, if we could predict which objects were going to be old, what if we knew that these particular objects were probably gonna be old, and these other ones, we don't know about? Maybe they'll be old, maybe they'll be new, we don't know. But there's some objects we might know are old. So if we know that those are old, why don't we start maybe on one side of the page
48:20
with the known old objects, and the new side of the page with, the other side of the page with possibly new objects. That way we would end up with a page where one side of it looks like Swiss cheese, and the other side looks more like Gouda. So we have a more solid page, and those probably old objects won't get copied to the child process. Or maybe we could even have pages
48:40
that are strictly dedicated to things that are probably old objects. So you might be thinking, what are probably old objects? And there's actually a lot of spots that we can see these. For example, when you create a new class, you know that class is probably not gonna get garbage collected. In Ruby, our classes are objects. That class probably isn't going anywhere. Neither is the module, neither is the constant.
49:01
There's other things too that we can say, like maybe these things are probably going to get old. So, trade-offs. What are the trade-offs for this? Unfortunately, I don't know yet, because I haven't implemented it, nobody's implemented it yet. My current strategy is basically wait for Koichi to do it, and then go to his presentation,
49:23
and learn that he did it. So maybe it's complexity for memory. How complex is it? I don't know. How much memory is it gonna take? We need more tools for introspection, I think. So I built a tool that I thought was really fun. I call it heap frag, this is it. And what it does is it shows you a layout, the fragmentation and layout of your memory.
49:42
Is the video starting? Okay, yeah, so on the left side there, you see the layout of the memory. The black parts are free slots. Those are where we can allocate objects. The red dots, those are old objects. And green dots are new objects. So you'll see as I'm allocating objects in IRB, it's filling out that page.
50:01
And you can see as soon as it does a garbage collection, that it's actually wiping those objects away. So I'm gonna disable the garbage collector and allocate a bunch of stuff. And we can actually see the heap expand, so it gets wider and wider. And then when we garbage collect those, they all go away. Go, Aaron, yay!
50:22
We GC a bunch of times. All right, I'm almost out of time, so I'm gonna wrap it up. And I wanna wrap it up with five lessons. What do I value? What should I measure? How can I measure it? And does it provide shareholder value?
50:45
Five lessons, let me repeat those. What do I value? What should I measure? How can I measure it? And does it provide shareholder value? All right, everybody, let's make Rails great again. Thank you so much.