We were unable to load Disqus. If you are a moderator please see our troubleshooting guide.
Unfortunately, the filter doesn't work. If we have the expression like: `item in items | immutable`, each time Angular evaluates it, the `toJS` method of `items` will be called. This means that we will get a new reference, which will make the `$digest` loop to fail (run infinitely).
Using filter might lead to additional calls of `toJS`. However, I don't think it will have any significant (if any) slowdown, so it sounds like a good idea!
I haven't used this strategy with larger data than the one in the sample benchmark. Definitely the garbage collector will be busy if you deal with huge amount of data and change it frequently. As @James stated, the difference between the memory usage wasn't that significant but you can only be sure when you profile it in your specific case.
Filter seems like a good approach as long as it doesn't cause other issues. Good suggestion!
Nice article, thanks!
very useful, thanks!
Thank you very much for the article. Did you also make a benchmark with the normal list and the bind once syntax?
<li ng-repeat="item in ::list" ng-bind="item"></li>
@Volker, bind once is not applicable in this case, because we want to change the collection, which should reflect on the UI.
With bind once, the collection will be rendered only a single time, after that all changes made in the loop (the recursive call of changeCollection) this will not reflect the unordered list.
Ok. I understood it the way, that your directive is doing this anyway?!
`ng-repeat` is implemented to work with JavaScript built-in arrays and hashes (objects). Since Immutable.js' List creates a wrapper around the built-in array object we need an adapter, which provides the appropriate object to the `ng-repeat` directive. `immutable="list"` means that the immutable value over which we want to iterate is the `list` property of the scope.
Once `immutable` (the directive), knows that this is the immutable property, it invokes `.toJS`, which returns the plain JavaScript object/array, over which `ng-repeats` knows how to iterate.
Okay, thank you for clearing this out
Hello.
Thank you for the interesting insight.
I have a question. It is for a long time now that Angular has controllerAs and it is advised to use it as opposed to $scope. Seems like the directive does not work with controllerAs.
Am I missing something? Or are you planning to add the support for controllerAs?
Yes, in this case the directive will not work. At this point I don't have plans for extending its functionality since Angular 2 is close to official release and it brings support for iteration over immutable objects.
It seems like a good thing that you have shared this kind of information in order for them to gain something that will be helpful and useful on their part as a student. Thus, those people who have a job related to this will also gain a lot of things from it.
Interesting article and it is nice to have numbers. Thank you!
I am trying to compare performance of rendering matrix of cells (600 rows, 30 cols) with ng-repeat and when matrix is immutable List of immutable Lists, and with immutable data structures performance is significantly worse (800ms vs 200ms). Here is my template:
<div immutable="immutableCells" ng-repeat="row in immutableCells track by $index" class="row">
<div immutable="immutableCells.get($index)" ng-repeat="cell in row track by $index" class="cell">{{cell.index}}</div>
</div>
Changing template to:
<div immutable="immutableCells" ng-repeat="row in immutableCells track by $index" class="row">
<div ng-repeat="cell in row track by $index" class="cell">{{cell.index}}</div>
</div>
makes things worse (900ms vs 200ms) as expected (immutable version is faster by 100ms).
It seems that although immutable data structures are fast, app can slow down instead of speeding up because of conversion to plain JS objects.
Do you have any thoughts about improving performance in the case of nested ng-repeats?
Excellent article, thanks for the good read and thorough insight.
Thanks for the interesting research. I'd like to make sure I understand your example fully:
<li ng-repeat="item in list | immutable" ng-bind="item">
The performance improvement comes from the fact that, during every $digest cycle, the watched expression always returns a different value, regardless of whether the value of "list" on the scope changed or not. This is because the expression 'list | immutable' is evaluated each time which will always return a different value, since under the hood toJS() always returns a new object. So the fact that you always have a different value, regardless of whether "list" actually changed or not, guarantees that you always have very quick strict equality checking. If this wasn't the case (and somehow toJS() returned the same object if the immutable has not changed since toJS() was last called) this approach would offer no benefit in the case where the "list" value does not change, as a shallow check of every item in the returned array would need to be performed. But surely the downside is that the listener will always be triggered even when "list" did not actually change?
In fact, looking at the implementation of $watchCollection, it doesn't seem to do a strict equality test of the watched collection in any case. So I'm confused as to where the performance benefit comes from.and how you move to O(1) performance when using immutables with watchCollection.
`ng-repeat` adds only one of many watchers. You can take a look at the second part of the series, which explains the optimization further.
Most of the watchers to the expression are added by explicitly calling `$watch` (in the case of expression, which is evaluated to immutable collection) or `$watchCollection`. Since when an immutable collection "changes" (i.e. we apply mutation to it) we get a nice immutable collection, we get a new reference. This means that we can add watchers to these expressions by using `$watch(expr, fn, false)`, which on the other hand performs a check with O(1).
Thanks for pointing me to the second article. So it seems you are saying that in order to extract the value from using immutables, you need to exclusively use $watch(expr, fn, false), rather than $watchCollection. I agree with you on this. I still see issues with the filter you developed - I will move the conversation to that github page. Thanks.
Which version of Angular was this benchmarked against? 1.4 has a performance improvement (it's only in RC right now), and I am wondering how much faster would it be.
The performance improvements should not have any significant impact on the benchmarks above. In all cases you need to perform a loop over the entire watched collection, when you add watcher with `$watchCollection`.
I took a look at the change log but didn't find anything, which states there're performance improvements in the watchers, can you please provide a reference?
PS: for further benchmarks checkout part two here http://blog.mgechev.com/2015/0...
I appreciate the reply. Supposedly there is a 30% improvement increase on the digest which I would expect some kind of improvement, although I would not expect it to beat the immutable. I saw it in a slide in a video somewhere, but I found it here too: https://scotch.io/bar-talk/the...
Great article! Love the experimentation and research. Thanks for taking the time to not only write this, but also open sourcing and packaging it up with Bower.
I do have admit, however, that given the shiny-ness of these new immutable approaches, I wish your findings would have shown better outcomes for collection sizes less than 10K. What uses cases do you typically run into with collections that large in your Angular apps?
The benchpress script is already available. You can check it out here https://github.com/mgechev/ben...
Tonight, I'll run the cartesian product of the following arrays:
`dataSizes = [5, 10, 20, 50, 100, 500, 1000, 2000, 5000, 10000, 100000]`
`bindingsCount = [5, 10]`
You can customize the script for your needs by changing the arrays in these two lines https://github.com/mgechev/ben...
Thank you!
Haven't tried it with smaller collections yet (less than 100 elements). I plan to do more benchmarks with benchpress soon. When I'm done I will post link to the github repo with the benchpress' config in order to allow developers to run custom benchmarks, which correspond better to their applications.
Is there a starting guide for benchpress? Let me know if you had time to work on the benchpress that allows custom benchmarks.
I'm not aware of the existence of a benchpress guide.
I'd recommend you to take a look at https://github.com/mgechev/ben... and https://github.com/jeffbcross/...
Cool, thanks! :)
Interesting article (and nice Github project). Have a star. I look forward to experimenting with this. :)
Perhaps I am reading it incorrectly, but doesn't your memory dump show the plain JS array (32.0-59.8) using more memory than the immutable list (34.4-50.2)? They are on different scales, so it is hard to compare garbage collection frequency and whether this approach may affect animations.
You're correct, the memory usage doesn't differ that much because of the way immutable.js is implemented. They use persistent data structures, which optimize the memory usage/computation resources.
Great article. Thanks for diving deep into this and posting numbers. I'm curious what the seemingly small impact of the memory overhead will look like in an application with tens of large immutable data structures. Have you used this in a more realistic application?
Also, have you tried to use a filter instead of a directive for angular-immutable? There might be a problem I haven't thought of, but it wouldn't require your directive's watcher and wouldn't require the collection variable to be declared twice.
example:
ng-repeat="item in list | immutable"