-
Notifications
You must be signed in to change notification settings - Fork 111
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Change generateData() to preserve object identity. #19
Comments
Hi @mhevery I've changed the way data is generated, and indeed, angular performs way better with. But I'm not sure it's something I want to push because, it's an optimization for Angular as angular is tracking object by their identities. The idea of this project is to provide naïve implementations (I'm not sure every implementation is naïve, but I hope so). Keeping row identities is not a real world use case, data returned by an REST service will never keep row identities, and more important, it's not how a new user will use Angular. But you can provide optimized example (in the optimized version section) with identities and track by strategy when it's implemented. I'll try to push the new data generator asap, with a flag to turn identity keeping strategy on. |
On Thu, Sep 17, 2015 at 3:09 AM, Mathieu ANCELIN notifications@github.com
So tracking identities is way harder (in terms of code complexity as well From my point of view the other frameworks are buggy in this behavior. This
Correct REST use case is a good example, and there track-by should be used, But you can provide optimized example (in the optimized version section)
We are having a lot of discussion internally about this. Should we do what
|
Hi @mhevery, I've pushed a new data generator with same identities for rows (I hope the code is right, if you can review it) also I've added optimized Angular 1 & 2 version here http://mathieuancelin.github.io/js-repaint-perfs/angular/opt.html |
Hi @mathieuancelin, The numbers look a lot better, but it is not the whole story. Currently c7 and Angular2 test case are limited by the browser. In both cases the rendering dominates, and browser will not render faster then 60 fps. So what you are measuring now is that both tests complete faster the browser can render, it does not actually answer the question of which one is faster. (These charts are done using Web Tracing Framework) Here is what it looks like for C7: The green bars are frames. The purple large bar is the setTimeout which updates the DOM and the blue short bar is the request animation frame which updates the fps rate in the corner of the screen. Notice that the a large amount of white between the bars. That is the browser doing rendering. It is what limits your speed. Even if the purple bars would get smaller the whites are already dominating. On average it took 7.148ms to execute the purple bar, which is the NOTE: the zoom level is different here, so you can't look at size of the bars, but the green bars are frames and they are still at 60fps, so use green bars as mile markers. Same story here. The browser is the limiting factor. An average it took 9.55ms to update the purple bar. So Angular 2 is slightly slower the C7 in this test. Let's have a look why. I will zoom into one of the purple bars. (NOTE: Angular hooks into WTF and the calls are not free which adds about 0.005 ms per call (600 calls) which in this case adds up to about 3ms. So if you subtract 3ms from 9.5ms you get about 6ms, which means Angular should be slightly faster then c7 with WTF off, but let's ignore this for a second) In the above graph what we see is that the purple setTimeout is broken into two parts. 1.) the purple bar with nothing below it. This is your call to My explanation is that the outer If you fix the second identity churn on inner |
Hi @mhevery, the last commit should do the trick : b86281e#diff-d843cdb1260fcc6789effbb70eb0a2bfR96 but I can't really try it as I can't reach 60 fps on my machine :-) |
Nice work sir! It is way faster! Here is what I am seeing. WTF: Angular 2 I see pure cyan color! this is great. It means that we are doing no structural changes, only DOM update. I get 3.67ms on each update This breaks down to 1.9ms for Angular which leaves 1.77ms for the So out of 3.8ms which is what it takes for the script to run (without WTF) Angular DOM update is 1.5 ms which leaves 2.3ms for the The point is that your application code is outweighing the test code, and as a result is dominating the time. Which means you are not measuring what you think you are measuring. WTF: t7 Shows that on average the update is 4.5ms. Already this is slower then Angular 2 which was 3.8ms. But remember that 2ms is So at the end we have c7 at 2.8ms and Angular 2 at 1.5ms. So Angular2 is 50% faster in this case. Now this is hard to see since at the Never the less. I get 80 fps on Angular 2 and 60 fps on t7 on my machine But the story is not done. Let's look at GC pressure. 1.08s between GC with 40MB going up to 48MB - GC - 40MB or 8MB per GC cycle. or a rate of 7.4mb per second. 2.85ms between GC with 18MB going up to 107MB -GC- 16MB or 90MB per GC or 32MB per second. As you can see Angular app generates lot less garbage and as a result the GC can run often with very little pause and memory pressure. |
That's great ! It's a shame that I can't reach the top frame rate, on my machine Angular 2 is rendering a 30/35 fps max according to Chrome Dev Tools. Do you think we can do something else to globally enhance the test ? |
Do you see the same ratios between t7 and angular2 as I do? We could make the generate function faster so that the difference in We could play with CSS so that it is easier for browser to paint. On Tuesday, September 22, 2015, Mathieu ANCELIN notifications@github.com
|
I've got something like :
Faster data generation would be great, if you have some ideas, please don't hesitate. |
Instead of doing complex loops and math.random, just have one function On Tue, Sep 22, 2015 at 11:45 AM, Mathieu ANCELIN notifications@github.com
|
I have only looked at Angular2 and t7.
The two frameworks make different assumptions on how to interpret the change in data and as a result mutate the DOM in very different way, which then results in a material performance difference.
The mutation of the data happens in generateData() method. The generateData returns a new array with new items in it each time it runs. This has important implication. Because the rows in the returned array have different identities, there are two ways of interpreting the data:
Both strategies, look reasonable at first, until you try to repeat over user input elements, or try do do animation, then strategy 1 breaks.
Because of the above corner cases which are hard to debug (insertions at end works, but insertion in the middle does not, but only if user is typing and or animations are enabled), Angular uses the second strategy. In most real life applications this is not an issue, since it is very rare that the model is constantly changing the identity. In real life cases model either does not change or only changes values. If the model only changed values but not the object identity, then Angular would not delete/insert rows and the performance would be identical.
Which brings are to the last use case of track-by. There are cases where you want Angular to treat two objects with different identities as equivalent. For example calling the backend and getting a refreshed dataset from server would appear as new objects which would then animate the existing data away and animate new data in. In such a case you can specify a track by function, which allows Angular to recognize that two objects should be treated as equivalent and reuse the corresponding rows. This is why Angular 1 with track by performs better then Angular 2 without track by, because the track by causes Angular 1 to interpret the data in the #1 way. BTW, track-by is coming to Angular 2 as well, we just have not gotten to it.
The summary is that these perf tests do not compare apples to apples. Because generateData() method creates new data set each time, it is hard to say which strategy individual frameworks have chosen to implement when doing their updates. My suggestion would be to update generateData() method so that it updates the data in a way which keeps the identities same. This is a much more realistic case, in which case all of the frameworks will interpret the change in a same way.
The text was updated successfully, but these errors were encountered: