1000x FASTER JavaScript?

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
all right so let's get started so I do have some I do have some really good ones coming up okay so the first thing I want to do is this one look at this one making tan stack table 1 000 X faster huh are you guys excited about this one I'm excited about this I actually really wanted the real I really wanted to read this one this one's great I have even read it all I know is I saw this and I was like dude I gotta read this this looks exciting I immediately think they're you know one line change yeah my guess is that okay so here's obviously my guess my guess is that they did something stupid the stupids in the stupids as I like to call them in JavaScript are very very simple uh memory and that's it it's always memory so my guess is that uh either they're doing a bunch of tight Loops using a map filter reduce or map and filter or be a spread operator that's almost universally where all the speed UPS happen it's it's one of the two it's one of the two it's always been one of the two so let's find out right now what it is uh let's see uh let's see let's go marker tan stack I got it right yeah I figured I'm gonna get it right tan stack uh make query faster all right let's do this okay so that's my guess is it's one of those two because that's what it always is it always ends up being something like this because that's the thing about JavaScript that a lot of people don't understand is that memory plays an enormous role in the performance of your items I actually have a pretty cool little test I'm just getting started I'm gonna build a whole uh experience around it gonna build a server around it do all that I have a pretty fun blazingly fast video coming out of the works here let me just give you a quick little preview just a quick little preview uh oh yeah uh rebase rebase me Daddy welcome to Costco I love you rebase me Daddy all right uh oh my goodness are you really dang it uh fine I'll just I don't even want to type that in right here I don't even want to type it in somehow I got there uh JavaScript memory look at this okay so I have a simple test create an array of whatever size for Loop and Summit or reduce in Summit how much slower or faster or the same do you think it reduces this remember this is a micro Benchmark this is not fair this does not actually equate to real world performance but what do you think what do you think type one if you think it's the same two if you think the four Loops faster three if you think the reduces faster one the same two the four three the reduce we got some threes popping in nice I like the adventure got some twos coming in all right very very good we got some ones go in there nice and some zeros because they clearly are good at rules uh so just one thing I do a little quick little timing of it all and then I can execute the function over and over again and I create effectively my different arrays this is the size of array I've been working with a lot with at Netflix lately it's been very very hard to do anything lately and so I'm gonna do one ten hundred or a thousand runs right here's my results there we go I'm gonna just warm it up make sure that the jit has been you know enabled everything's going good and then I'm just gonna push them all in and do that very very simple right very very simple uh and you can even use uh TS node because again it's all about TS nodes slow down is only in the loading it doesn't affect jit at this point once you get to the actual code running everything should be easy uh where is this thing called it's called JavaScript memory and we'll just do a simple test and as you can see on my slower computer we've started all these tests we're running we're running we're running we're running probably going to be a few seconds my guess is that there we go as you can see what I work with at Netflix is 577 milliseconds versus seven seconds we're talking a plus 10x Improvement but that goes way down as you get closer it's not that reduce is slow as a but here's the deal is this isn't even showing you the real problem okay again micro benchmarks remember micro benchmarks be careful don't just look at them right so with a huge array as you can see it's like 10x and then only two right oh look at this reduce is now look at that reduce is looking faster here huh that's weird no one knows what happened there right nobody knows what happened there this is of course a one run you know but anyways kind of interesting right so it's it's it's not as simple it's not as simple right but anyways let's keep on going dismal a dismal all right so let's do this making the tan stack 1 000 X faster with a one line change all right hot dang hot dang Tanner Liz Tanner Lindsley coming in should we get tanner on the stream type one in the chat if you'd like to hear Tanner just talk about tan stack I think it'd be a lot of fun uh Cool Tech um smart guy I build out Falcor so I should be able to have a lot of uh ability to kind of talk about this I even see some ones coming in all the way over in the in the other Pond all right a lot of ones okay a few minutes back when I was working on JavaScript front end for a large data set using the tan stack uh the relevant constraints were up to 50K rows of content grouped up or grouped by up to three columns okay so I don't really know how tan stack works or really I've never even tried using it by the way I didn't realize that there was no music that's weird anyways I'm gonna put it lightly in the background just lightly in the background okay just lightly because we're reading reading is boring without it all right so all right so I don't really quite understand this let's say using react and a virtualized renderer showing 50k rows was performing well you know that you know that that Meme with GIF with that with with doubt I'm having a doubt coming in right now I'm having a doubt going in hard right now um but when tan stack table growing uh feature was enabled I was seeing slowdowns of a few thousand rows and huge slowdowns on 50 000 rows it might have gone unnoticed if it was a hundred milliseconds or slower or even 500 milliseconds slower but in the worst case renders would go from less than a second without grouping up to 30 to 40 seconds with it yikes okay tracking down the issue initially I tried to use Chrome JavaScript uh profiler but but it can be tough to use while the when the performance is slow okay so my guess is that he wasn't using it correctly because this is actually exactly what the performer so use the performance profiler not the profiler the performance Tab and the performance tab will actually give you a timeline and if you highlight over it it'll actually not only give you a timeline but exactly how like the aggregate time of that as well within the stack it's it's actually the easiest way to do it uh the profiler adds a noticeable overhead yes absolutely and that's okay that's the thing is it's okay to add it because it's about looking at proportions it's not looking at specifics that's the that's the real that's the real key I'm very curious about this whole unusable business I'd like to actually see if it's unusable enable let's say unable to profile I reach for an old simple handy console.time this is a great this is also a great way to do things great way to do things expensive code expensive code yep all right perfect a side note about optimizing code as a programmers we're full of ideas about what needs to be optimized and are terrible at getting it right okay remember my prediction it turns on your bad feelings and the good feelings it's a nightmare oh my goodness I forgot to turn off alerts hey thank you very much I appreciate that but I gotta turn off alerts for a second um let's see so my guess again spread operator because that's like that's memory that you don't realize how much memory is actually being created there like it's so much memory a clone or a bunch of Loops in a time in one of the hottest spots that are running over and over again that are creating a bunch of garbage right and you're kicking off huge GCS but since it's so high since they're talking about 30 or 40 seconds as opposed to an amortized amount it has to be it has to be a spread or a clone because that's the only thing that's going to cause such terrible performance difference uh let's see we make educated guesses at what is important to optimize and where the problem is and until we measure we're usually wrong I try now to wrap everything possible in a benchmark to make sure I'm doing it uh in the right place so this is dangerous again always be careful about this micro benchmarks extremely misleading uh I'll give you some reasons why a micro Benchmark when you run it in by itself it could actually create a bunch of garbage but the duration of a micro Benchmark does not get affected by GC and so you run into this worst case situation where it looks better on paper worse in production got to be careful about that and it can and here's the worst part about that is it won't show up on the client because the client's largely unaffected by a lot of these GCS other than when you're scrolling but it it shows up heavily in requests per second to a server that's where it really really becomes obvious the design decisions you have made the name is the prime machine press like or subscribe got him where's that twitch private baby all right um let's see now let's see now let's add okay uh here we go let's see and then start narrowing The Benchmark inside the code from there when tracking down performance issues this would be a general outline okay okay okay you're just effectively rebuilding a flame graph I see I see I see uh good good time you could have used actual the actual node flame graphs at this point if you couldn't do so so good side note here if the JavaScript profiler which indeed does add a lot of overhead doesn't work for you you can enable uh perf basic profs and pipe the stuff the perf utility and you can actually generate flame graphs and this will give you a lot of idea where your code's going fast or slow you know just another thing because that's going to be using you know that's going to be using d-trace and all the stuff deep under the hood as opposed to something that's not as good you know what I mean because the thing about like the performance analyzer that's weird in the JavaScript profiler is that it actually measures every single call along the way right it's doing everything whereas perf it doesn't do that you say hey give me a stack Trace uh every like 99 times a second and what that's going to do is it's going to ask for a stack Trace 99 times a second but you may call a hundred thousand functions so the difference between the performance doing a hundred thousand operations versus something that's just going hey what's your stack Trace hey what's your stack Trace very very different in performance this will largely you know keep the performance mostly in in line and then you just measure for a long period longer period of time have it run a million times have it run so much right all right anyways sorry we'll keep on going all right back to the table rendering as usual before measuring all my guesses at the potential performances were wrong yep my own code is fine this is the case where it usually uh is actually a library bug which was a surprise but there was no code path I could find that was performing poorly and all the time was let's see of all the time that was spent in the library when using tan stack table in react all of the logic happens pretty centralized place the react table the use react table hook so it's easy to see the time was there okay gosh I hate react code like the I I hate Hooks and how they work there's so many things about this that I think is just disgusting anyways one of the nicest things about developing JavaScript with packages is that at any time I can open up node modules folder and play around with third-party code in this case has modified the 10 stack table source code directly to add some timing information this is nice I will say that this is one of the nicest Parts about working with a dynamic language is the fact that because it's interpreted first run maybe even several runs depending on the size of your function uh actually it's really the amount of characters within your function actually determines uh how fast it can get jitted and the arguments going into it either way this right here is really super cool I love that uh turning on grouping was uh when everything slowed down so it made most sense to start timing that code this is an abbreviated version of the group row source code with my first pass at timing what I thought was like the likeliest call for us pay attention primarily to console.time statements you don't have to understand everything that's going on okay so you do the everything you do the filter you do the grouping filter sub row aggregated row cursively okay so he's going he's doing like the whole nine yards here he's getting everything let's see do we see any of my general culprits do we see a clone in here do we see a structured clone group up recursively no uh I always hate seeing just functions created in functions uh there we go subros group up recursively oh see that's it like this is just such a such a dirty piece of code anyways existing groups groups groups groups groups groups groups okay look at this array Dot from entries this right here seems really dangerous like right away I feel like this is a this is a naughty line of code right because not only that but then you also you also map over it and you create a what the hell are you creating what the hell is this right so this is just copies of copies of copies right gotta be very careful about this anyways my hunch was that that the group up recursively function was to blame it made logical sense that tens of thousands of recursive calls could cause a Slowdown uh yeah that's not okay the first pass uh was a bust it logged thousands of the subro timers yeah see I wouldn't be so sub row timers I probably wouldn't have done this seems kind of boring um I would have yeah it seems boring the only thing I worry about is this thing right here it just seems like yeah that's gonna cause a lot of memory but maybe that's all wrong maybe it's somewhere deeper first pass is a bus log that every time iterations was fast and there were too many of them to be useful so I cut it yeah so this is where knowing how to use a good uh command line utility would be very useful so to be able to pipe in that uh pipe this in to a uh like to awk and then just pluck out the times and sum them all together I really do the actually so that's one thing I'm very excited about copilot I really do want to try copilot CLI because it's going to give you a lot of it's going to give a lot of people fresh access to a set of utilities that have been around for five decades right it's gonna be fantastic all right blah blah blah blah are removing that uh I started to get closer I was accounting for most the time but I had two problems I wasn't counting for all the time everything was 33 seconds group up recursively was only 23 seconds and the chunk of time I was logging was too large to uh to usefully identify the problem yes exactly that makes sense I realized I had missed a function call a little unassuming a function called Group by I added the console time blocks Rose Group by columns row by this one got it almost the entire 31 seconds was concentrated into three calls group I nice nice nice okay awesome this is great to see so let's see for each grouped column it was calling group I and for each call took roughly 10 seconds so what the heck was going on in the group by function all right let me see and look at this I always warn you remember memory why are you copying and resetting into a row why are you not pushing just push the F and key push the F why would you create a new key just push it's it's already here you could just mutate that son of a I started chopping up the function I so let's see I switched the map to be an option okay so map to object literal will always be slower it will 100 be slower always a for Loop to reduce so this is bad okay okay so this is good that he's experimenting that he's understanding the problem is is that's not meaningful a a for Loop to a reduce will not cause that many seconds unless if you're iterating over it like we showed like millions of items so this is not Millions so something's wrong here now there could be a slight speed up in not using a reduce and just using a for Loop right since you're going over 50 000 rows potentially you can see there could be some millisecond differences there maybe like a tenth of a millisecond but not enough to really call home phone it in call it great uh let's see those those had no effects so next I started commenting out all the lines of the function when I come into this line everything finished instantaneously yeah what's the purpose of that line and why was it slow okay there you go on each iteration of the reduced call code uh use the value that the column cell has a map uh key let's say this Value New York if there was no value Associated New York we'd set a new value yep that makes sense if there was already a value we would use the JavaScript stupid operator to concatenate the road on the end dude push mutate just mutate the effort object this means that each iteration of the reduce the spread operator was creating a new incrementally larger array getting slower each iteration yes it would yes it would what is the spread operation uh do behind the scenes it should be obvious exactly what it does in our case the operator takes an iterable object and Loops over and creates a new array it's probably more nuanced than this but the spread operator is likely oven yes yes uh it's always over and it's the size of the memory it probably in this case I bet you V8 can do something pretty smart like mem copy but it still has to go and allocate that memory it still has to go off to the you know the the garbage collector has to hold on to in some sort of Nursery someone else has to allocate that memory you know there's a lot to it it probably is more nuanced than this uh that means the size of the array grows but probably well it depends on the objects inside of here if there are simple numbers if they're all smes I bet you you could just do something like mem copy but probably they're not all smes they're probably objects and then I think mem copy can even take place right maybe it could because you're only copying pointers yeah why not why not you probably just man copy it uh I'm certain that there are let's see additional efficiencies provided in the language internals but the following code is equivalent yes yes which means the original version of group by code was a functional equivalent yes this is this is this is equivalent why would you create a new array at all I don't understand creating a new array I'm super curious why you're creating a new array and the reason why I'm saying I'm super curious about that is it like if you look at the original code right here there's no reason not to create a new array you only need one array here why are you creating a new array ever right all right no you don't even have to set the key don't even set the key don't even manually spread it seeing the manual spread one uh one thing sticks out uh immediately we have a nested Loop of course that's how it works a nested Loop is good uh is a good shorthand for identifying one of the slower forms over algorithmic complexity of Big O complexity uh o of N squared [Applause] um this means that uh for an array a size of 50 000 the number of iterations would be fifty thousand uh this oh so I mean yeah it's technically and you know there's that whole formula that whole n times n plus one divided by two is actually going on right here uh in our specific case the array started out empty and grew to the size of 50 000. it wasn't uh quite as bad but technically the complexity is still this yes which is a lot so that's a lot you couldn't do that blah blah blah blah pour one out for the garbage collector on the JavaScript runtime absolutely thank you for recognizing this part that's the most important part honestly I know that was the most actually moment of the stream ever huh honestly for how many iterations and how much garbage uh was accumulated it was operating quite well so the question was what could be done to improve it spread is an immutable pattern so should the code be kept immutable no no it shouldn't it's a reduced it's X it's not a pull bottle it's not a pull bottle iterators are not or these aren't iterators there's no pull bottles so you you can you can look at a reduce as its own like Atomic unit um aside from providing convenience Syntax for combining intervals the spread operator allows us to keep code immutable Boo this is the benefit because the immutable pattern means we avoid mutations by the way this sentence is like the most hilarious sentence sorry the writer I love your article I think you're doing great I just think that this is a funny sentence this is the benefit because the immutable pattern means we avoid mutations I mean you use the definition inside there by the way this article is very good though I'm very happy that you're discovering these things because this is what we need more of in the JavaScript Community the understanding that like garbage is really important and not only is garbage important linear linear creation of garbage is extremely important which means we don't change data from underneath the code that we're using it yes if this code was written to be immutable there were some other options we could try to improve the performance just push it in just push it in holy cow hey uh too long um I'm sorry for you or I'm or I'm happy or congratulations or I'm sorry for you nope the concat is just going to cause the same problem don't do it yeah look at that concat just as slow uh don't manually do it it makes no sense concat Road don't I don't understand why that one somehow faster I don't even get it at all yeah just push just push just push it just push the damn thing ah uh array from slice concat all have the same performance characteristics as the spread operator of course they do a slice is literally just creating a copy taking an iterator I bet you just taking an iterator and spreading it into a new array calls the same code AS slice zero which calls the same code as pretty much concatened nothing all right I bet you it's all the same thing Amber I'm not really sure what the hell ammer is uh using the inner node package oh dear gosh don't do that what do you mean the immr what the hell is emmer what what the ever a manual for Loop was the slowest option of course you know why was the manual anyone anyone in here why is this the slowest option anyone speak up right now why is this the slowest option anyone say right now say it right now why is this the slow the slowest because it's stinky variable initialization you're allocating memory each iteration yes okay so uh yeah so Alex so you don't allocate memory each iteration but that's a good point out a raise that just like just like in Rust they have a capacities you can actually see the capacity if you ever get into the engine and put print statements as you push things in there's like these little thresholds that exist so when you create an array usually it's like five elements are pre-created five pointers to objects are created for you and as you fill it in that's okay um and so you'll get you'll get allocations every now and then but yes that's exactly right but when you do a slice the size of your of your cut or the size of your copy is known beforehand so you can initialize that array and effectively mem copy it in whereas now you're not using a c utility you're writing mem copy in the slowest performing language so you know you you could would you do this and see or would you want to do this in JavaScript you'd want to do it in C right you'd want to do it and see where you know the size you always win huge performance can be done right so it's stinky yeah you stink whatever using immor performs slightly uh significantly faster than all other options uh Mr provides regular JavaScript interface on immutable data types using proxies oh gosh like no don't do that just don't just don't even do this this whole thing is just crazy you're in a reduced uh however the main case of the let's see the let's see however in the main K let's see however in this case the main benefit of the Emer approach is that we do not have Let's see we are we're not copying arrays just pushing directly to them because they were created in line in our function yes but do we even need immutability at all was the inner approach good could we get any faster of course yeah of course for performance you can't beat mutations of course that's how it works mutations uh modify data in place without copying or creating duplicate data over the group by function blah blah blah blah blah we are using a radar push which are effectively running the algorithm complexity of of one foreign okay so you know it would be a really great thing to do right here a really great thing to do right here is actually to uh take whatever code this is if this is going to be what would be referred to as a micro optimization at this point really like truly it would be take the map and initialize it with a size right take the arrays you're going to create and initialize them with a size right even if you're not using all the data initialize it with the size but then you have to create a wrapper object one that contains actual size and uh and then the array itself because the arrays length will be like 50 000 right and so you just create okay this thing's gonna have fifty thousand and then you create the size so you only allocate that memory once by allocating that memory just one time you don't have nearly the problems that would exist otherwise right all right let's see but do it let's see so yes let's say pushing directly yeah I mean maybe it's amortized but I think it depends on how you're using it since you're pushing it into it over and over again I don't think you can say it's amortized one uh that means our array grows the cost of inserting new elements does not funny you should say that it does it's just less uh it's constant time it's fast uh for one item as it is for fifty thousand I mean again technically right actually right because it depends on your balance and where the capacity is at every time you cross the capacity you have to do a linear copy into a new space you have to call effectively remalek underneath the hood Here's the final version now 100 000 times or a hundred thousand percent faster nice there we go thank you for doing this thank you for doing that thank you mutations always make sense right this is why you should always rely on mutations uh I love them for performance they are obviously the only way to go this whole push into this immutable thing largely it's done in places that don't even need it right like if you know that reduce is called reduce itself is atomic therefore you are returning a map produced by this function therefore it itself is kind of like its own Atomic block therefore you don't have to worry about any mutation that takes place within these lines it's not going to have any sort of outsized effect uh real look is the best C function it's the best C function all right there we go uh down to 10 milliseconds 100 uh X faster than the original code hey good job let's go well done mutation is always faster in every language every language mutation is faster every single time if you're creating new memory you have to go and you know get the memory now if it's on the stack that's much faster because it's like ad24 but if you're not on the stack and you're on the Heap it's like yo Heap can I have some memory and heaps like let me go find you a spot oh crap route hey operating system give me another page I got another page okay here you go here's this thing right like there's a lot of bookkeeping that goes on with the Heap source code here you go fantastic great job great great job I think this is a fantastic thing that uh that he did all right I also find it to be hard to get a great sense uh for for a profiler when profiling react codes and certain certain internal react code pads are hit so often that the results are yes I would agree with this it is very very difficult react in itself is huge is what he's just saying right there uh it's non-standard but the future to remember console.profile as well yeah yeah that's good we could have been helpful here profiling smaller blocks code rather than deciphering the whole react render stack absolutely consider how slow code was uh it still may not have uh been usable anyways okay what we found is Javascript engines have gotten optimized at a H4 yeah can be as efficient as handwritten for Loop shouldn't started off the video that way depends on the size of the data I'm actually really bound right now by CPU really really hard so having well optimized code is really worth it all right I think that's pretty much it I don't think there's really anything else that are uh fantastic so guess what the name is the prime engine thank you for watching I gotta go pee oh my goodness all right hey hey um all right actually there's one more thing I did want to say on this which I think is really really misunderstood in the JavaScript world or in the JavaScript Community it's just that if you want to understand JavaScript in the simplest terms just simply look at the code and just pretend you had to implement everything that you're seeing it's just like go one thing I really like about go is that you really just do have to implement everything there's not a lot of convenience I look wet yeah it's snowing like wild outside I pee outside and so it it helps you understand that when you see a spread operator it's just a you can think of it like a for Loop right it's that simple when you see something that's like structured clone try building a deep clone understand what it takes underneath the hood right like to know these things is really important and I think that it would add a lot of benefit to a lot of people's career just knowing and understand how things work deeper one level deeper you don't have to go all in just a bit deeper the name is the prime Stone
Info
Channel: ThePrimeTime
Views: 129,873
Rating: undefined out of 5
Keywords: programming, computer, software, software engineer, software engineering, program, development, developing, developer, developers, web design, web developer, web development, programmer humor, humor, memes, software memes, engineer, engineering, Regex, regexs, regexes, netflix, vscode, vscode engineer, vscode plugins, Lenovo, customer service
Id: B76gFi43HvM
Channel Id: undefined
Length: 29min 17sec (1757 seconds)
Published: Fri Apr 07 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.