Check out a free preview of the full Blazingly Fast JavaScript course

The "Wrapping Up" Lesson is part of the full, Blazingly Fast JavaScript course featured in this preview video. Here's what you'd learn in this lesson:

ThePrimeagen wraps up the course by discussing the importance of caring about performance early on in a project. They also touch on different programming languages and their performance characteristics, as well as the use of optimization techniques such as avoiding promises on the hot path and deferring networking to C. The instructor concludes by reflecting on the progress made throughout the course and encouraging students to continue exploring performance optimization.


Transcript from the "Wrapping Up" Lesson

>> Hopefully, you guys really did enjoy that. Because at the end of the day, no one's gonna care a lot if you make big performance wins other than people in a really bad situations, right? I have someone right now that's just like, they didn't have a problem, they didn't have a problem, they gained 10,000 customers, and now they can't keep their node instances up in production.

So at some point it usually goes, it doesn't matter, it doesn't matter. Shit, it's the worst thing we've had in a long time. And so, if you care early, it will often lead somewhere good.
>> You didn't necessarily mention this but I think I heard it somewhere else that had Bun done anything with async to make that stuff from an asynchronous fashion bomb?

>> They do have a different event loop, so it's libuv versus whatever is in SIG. So is that faster? Is that slower? Probably, not a lot difference. And if it is, for whatever reason, one is fast than the other, they'll be able to catch up. A lot of it just comes down to Node async versus Bun async.

Node just has a lot more hooks and everything built in. As far as I can tell, that's what someone from Datadog effectively said, and so they're working a lot on trying to make that thing much, much faster. And so that's a big bottleneck and a problem in mode right now, is just how slow that part is.

It's probably not libuv, though maybe there is something to it, which if you have a small additional cost per every single promise that is introspection. And then on top of it, you're making a ton of promises. And then on top of that, you're trying to process requests. Are you gonna create some general problems?

Potentially, you may, instead of having one request that takes 500 milliseconds, you have all your requests taking 250 milliseconds, 400 milliseconds instead. Which one's worse? I don't know which one's worse, is rising your 50 or your 90 worse? So that's more of a you problem [LAUGH] which one you want.

But Bun, in general, seems to be really fast. But Bun, in general, isn't nearly as feature complete or as much legacy support as Node does. Is legacy support important to you? If it's not, maybe you can try Bun out. If Bun also segfaults in production, is that a problem?

Well, it's segfaulted on me a few times. It hasn't been doing that lately so maybe it's a good thing now. But still, nonetheless, it's like there's risks, there's straight offs. JSC, is JSC faster? I don't know, people have been claiming it's faster.
>> What was the charting library you were using?

>> Okay, so this one is Chrome DevTools, memory and performance. This one is something I wrote with HTMX, let's go HTMX. It's just as literally Charts.js and Go and HTMX
>> And if you wanna know more about HTMX.
>> Yeah, if you wanna know more, we're gonna be having an overview of HTMX.

Yeah, we'll do gets and posts and deletes.
>> Does all this apply in browser engines as well?
>> A lot of it does, but often your problem, usually, your biggest problem in browser engines isn't the fact that you're not pooling or pooling or you're using JSON or not using JSON.

It's that you're either over painting the screen and making a bunch of layout changes or, B, you're using a very heavy client-side library. And just like it doesn't matter how much you do. They just have won the competition for using all of your resources. If you've ever gone in and you try to debug, say React or if you have RxJS or any of these libraries, you'll look at the call stack, and the call stack is 900 calls long to get to where you're at.

That's just like, you're not gonna be competing against that. Often, the problem isn't a you problem in that situation, it's the libraries you've chosen to use. It's a future you problem is I like to call that one.
>> Some point, should you just consider switching to Rust or C, or how do you know when you've reached that point?

>> Okay, so that's more of a philosophical question. Generally, my take is that, if performance could be a problem, use Go. If performance will absolutely be a problem, use Rust. If you don't have skill in statically-typed languages, use JavaScript. They take skill, they're annoying to use, I mean, they're not fun.

You know what, the funniest part about TypeScript is when you quit caring, you just slap an any on it, and you're just like, get out of here, and it's just fantastic. And you just don't even have to care as any as user. Boom, this is a user now, all right?

It's just like you can't do that in other languages, that's not an option in Go. And so obviously, also OCaml, people want me to mention OCaml. If you're a functional guy, OCaml is really fast. It's like they have, whoever is writing Reason ML, the creator of React wrote Reason ML, right?

What's the guy's name? I just saw it last night, Fulk, Falk, Falk. Anyways, the OCamlers writing ReasonML, they were able to serve the same website for Ahref, which is a company, a pretty big company. Same website render 65 times faster from their measurements on the server from going from JavaScript to OCaml.

