Optimizing an Angular application - Minko Gechev

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
so here are a couple of words about me I'm working on rhyme which is the platform that you can actually today use in order to follow along the workshop this is a platform which allows you to build more scalable and consistent workshops and corporate trainings by using virtualization in the browser and I'm also quite active in the open source community so I'm former member of the angular mobile team I also together with jump app webinar and warbell works on the angular style guide which is known in your IO I'm working on angular seed colorized Collider is the two for static analysis which you might be already using in angular CLI and a couple of other projects which are related to the angular compiler mostly so the workshop today I wanted to focus specifically on runtime performance previously I was building a lot of content related to making our bundles more but as we saw yesterday on the keynote the angular team is working in the exact same direction so we have Ivy and Ivy can allow us to build a hello world which is only 2.7 K and we have amazing to link underneath in the anchor CLI which allows us to tree shake our application apply different dead code elimination techniques and so on and so forth however regarding runtime performance everything is in our own hands so we need to take care of this so there are a couple of techniques that we're going to take a look at I try to make them angular specific but also to allow you to see where they're inspired from so most of them are inspired from functional programming and you can apply them not only in angular but in other technologies as well if you want to follow along with me you can join on this URL right here rhymes comm / go / ng perf if you're watching the live stream you should be able to join on this year as well after registration you should get a virtual machine which has the exact same setup as like everyone else it has the project prints told it has node modules pre-installed so you don't have to waste time in this it has vyas codes and a few other a few other tools which I thought it might be appropriate after you register your going to see screen similar to this one here so your virtual machine is going to be on the left you can control it and you can follow along with me there and from the right usually there is a video of me showing something or of the guide showing something either a video or just their virtual machine performing but however in this case you're going to see my screen here on the big on the wall so you can just collapse the right the right side if you're following the live stream you can watch the video on YouTube and follow along we need in your virtual machine right so we're going to take a very simplified business application today and we're going to discover all the performance issues that it has and we're going to go step by step into optimizing it and making it faster let's take a look at the business app so you can open if you're following along you can go to projects optimizing connect your app and so you can run and you serve there this is an angular CLI application so until this loads we can just briefly take a look at the code base so as in any angular application and your co application we have a main file in the main file we are booster wrapping the app with the app module in the app module we have the bootstrap component app component which internally has a templates and in this template we're creating two instances of the app of the employee list component also we're holding some state inside of the app component we're holding two areas of items of type employee data that's pretty much what's interesting about this component we can take a look at the employee list component right after after that but before going into the employers component let's take a look at the application so that's it we just have two lists of items we have a list for a sales department and at least for the R&D department in our organization and for each individual list item we have a name and a numeric value this numeric value goes through some heavy computation so it can go through whatever business specific competition you have in your application like standard deviation or whatever is make sense for you and also we can add new employees for instance here is Jason we can remove employees and so on and so forth so statically the application structure looks as follow we have our app here and it is built with two components with the app component and with the employee list component the app component wraps the entire application and it can it also hold some additional piece logic the employee list component wraps the individual Department in our our organization so it can wrap the sales department or the R&D department and the interesting thing which we already mentioned was that our app component holds the state so it is the container component and the employee this component just visualize it and just does some aggregation regarding the numeric value for the employee data so that's it and yeah I used a very simplified calculation I just mocked a business calculation in order to make sure that we are all familiar with the computation and in the same time it is implemented in a very inefficient manner so we can stress even further on the optimizations that we're going to perform so Fibonacci is actually not too far from real-world because it has very important characteristics that most calculations that we want to apply in our apps have we're going to stop on this in a little bit later so yeah that was the app it looks quite simple but let's add a little bit more data so we can just switch to the branch on push and optimized after the browser reloads we're going to see that we have few more items in fact we have 140 items so no that many and if we try to add a new employee yeah I'm typing right now still typing I'll stop alright yeah that was it so it's pretty slow and this is with a hundred and forty items there are not thousands yeah it is very slow obviously and it is interesting to see why it is that slow so we can use chrome dev tools we can just open the performance tab here I'll do and just start the recording let me let me rephrase because it's going to take less time than pressing backspace so once we start recording I will type several characters chrome dev tools here is going to make a recording of which functions are invoked how often do they do they get invoked and how long does it take them to their execution to complete after we press stop google chrome is going to just aggregate this data and show us what's going on so we can see that we spent a lot of time in scripting when we take a look at the bottom up top we're obviously going to see that our Fibonacci implementation gets involved far too often so let's see what is actually going on and why it gets involved that offand each time when the user presses a key on the keyboard we're going to invoke them multiple times to change detection for first the app component which is the root component in our app right after that we're going to perform the first traversal so we're going to go to the employee list component for the sales department we're going to compute the numeric value for the first employee for the second employee and so on and so forth for all the employees in the lists we're going to do the exact same thing for the other department so we're just typing something into the textbox but the change detection gets invoked for each individual component although there are obviously no changes so what we would want to do is to invoke the change detection for each individual employee in the list only when we get a new list right it makes sense because otherwise we have already computed this data so we don't want to recompute it but if we get a new list of employees we would want to go through each individual employee and recompute this data again so we can use on push yeah that's a very popular optimization practice that we can apply in our applications and the on-post change detection strategy is going to help us in a way that it is going to trigger the change detection in component when this component receives a new input so when the component receives a new input for instance our Employee List component receives a new reference to an array of employee data objects we're going to trigger to change the tection there this means that passing a new reference is going to trigger the change detection so angular internal is performing is comparing the data structure of the current inputs and the previous inputs by using a reference check which is very fast it has constant complexity but it may require us to create a new reference of the RA right so this might not be extremely convenient for us this means that we may need to copy the entire RA in order to make sure that angular knows that we have added a new employee so this is not very convenient because it first is very slow we need to iterate over the initial list in order to copy all the items from it into the new one and right after that we're going to allocate a lot of additional memory which we really don't have to so we can use immutable J's for the purpose how many of you have actually used immutable J's alright a couple of you oh this is a technology by Facebook which implements a lot of immutable data structures in a smarter way so internally they are using persistent data structures with partial persistence and this has two important characteristics first we're going to get a new reference every time when we intend to apply a mutation on top of given instance of a data structure so we are adding a new list to our immutable list and we're going to get a new lists so the initial list is going to be unchanged and also we need to copy the entire data structure we don't need to copy the entire data structure because internally immutable J's is reusing everything it can from the original list right so let's do some refactoring and right after that I'm going to show show the benchpress results so first what we need to do is import the immutable list from immutable and we need to change our employed data to use immutable lists we can we can see that we're getting an error here that's because a list and sales and tyranny here needs to be converted to immutable lists also the ads and the remove method needs to accept immutable lists now so we can just change this and that's it so there is something important to notice here I mentioned that when we mutate an immutable list we're going to get a new list and here we are not going to we are not doing anything with the new reference so we can just return it for now all right and we need to assign it to the already existing reference for the corresponding list so here we need to assign it to the sales list sales list again and here are in the list all right so now there is also one more change that we need to do so when we go to the employee list component we need to change its input data to be of the appropriate type in multiple lists and since we're using the length property of the RA we need to change its to size because that's what Dima - list uses alright and the final thing is just to change the change detection strategy of this component so we're using change detection change detection strategy dot on push cool let's see whether everything works everything seems fine now now yeah well we should have a very fast tap right I'm typing yeah it's not too fast isn't it so it was not even able to accept my first character I guess and when we remove items it is too very slow so on push I guess it is not it is not the only optimization that we need to apply right I didn't think initially that I have I have any performance improvement so I did some performance benchmarks with tank your benchpress and here is what I found it is at least twice faster but it is still very slow so we need to put a little bit more effort into this and see what is actually going on in order to do this let's just inspect it so let's go back to our list component and that one log statement so I'm just adding the log statement into the calculate method which is invoke for each individual employ in the list and just logging calculating and the name of the department so when I start typing something we can see that the sales department has been crazy we are recomputing the numeric values for each individual employee in the list when we are typing and the understanding of the Pampas change detection is that we're going to trigger the change detection when our component gets new inputs right this seems that is not exactly the case because we are not creating new lists of items so we are not changing any references well I was very interested for this going on so I took a look at the integration test of your core and I found out that anger is also going to invoke the change detection of a component with on push change detection strategy when an event instead of this component gets triggered so this wasn't something too obvious and I had to do some refactoring in order to fix it so here is a refactoring that we're going to do now we're going to decompose our employees components to the name input component and to the list component so our name input component is going to encapsulate all the events which are happening while we're typing something like the name of the new employee and the list component is going to use on push change detection strategy it is going to render the individual items and compute their numeric values so this way we're going to encapsulate all the events they're going to be insulated in the name input component because the name input component is sibling component of the list component so that the list component is going to be just not going to needs to recompute anything so let's do this and that's what we're going to explore what is actually going on in terms of change detection indication so first if you're following along in your virtual machine you can go to SRC app employee lists and here we want to use angular CLI to generates a new component so ng GC angular generate component name input keep in mind that we're adding a declaration of this component in the employee list module so we want to do the exact same thing for the list component so we have our two new components and we need to move some logic to them so our our list components needs to handle key key down events so our name component excuse me name input component so we can just move this logic to to this component we can remove only in its lifecycle cool because we don't need it and here cold weather complains that we're not following the convention in our application so we need to change the prefix of this component selector with d and we're also missing the ad output so we need to copy this output as well you need to import the event emitter and make sure that you import the correct event emitter from angular core because the first suggestion is from protractor alright so it seems that very good with the name input component let's go to and employ this component now and copy the template just move it to the name input components template right and we need to do the exact same thing for the list component so we're copying the material design list to list this component I mean we need to copy some logic as well so we need to copy the calculate method and we need to copy the remove output as well and finally Fibonacci and we no longer need the calculate method and now we need to add these components in the employee list components template so here we need to add the name input component which throws one output at and we need to trigger we need to handle it just by omitting the value that it rolls we need to do the same with the list component however here the output is removed and we have the data input so let's see whether this works all right the SD list is missing here always right here oh yeah you know I didn't edit the inputs of the list component so it is right here as well all right so the layout will be a little bit broken because we didn't move styles we're going to do this as well now let's move the styles from the material design form to the name input component and the rest of the styles we can move to the list component and this should look a little bit better after we refresh and there is one final optimizations that optimisation that we need to perform we haven't actually changed the change detection of the list components to use on push all right we can save this and now we can start typing yeah it's so much faster right [Applause] so in fact that's how much faster it is and here is what is going on every time when we start typing something we're triggering the change detection strategy in the app component after that we are going to employ this component and everything stops here this happens because the name input component is inside of the employee list component so we are triggering the change detection in the employers component as well eventually if we remove the own push change detection strategy from the [Music] employee this component we may invoke the change detection also in the employer list component for the R&D department but this will happen only when if we want to do that I mean we don't have to but also the computations happening in the employers components are very lights so if we invoke the change detection there it's not going to be a big deal yeah so we saw how fast it is but let's see how how quickly we are adding new items so my machine is quite fast actually but let's set the log statements to see what is going on there is still some delay with which is about 100 milliseconds eventually so we can add log statements we just says calculating and that's it so we're adding now when we are typing nothing happens because of the optimization that we just did but when we press ENTER now we are calculating a lot of values in fact there are exactly 71 we are calculating and the value for the new employee and all the remaining all the other employees from the list although we have already calculated their value so it doesn't really make sense to recompute them right but that's how the anchor change detection works if you're just going to take a look at so first let's go to the diagram we're going to add a new element we're going to throw we're going to trigger a change detection in the app component the change detection is going to be triggered in the employee list component after that in the list component because we have gotten a new reference right we have added a new element so we have created a new immutable list which we are passing as an inputs to the list component so the list components change station gets triggered because it has received a new reference for its list first its input and this is going to recompute the numeric values for each individual employee in the list because we're going to just recollect calls in the ng four in the template so this is not too practical we're wrecking everything every time when we add a new item and this is definitely not something that we would want to do instead we can take a look at the characteristics of the Fibonacci implementation I mentioned that it has very important properties which are common for most business calculations that we are performing right so first it does not perform any side effects this means that inside of the function we are not sending Network requests and we're not touching local storage we are not logging anything into the console and it returns the exact same result when applies to the same set of arguments so it even doesn't hold internal states which is completely stateless and it just receives an input and returns an output that's it so functions like this are called pure functions and in angular we have a concept which is inspired from for pure functions these are the pure pie pipes I guess you're mostly familiar with them they're used for just data processing they can be either pure or impure and the pure pipes are kind of the alternative of functions they're kind of inspired from them so there are stateless and then you realize that the pure functions are always always going to return the exact same output when you walk through the same input an example for a pure pipe is the date pipe let's say an impure pipe is the sink pipe which some holds some state internally so it can hold a reference to the pro to a promise or an observable so it is not a pure pipe and angular can perform an optimization with pure pipes it can it is going to invoke a pure pipe only when it detects changes inside of the pipes inputs input values so again only a reference check is going to be performed angular is not going to do like shell it's not going to compare the values with a shallow like search or you know it's in depth it is just going to be a reference check so we can take advantage of these pure pipes we can just open angular CLI again and generate a new pipe ng g P this time and go it with the creative min calculate so when we create the calculated pipe this is going to add a new declaration in the Employee List module we can just go to them to the list component now we can copy just cut that the Fibonacci implementation now you can see if and move it to the pipe implementation right here just for completeness we can just set the pure flag to true by default pipes which really clear our pure so the default value of the flag is true but this is just more explicit and maybe more obvious and now we can invoke the Fibonacci implementation and the final thing is to go to the list components template remove the calculate invocation and replace it with the calculate pipe all right well now adding items will be faster maybe not noticeably faster but we can add one log statement just to see what is going on there so here we can lock lock calculating with the value we're adding can employ and we're calculating Fibonacci of 26 so we're not rekt computing already existing values this is because angular treats such expressions which are containing only pure pipes as referential it transparent this is a concept from functional programming which means that if given expression performs insignificant side effect or no side effect at all if we replace the expression with the value that it produces the semantics or the meaning of the program will not change so what angular does is that when it finds that we have invoked previously punitive 27 it is just not going to invoke the expression again because we haven't changed the input of 27 we're just going to reuse the result and the change and the semantics or the meaning of our program will not change so this is why we can use pure pipes right so it's faster now for sure let me take a look at the results from the benchpress benchmarks yeah so we're a couple of times like a couple of hundreds of times faster again just by using pure pipes and that was a very simple optimization right everybody can do this just move your pure calculations to pure pipes but if we take a look at our data we can see that we have basically values from small range so here we have Fibonacci of twenty seven several times we have even H of 28 and Fibonacci of 29 and angular is going to invoke it is going to evaluate all the all these pipe expressions so if it sees that we have several employees with numeric value of 27 it is going to reuse the value which it has already computed it is going to recompute it every time but on the next tick of the change detection angular is not going to recompute these values because there are in the past two referential transparent expression so with this small sample with these values from a small sample we can do some caching in order to speed up our application even even further let me show you what I mean here so here is what happens during the initial rendering of the app you can see that we have multiple invocations of calc multiple locks of calculating with 20 of 25 29 here and 28 and 27 so we are recommitting the same value all over again although we know that the result of invoking calculate of 29 is going to be always the same so if we have some global cache in terms of memoization which is only possible for pure functions we can speed up our application even further and in this case here is actually what the difference between memorization and tor pipes is going to be so with two pipes as I mentioned when anger sees calculate of 27 it is going to compute this value when it sees this this expression again calculates 27 it is going to compute the value again and same for each next time however with memoization if we have calculate of 27 we're going to compute the value but we're also going to associate it with another with the arguments of the function in a cache so we're going to associate the result of the computation with the input which is 27 so in this cache 27 is going to be the key and the value is going to be the result of the computation the next time when with the same thing happens when we have calculate of 27 we're first going to take a look at the cache we're going to try to find whether we have 27 there if we do have 27 we're going to reuse the result from the cache and the same for the third calculation so before we go to server updates this very simple optimization so again here is how often the same calculation happens we have 29 several times and the memorization we can imply with a very simple decorator so we can just import memo from memo decorator and we can apply this memo decorator to our pure pipes transfer method and that's it we have cooked we have calculated 27 only once 28 only ones and the value from 29 only ones as well [Applause] oh yeah so we don't have we haven't calculates 24 and 25 but yeah we're just not doing any redundant calculations now and this was only a matter of this simple decorator which you actually have already pre-installed on your virtual machines so memo memo decorator is the NPM package on NPM which we you can reuse and now the final part of the of this presentation of the session is to handle real-time updates so for the purpose you can you can first let's reset all the changes because we already have these seeds and switch to the server updates branch oh I have to remove the files here all right so in this branch here there is some additional piece of logic that we're going to take a look at in a second you can also go to the server directory and here by using TS notes run index dot yes all the required modules for the server on your virtual machines are already pre-installed so you should just run TS notes index dot yes and after we refresh we're going to see that we have very frequent live real-time updates from the server right so this has like several issues the most obvious one is that this is not very digestible from the user right just some things some stuff is free flickering on the screen and the second one we're going to take a look at that in a second after we do a very brief performance profiling so after a performance profiling loads we are going to see that we're actually spending a lot of time in some useless calculations which are not necessary for hemming updates in such Ryota real time manner so we were spending about one-fourth of the time in scripting one fourth in rendering and one fourth in painting like very roughly and this is definitely not a good idea so what we can do instead is to apply these real-time updates which are coming from this socket right here constantly what we can do instead is just to apply them on chunks so we can buffer updates for let's say one or two seconds and right after that apply all these different buffers commands which are coming from the server at once and there is usually often there is the needs to run code outside of the anger zone this time we don't really have to do that so we are not going to do anything too fancy we're just going to use some observable operators but before that let's take a look at the app module so in the app module we have one more provider now we have the employee service and the employee service it is creating a new observable from the WebSocket so we're creating a new WebSocket clients we're connecting it to a local host port 5 5 5 5 and we're after that exposing the observable through this get through this gather after that we're injecting this employee service into our app components and we're handling the updates real time in the NGO unit method so what we do just to subscribe to the command observable and handle the commands one by one what we can do instead is just to just pipe our commands observable and invoke the buffer operator here and so the buffer operator we can pass observable dots interval of let's say 200 milliseconds 2,000 milliseconds after we save this we're going to see that we're getting a compile-time error here this is due the fact that all the different commands are already buffered so what is going to happen is that so the commands the commands observable is going to push different commands coming from the server which are for removing or adding an item to each one of the departments and these updates are going to be handled real-time however when we added the buffer operator or we're just going to start buffering them until the internal observable right here which is the interval observable triggers a value so each 200 2000 milliseconds here we're just going to trigger a new value and all the buffer cam commands to these points are going to be pushed to the callback which which we have passed to the SUBSCRIBE methods and that's it so there are an area of commands now so let's call them commands and let's just iterate over them so here it is so we are applying you updates every two seconds now it is maybe still not the best user experience possible but we are not at least like spamming the user with updates every 10 milliseconds and on top of that when we run performance benchmarks we are definitely going to see that we have performance improvements because at least we're performing much less frequent updates right so we're going to spend much less time in rendering so yeah of course much less time in rendering in scripting can't in painting my point with this example was to show that it's not necessary to do super fancy updates running cold outside of the anger zone and something to angular-specific all the time there are some concepts such as buffering and caching which we can which we can apply in all different technologies and we can we don't have to do like reinvent the wheel this way so with the real-time updates we we got such performance improvement and also slight UX improvement so that was everything that I had today and I'm actually happy to announce that that was kind of the official release of rime as well so yeah so each one of you who was able to follow follow along and get a virtual machine is going to get I created for creating courses in ranked if they want here we really have to hear your feedback about your experience here are some links so I wrote a series of blog post about this these these topics there I'm going a little bit more in depth and a little bit more theory so you might be interested to take a look at this thank you very much for your attention [Applause] you
Info
Channel: ng-conf
Views: 64,050
Rating: undefined out of 5
Keywords:
Id: ybNj-id0kjY
Channel Id: undefined
Length: 39min 52sec (2392 seconds)
Published: Thu Apr 19 2018
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.