{"action":"created","issue":{"url":"https://api.github.com/repos/mathieuancelin/js-repaint-perfs/issues/19","labels_url":"https://api.github.com/repos/mathieuancelin/js-repaint-perfs/issues/19/labels{/name}","comments_url":"https://api.github.com/repos/mathieuancelin/js-repaint-perfs/issues/19/comments","events_url":"https://api.github.com/repos/mathieuancelin/js-repaint-perfs/issues/19/events","html_url":"https://github.com/mathieuancelin/js-repaint-perfs/issues/19","id":106808654,"number":19,"title":"Change generateData() to preserve object identity.","user":{"login":"mhevery","id":111951,"avatar_url":"https://avatars.githubusercontent.com/u/111951?v=3","gravatar_id":"","url":"https://api.github.com/users/mhevery","html_url":"https://github.com/mhevery","followers_url":"https://api.github.com/users/mhevery/followers","following_url":"https://api.github.com/users/mhevery/following{/other_user}","gists_url":"https://api.github.com/users/mhevery/gists{/gist_id}","starred_url":"https://api.github.com/users/mhevery/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mhevery/subscriptions","organizations_url":"https://api.github.com/users/mhevery/orgs","repos_url":"https://api.github.com/users/mhevery/repos","events_url":"https://api.github.com/users/mhevery/events{/privacy}","received_events_url":"https://api.github.com/users/mhevery/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":1,"created_at":"2015-09-16T16:19:54Z","updated_at":"2015-09-17T16:43:45Z","closed_at":null,"body":"I have only looked at Angular2 and t7. \r\n\r\nThe 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.\r\n\r\nThe 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:\r\n\r\n1. Ignore the identity and assume that first item in array is same as first item in the old array. This is what t7 does. Implication is that we just have to update the existing DOM structure with the new values and we are done.\r\n2. Look at old/new array and compute how the identities changed. This is what Angular 1 and 2 do. In this case the change is interpreted as all items left the array and new set of items were inserted. Angular will try to honor the removal/inserts and so it will remove all DOM rows for the items which were removed and then insert a new set of DOM rows for the new items back in. Angular 2 can do this relatively efficiently, because we can cache and reuse the rows. \r\n\r\nBoth strategies, look reasonable at first, until you try to repeat over user input elements, or try do do animation, then strategy 1 breaks. \r\n\r\n- Animation: reusing the row will not animate old row away and new one in. At best it could animate new rows additions or extra rows removal. The result is that inserting a new item in the front of the array will result in data jumping to next row (identities are not checked) and the last row is animated in. This does not reflect what actually happens in the model.\r\n- User Input: If the user is editing a form which has repeated rows, inserting a new row in the front will clobber all values including the user cursor and or selection.\r\n\r\nBecause 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.\r\n\r\nWhich 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.\r\n\r\nThe 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."},"comment":{"url":"https://api.github.com/repos/mathieuancelin/js-repaint-perfs/issues/comments/141144251","html_url":"https://github.com/mathieuancelin/js-repaint-perfs/issues/19#issuecomment-141144251","issue_url":"https://api.github.com/repos/mathieuancelin/js-repaint-perfs/issues/19","id":141144251,"user":{"login":"mhevery","id":111951,"avatar_url":"https://avatars.githubusercontent.com/u/111951?v=3","gravatar_id":"","url":"https://api.github.com/users/mhevery","html_url":"https://github.com/mhevery","followers_url":"https://api.github.com/users/mhevery/followers","following_url":"https://api.github.com/users/mhevery/following{/other_user}","gists_url":"https://api.github.com/users/mhevery/gists{/gist_id}","starred_url":"https://api.github.com/users/mhevery/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mhevery/subscriptions","organizations_url":"https://api.github.com/users/mhevery/orgs","repos_url":"https://api.github.com/users/mhevery/repos","events_url":"https://api.github.com/users/mhevery/events{/privacy}","received_events_url":"https://api.github.com/users/mhevery/received_events","type":"User","site_admin":false},"created_at":"2015-09-17T16:43:45Z","updated_at":"2015-09-17T16:43:45Z","body":"On Thu, Sep 17, 2015 at 3:09 AM, Mathieu ANCELIN <notifications@github.com>\nwrote:\n\n> Hi @mhevery <https://github.com/mhevery>\n>\n> I've changed the way data is generated, and indeed, angular performs way\n> better with.\n>\nWould love to see the numbers\n\n\n> But I'm not sure it's something I want to push because, it's an\n> optimization for Angular as angular is tracking object by their identities.\n>\n\nSo tracking identities is way harder (in terms of code complexity as well\nas CPU time) then assuming that array order is same. So Angular is trying\nto be more correct here so we can properly do animation and user\nintercations. We could change Angular to be positional by default and you\nwould have to opt-into the track by identities, but it feels wrong to me to\ndo it just to satisfy the benchmarks.\n\nFrom my point of view the other frameworks are buggy in this behavior. This\nis a situation where doing nothing/less is actually the wrong behavior.\n\n\n> The idea of this project is to provide naïve implementations (I'm not sure\n> every implementation is naïve, but I hope so). Keeping row identities is\n> not a real world use case, data returned by an REST service will never keep\n> row identities, and more important, it's not how a new user will use\n> Angular.\n>\n\nCorrect REST use case is a good example, and there track-by should be used,\nbut in case of REST, the REST call outweighs the cost of render by orders\nof magnitudes, so I am not sure it makes a material difference. I don't see\na use case where the rest calls are coming in at 50 fps and we need the UI\nto keep up with them. Any changes to model at 50 fps rate are timer driven\nnot REST driven.\n\nBut you can provide optimized example (in the optimized version section)\n> with identities and track by strategy when it's implemented.\n>\nThat is our plan.\n\nWe are having a lot of discussion internally about this. Should we do what\nis fast, even if it is sometimes wrong, or should we do what is correct by\ndefault. Do you have thoughts?\n\n\n> I'll try to push the new data generator asap, with a flag to turn identity\n> keeping strategy on.\n>\nThat would be awesome.\n\n\n> —\n> Reply to this email directly or view it on GitHub\n> <https://github.com/mathieuancelin/js-repaint-perfs/issues/19#issuecomment-141034239>\n> .\n>\n"}}