So there's other options. If you're a functional person, you got Elixir, you got OCaml. I don't know, different. I'm not a functional person. I wrote some classes today. You can tell right away that it's just not a functional overlord, all right? Any other questions? You had a question?

>> Yeah, how will you rate the optimizations we've made so far? Which ones are that we should care and that was more type pre-major optimizations stuff?
>> Well, the big one obviously was getting off of promises. Finding your hot path and getting off promises, that will almost exclusively be a good call.

Now you may not be able to do that, right? If the API you're using just uses promises, if you're using fetch and it requires promises, sometimes you're in a bad spot. And so you may not be able to do those kind of things and so that's just real.

That's just how it works. So promises off the hot. Hot paths are as few as possible, always a good play. The next one is just try to defer as much of the networking to see compared to JavaScript. In reality is if you're in a server, if you're on a client, it's a completely different story, cuz the client is processing a singular connection.

A server is just processing all the client connections. So that small cost of JavaScript really adds up. So WS just, even though it's really small on a singular machine, you could literally not measure the difference. And it may actually even be faster just by itself than doing C just cuz of the interop maybe, I have no idea.

You'd have to measure it or it's just negligent. It's just that once that scales, it really falls apart. So those are the two big ones. Everything else is that only matters. It matters so much more not on my machine. All the other changes we made would likely have shown significantly larger wins if we are just in a constrained memory and CPU environment.

It would have been glorious. But we're just not in one of those, so it's really hard to surface any change. The goal of this course, of course, was to make you think every time you do squirrely braces, there's a real cost to it. You should think about that.

Spread operations, remember, you're copying objects over. You should be thinking about these things. Do you really need to copy all the time? Not all software is going to be easy. We observed and fix some pretty obvious stuff. And then we did some things that were less obvious, right?

Just even being able to identify that WebSockets were a problem required us to use two different graphs. And often, people don't use the memory graph as an indicator into what's right or wrong. And I think once you can kinda tell once you've hit a, you're probably doing too much moment.

And I wanted you to have that feeling here, cuz everything we were doing just wasn't yielding much. But usually at that moment, it comes around when you start saying this. When you just have nothing left that's going on, and you're trying to just how can we make this better?

Well, it's just like, at that point, you probably can't make things much better unless if you change the problem. So good thing to think about, and also it's kind of cool to think about where we came from. We started here, on master, on the very on the original one, at 50 concurrent or at 500 concurrent games.

We were at 31% of the time, we had a bad ratio, right? Now we're sitting at 3,000 games with 44%. Well, if you look at this, at 2,000, we're doing significantly better than we were doing at 500. So that is a real win. We have made real wins throughout today.

And so I just really do hope you take home kinda the art of this all, which is observe and try to fix and kinda come up with creative ideas. Performance and memory tabs are great. Node async stuff is slow for now. Memory is a good indication where problems could be.

GC time is a good indicator of issues. Sets are not always fast. Pull items, be a bit clever. This is more just an academic jab. Just because something is fast academically, just because you have a Big O notation of whatever, it doesn't mean it's practically fast. A really good trick to know about, this is gonna be very deep academic stuff.

And if you're not used to Big O, maybe this will be a bit confusing. But if you're ever doing graph algorithms, the length of E is equivalent to the length of V squared. That would be a graph algorithm edges. The worst case of edges is V squared. How do I know that?

Think of an adjacency matrix where every single node has a connection to every single other node. That's V by V so that'd be V squared amount of edges. So that means if someone's doing a graphing algorithm and the length is E log E, well, you can change that to E log V squared.

Which means that you can change that to E 2 log V, which means that the running time is E log V, right? It's just like, well, that's kinda cheating. I feel like you just cheated me because you're saying one thing but you're not actually doing something. So do you always just take run time as this is what it is?

Maybe take a little bit of time to look over and to test things and to actually confirm the assumptions that what you're looking at is actually better. All right, want more? I do a lot of this stuff live or I do some amount of this stuff live. Mostly, we do read quite a bit of articles these days, okay?

I become a React and if one will, but we do a lot of the perf stuff exploration from time to time on the channel. Do it a lot on Twitch, yes, this is self promo. There you go, that's really it. That's the end of the course. I hope you guys enjoyed it.

I had a lot of fun. You can clap, it's okay.
>> Thank you. Thank you, thank you.

Learn Straight from the Experts Who Shape the Modern Web

  • In-depth Courses
  • Industry Leading Experts
  • Learning Paths
  • Live Interactive Workshops
Get Unlimited Access Now