 So, Stefan Penner, there isn't a hashtag of my name, that's just my IRC. As you may have noticed, I love open-source software. I believe in continuous reinvestment, like our previous talk. I'm also a husband of a very patient wife, and she's actually here, so if everyone give her a round of applause, we have birds, and they like to pair program. One of them matches my color scheme. We did, in fact, choose the bird based on my color scheme. We have a new feathery friend. Unfortunately, when I tried to pair with him, turns out he can like one-shot headphones and power adapters, so he can't pair with me. But he's lots of fun. He actually helped us navigate up to Portland. So, it turns out I accidentally washed my passport, can't fly here, luckily I'm on this coast, so driving up from San Jose was actually quite fun. So, announcement, I now work for Yahoo. They believe heavily in open-source, and a large part of the Yahoo team runs some pretty massive EMBRA applications. So this is pretty exciting for me. So at first, I was kind of apprehensive. Yahoo, I think my first email address 17 years ago was at Yahoo, and I was pretty apprehensive. But being there, everyone is smart, that I interact with, is smart, motivated, and excited, and it's really cool to see. So what am I doing at Yahoo? I'm fixing EMBRA CLI issues. This is awesome. I think my wife appreciates this, that I have a little bit more time to spend with her. And this is loads of fun. So Yahoo is hiring. If anyone wants to come join me and have some fun, we are riding the Canary Wave on as many applications as we can. Talk to me, or talk to one of the other Yahoo's. And let's see if we can work something out. Anyway, so I love discussion. Feel free to ask me questions. But we went a little bit over before. So if I cut you off, that just means bug me afterwards. So there's a warning. Browser internals are like French to me. Can understand a few things. I can say a few things. I can ask where the washroom is. Esque je peux aller aux toilettes. It is very useful. But I might be wrong. So this talk, we're going to talk about some performance. Myth, mechanical sympathy, and then Ember's misalignments. So we all know JavaScript performance is all about how you enumerate arrays. And just in case you're curious, those are all different ways of enumerating arrays. That's it. Talk's done. Turns out, Tom and Yehuda fixed all the problems. All the HTML bar guys fixed all the problems. We're done. Thank you. So although we're neck and neck with React, I think we can do a little bit better. So up until somewhat recently, this is what JavaScript performance looked like to me. I think to lots of you as well. Until I started getting involved with promises and there was lots of complaints about how slow they were, but they provide this fantastic mechanism by which we can abstract some complexity. Promises were really slow. There was actually a blog post by Spion who enumerated these problems. And then all of a sudden, there was another blog post by Spon saying, I'm switching to promises. And basically it was because of this library called Bluebird, promise implementation. And Bluebird's author, Petka Antonov, basically without changing the external API, made promises at least an order of magnitude faster. So at this point, I'm like, okay. I had done work on RSVP and I was like, how on earth has he accomplished this? So I expected when I opened the Bluebird box to find 12 weird tricks to double your JavaScript performance. When I opened the box after some investigation, what would I say next? Might shock you. What if I told you promises using normal, get this, normal for loops were actually fast. I love this picture. I've been like carrying it from talk to talk. It's fantastic. So basically the TLDR of why Bluebird was faster is it did less work. You want something to go faster? Make it do less work. The question often is, what is the work? So in the Bluebird case, it was two things. Allocate less objects and then underlie, align with the underlying primitive. So figure out what JavaScript runtimes are doing and try to not fight them but work with them. And now promises are fast. But the most important part is this was done without an external API change. A wild ember appears. Turns out we care about making things faster without breaking externalize or minimizing external API breakages. So this is very appealing. Stability without stagnation. And boom, you guys saw this pretty awesome demo which I was a little bit nervous about since I'm giving a performance talk after that. But I had early access and I spun up the profiler and I saw these things. I said, ah, this is something React doesn't have. We can do better. So even with this item poten re-render branch, the ceiling I believe is still pretty high. We have a lot of fun work left to do and we can get way more performance out than we have even with that branch. How much faster can we go? Who knows? So there's another part and that is when we're developing we actually want to be able to develop faster. We just don't want to deal with esoteric ways of making things faster. We also want to be productive. And it turns out the same ways we confuse ourselves is the same ways we confuse the runtime. And often realizing what the runtime is doing under the hood might actually encourage good programming patterns. So it turns out by reducing the confusion we can actually make Ember even faster than it is today. So who here played Pokemon? Who here was able to clone rare candies? Well, for those that don't know what that is there was a glitch. And it allowed you to clone items in your inventory. So there's this thing called rare candy which would allow you to level up a Pokemon. But you could use this glitch to get many rare candies and level up your Pokemon really quickly. Unfortunately, as you leveled up your Pokemons if you allowed them to evolve too early you would miss out on some of their important skills. So as we work on Ember we have to be sure that we're not jumping the gun and we're not throwing things away that are actually good in favor of something that looks a little bit better but actually might lead us down a wrong path. So we have some important choices to make but it's often not clear which choices we can make. So as Ember evolves how can we make a choice that both is productive for developers and doesn't screw us when it comes to performance? So often I feel this is JavaScript performance. Who here doesn't know what cargo culting is? Well, anyways, South Pacific Islanders around the World War II era recognize that there was a correlation between the arrival of cargo and things that look like runways and planes and people working. They just missed, they weren't quite sure why that was happening actually but so they tried to emulate this and they just couldn't understand why sometimes cargo would arrive and sometimes it wouldn't. Let's be honest, a lot of times in JavaScript performance we make the same choices. We often hear X is slow, don't do X. We fixate on the X and we fixate on not to use it but as we know, anyone that's, yes, X is slow is the wrong thing to say. The more important question is when is X slow? Not just that, when is X used? Relative magnitude matter. Sometimes a very slow thing will actually enable less allocations or less work being done. So the better question is when is X reasonable and when is it not reasonable? But to make this choice, we have to understand what X is without missing the connection. We end up trying something and then we end up with Mr. Aleve's performance calculus. Two patches, 5% improvement, together 10% regression and we have done this countless times in Ember. I am at fault for lots of them. I'm sure everyone who has committed to Ember has. Contribute to this problem and we need to fix this. So there's this cool term that I picked up from Martin Thompson of Elmax. Elmax is a high performance trading platform and they achieved their high performance not by scaling out but by understanding the underlying hardware and the underlying runtime. He stole this idea from a dude called Jackie Stewart who is a three-time F1 champ. Basically the best drivers had the understanding of how the machine worked so that they could work in harmony with it. Rather than working against the runtimes, we can probably work with them, get a little bit more oomph out of them. So by correctly aligning the cars with the driver, the driver with the car, you're able to utilize the car to its full capacity. Turns out F1 cars, V8 engines, same problem. Yes, they have V6s but whatever. And don't worry, although I'm gonna be talking a lot about V8, these common paradigms and patterns really hold true across most of the runtimes. So the world's simplest example, A plus B. I stole this from Petka. I'm sorry but it's a great example. So what V8 does is it looks at that source code and it immediately emits some assembly. This assembly is fast but could be faster but it's extraordinarily safe because we can be adding two integers together, two numbers together, two objects together, we don't know. But the important part here is when it generates this code, it also adds hooks to record the type information for later specialization. Then as code gets hot, the type information is consulted to see, hey, can we specialize further? So basically if a function for A and B is used often and A and B are always 32 bit integers, we can specialize. How does this happen in V8? There's a optimizing compiler called Crankshaft that keeps track of the control flow, portable assembly and then emits native assembly for your particular runtime. How does this actually work? This awesome diagram by Mr. Aleph, source comes in, we compile something that's quick to compile, has these hooks, as stuff gets faster, we consult the optimizing compiler which gives us new native code that is more efficient. Ultimately, after the optimizing compiler is done with it, we end up with ideally something like this which is many times faster, but maybe more brittle. So to review, AST comes in, we first generate something that is safe, not specialized, as it's used, we can generate more optimized code for that case, mad science. So how does this work? Basically, types and stability and mad science. But Steph, I do more than just arithmetic. Well, it turns out there's these concept of shapes in the runtime and they allow us to take these same types of optimizations and apply them to the rest of our code. So if we look at this ES6 class or ES2015 class, it is a shape of, in this case, two slots, a first name slot and a last name slot. Here we have a rect, not a react, that also has two slots, but these two shapes are entirely different, just like an integer is different than a string, same thing. So when we go and instantiate a few of these rects, we have one shape, but we have multiple instances. Great. So remember this thing where if we knew the type information of A and B, we could specialize? Well, if we have an add heights method that takes A and B, rect A and rect B and adds them together, it's awfully similar. So as the dynamic portions are used, as the function gets hot, type information is recorded, and if we're always dealing with the same shapes and those shapes always have the same numbers or values, rather than having to do a very expensive property lookup, we can ideally know the offset of the integer and we end up with hopefully something as close as this in the ideal search area. So what this boils down to is if the runtime believes the code is stable and predictable, it's able to make things faster. As also as it turns out, well-factored code that humans can understand and read often has these characteristics. There's another aspect to performance, and that is the time versus space trade off. So the previous stuff was time-based. Can we make something execute faster? And the second one is space. So space takes up space, but also space takes up time. Allocations take time. Cleanup, the garbage collector in a garbage collected language takes a huge amount of time. And in Ember apps, this is a big place where we are causing lots of problems. Things that cost us in the GC and space world, closures, objects, unoptimized code. So it turns up unoptimized code, allocates more objects, turns out even compile code allocates extra objects, and if we have too many different shapes, the functions have to be specialized too many times, and we end up with compile code actually causing more garbage collector problems than our app data, which is just bonkers. So what we need to do with Ember is we need to do less work, allocate less, and also align us with the underlying primitives. I have an expectation that reasonable code should be reasonably fast. In some places, there's a bug in the runtime that we can get them to fix. In other cases, we are just doing unreasonable things, and we need to fix them. So I have three misalignments that Ember does today that we're going to cover. So the first one is, Ember just does way too much work. The solution is do less work. What work exactly? Well, we saw a great example of this in the earlier talk. HTML bars and idempotent re-render mitigate a whole category of extra work. This is fantastic. The other thing is, as we're moving to Ember 2.0, we are suggesting people use actions up and bindings down. Turns out that explicit data flow is also less work. We don't have all these two-way bindings everywhere, potentially wasteful, potentially not even used, or even worse. Often applications I see that we're relying on like sloshing between two properties that are bound actually happens and no one even noticed it. We're just burning time that we don't even want or don't even need. And another part of this doing too much work is Singleton controllers make this problem even worse. They don't really have an explicit life cycle. When we transition routes, we're causing change events that we don't care about. When I leave the user route and go to, let's say, the settings route, and I'm tearing down information from the user's controller, it doesn't know that we don't care about it anymore. It actually propagates the same change events. This is exceptionally costly. As we move to routable components and explicit state, we will just be able to delete the component. It goes dead and it costs us much less than if we allow it to live and be live bound still. So this basically means as we make Ember clear, our app will get faster, and this is absolutely the case. So the second misalignment is in a knit and super. A knit and super, I believe, is exceptionally hard to learn. Even, I like to think, I know a few things about Ember. And I'm often surprised. Who has tried to subclass an array proxy. Turns out you have to do things in very strange orders, but it's because we're misaligned with how super should actually be working. This causes us to allocate extra shapes, which makes the runtime not be able to specialize correctly. It results in more compile code that we're not going to use and just a big mess. And it's not quite aligned with what the new specifications class and super objects have. So the solution here is to embrace super. So who's written code like this before? Where we have first name and last name defined on the objects. No one's written code like this before? I, everyone in this room that has written Ember app before has written code this way. And it turns out that these shapes here, they come and sneak up and they hurt us here. Because what's actually happening is we create a new person. We create two new persons. We have the person proto, which represents the class on the left. We have the person shape and then two instances. They are of the same shape. But when we go to set full name, it turns out that first name and last name were actually set on the slots of the parent. We set first name and last name. We're actually creating a whole new shape potentially. Sometimes V8 and sometimes run times are smart enough to catch this. But as we have more and more properties, we run the risk of creating multiple different shapes, which just means that code that should actually all be dealing with the same person shape might have to deal with end different combinations and permutations of the person shape, which is not cool. Also, it is not clear to define these properties on the prototype, especially if someone defines an array on the prototype and all instances share them. So the solution to this, I believe, is to change init and to change super so that we actually pass args in, default args, we just use JavaScript to see, hey, was args on first name set? If not, let's set Joe. And then we'll call super. And at the root super, we'll actually set the properties to the instance. What we do right now is we actually set the properties before init is called. So there's no way for you to know if there's a default property or if the property came in via create or new. So another cool thing of this before, right now there's actually no set of rules that will define how to use super. With this change, there's two rules. We call super if we are overriding a framework method and before we touch the this in the method. And taking that moving forward, that is all you will need to know about how to use super. So not only is this easier to explain, teach and understand, but it's more aligned with the underlying runtime. Thoughts? Anyone? Please show me. I'm excited. Misalignment number three. And this one is rough, but I believe we have a solution. So basically Ember object reopen is super powerful. It allows Ember to slowly augment objects as you pull in new features. So if you add in the router, you we can add features. It also is a great way for us to add deprecations and to pull out existing parts of Ember and store them as add-ons, but mix them back in so that we allow people to fall back to old functionality. But it turns out it is kind of buggy. It requires complex internals and is the root cause for massive amounts of our allocations and shapes. Solution is basically, I would like to propose that we limit reopen to only work until the object has its first instantiation. So remember that chart I showed of Tom and Yehuda's branch? Basically the most expensive things that are left are related to this lazy reopen code. So who here doesn't know what meta is in Ember? Come on, put up your hands. Who's seen it but doesn't know what it is? All right, that sounds a little bit better. So although meta and actions four are very related, meta is a good thing, I think. We can fix it. This is meta. It's the heart of everything that powers Ember. It's how the listeners, watchers, events, CPs, bindings, chains, everything works as via these meta things. And in a normal application, this is kind of every class has a meta, every instance has a meta and the meta's inherit the same way the class hierarchy inherits. It turns out that most of these metas are fine, but the metas for the instances are what kill us. The flexibility that we need to support, reopen after the first instantiation, basically means that we are grinding against the underlying runtimes. So meta is basically for live inheriting. It does some crazy things that we can totally change. Basically every O create is a new shape. If we create 4,000 records, Ember data users basically, we'll have 4,000 different metas, meaning any code that touches meta, which is all of the super sensitive code, can't possibly be optimized. It turns out that if we can get rid of this reopen after first instantiation, we can actually make every meta the same shape and we can now potentially benefit from actual runtime optimizations in these code paths. And one trick that allows us to do this is no one inherits from the meta on an instance. No one makes a subclass of the user instance. They maybe make a subclass of the user class, but this little detail will allow us to shed even more of that performance problems that we saw. We thought meta was bad. It turns out actions for, which is heavily entangled with meta, is a little bit crazy. I don't know. Who here has run into this code before? I don't know. You look at it and you're like, well, I don't wanna deal with this. But as it turns out, applying these same rules, we can totally make meta listeners substantially faster. And how does this actually work? Each meta, which already has some problems, has another object called listeners, which has all of the problems that meta has and more. Instances is kind of the graph of the Ember object. But as you can see, the medas and the listeners in green, they're pretty fine because we have few classes in a system. We maybe have hundreds of them, but we can have thousands and thousands of instances. So if we can optimize those last ones, those last pieces of profile, go away. So solution. We will work with V8 to handle this better. I believe we will kill hopefully, maybe not, hopefully we can kill reopen after instantiation. We will optimize meta's for instantiation and listeners for subscription. So I have an early spike of this. I can't demo it to you because it is not quite ready, but the early numbers are substantially faster than what we have today. So I'm quite hopeful of this. And if anyone wants to help, Yahoo is hiring, come see me or come see any of the other Yahoo's and love to work with you. Also, when doing optimizations, turns out premature optimization is not necessarily a great thing. We should only do it when it actually matters and we should only do it through the code that will actually provide us performance. But it's fun, yeah, yeah, yeah. Another thing that I have learned is that benchmarks can trick you. Not only can they trick you, they will almost always certainly trick you. The only way to have reasonable certainty in your improvements are using all the tooling available to you and work with someone else that can nitpick your work. And remember, actions up and bindings down. That's it, thank you. Any questions?