惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

T
Tenable Blog
博客园_首页
Vercel News
Vercel News
WordPress大学
WordPress大学
美团技术团队
G
Google Developers Blog
大猫的无限游戏
大猫的无限游戏
小众软件
小众软件
Y
Y Combinator Blog
博客园 - 【当耐特】
量子位
酷 壳 – CoolShell
酷 壳 – CoolShell
The Cloudflare Blog
T
The Blog of Author Tim Ferriss
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Google DeepMind News
Google DeepMind News
云风的 BLOG
云风的 BLOG
腾讯CDC
M
MIT News - Artificial intelligence
爱范儿
爱范儿
Recent Announcements
Recent Announcements
雷峰网
雷峰网
Last Week in AI
Last Week in AI
宝玉的分享
宝玉的分享
The Register - Security
The Register - Security
Jina AI
Jina AI
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
Hugging Face - Blog
Hugging Face - Blog
P
Privacy & Cybersecurity Law Blog
Recorded Future
Recorded Future
Help Net Security
Help Net Security
N
News and Events Feed by Topic
博客园 - Franky
P
Proofpoint News Feed
L
LINUX DO - 热门话题
S
SegmentFault 最新的问题
The GitHub Blog
The GitHub Blog
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
月光博客
月光博客
D
Docker
Google DeepMind News
Google DeepMind News
有赞技术团队
有赞技术团队
IT之家
IT之家
Security Latest
Security Latest
L
LangChain Blog
V
V2EX
阮一峰的网络日志
阮一峰的网络日志
J
Java Code Geeks

Minko Gechev's blog

skillgrade Unit Tests for AI Agent Skills You Should Care About AI Generative Development LLM-first Web Framework Reactive framework in ~200 lines of JavaScript Managing Angular Are LLMs going to replace us? Prefetching Heuristics Design Patterns in Open Source Projects - Part II Design Patterns in Open Source Projects - Part I What I learned doing 125 public talks - Part I Dynamic imports solve all the problems, right? 5 Angular CLI Features You Didn't Know About Angular quicklink Preloading Strategy Introducing Bazel Schematics for Angular CLI Building TypeScript Projects with Bazel Joining Google Playing Mortal Kombat with TensorFlow.js. Transfer learning and data augmentation Fast, extensible, configurable, and beautiful linter for Go Introducing Guess.js - a toolkit for enabling data-driven user-experiences on the Web Machine Learning-Driven Bundling. The Future of JavaScript Tooling. JavaScript Decorators for Declarative and Readable Code 3 Tricks For Using Redux and Immutable.js with TypeScript Follow Your Dream Career with Open Source. Personal Story. Redux Anti-Patterns - Part 1. State Management. Faster Angular Applications - Understanding Differs. Developing a Custom IterableDiffer Faster Angular Applications - Part 2. Pure Pipes, Pure Functions and Memoization Faster Angular Applications - Part 1. On Push Change Detection and Immutability Understanding Dynamic Scoping and TemplateRef Implementing a Simple Compiler on 25 Lines of JavaScript Developing Statically Typed Programming Language WebVR for a Gamified IDE 7 Angular Tools That You Should Consider Announcing ngrev - Reverse Engineering Tool for Angular Implementing Angular's Dependency Injection in React. Understanding Element Injectors. Distributing an Angular Library - The Brief Guide Angular in Production Ahead-of-Time Compilation in Angular 2.5X Smaller Angular 2 Applications with Google Closure Compiler Using Stripe with Angular (Deprecated) Building an Angular Application for Production Implementing the Missing "resolve" Feature of the Angular 2 Router Scalable Single-Page Application Architecture Managing ambient type definitions and dealing with the "Duplicate identifier" TypeScript error Static Code Analysis of Angular 2 and TypeScript Projects Enforcing Best Practices with Static Code Analysis of Angular 2 Projects ViewChildren and ContentChildren in Angular Dynamically Configuring the Angular's Router Angular 2 Hot Loader Lazy Loading of Route Components in Angular 2 Aspect-Oriented Programming in JavaScript Flux in Depth. Store and Network Communication. Using JSX with TypeScript Flux in Depth. Overview and Components. Boost the Performance of an AngularJS Application Using Immutable Data - Part 2 Angular2 - First Impressions Build Your own Simplified AngularJS in 200 Lines of JavaScript Persistent State of ReactJS Component Boost the Performance of an AngularJS Application Using Immutable Data Processing Binary Protocols with Client-Side JavaScript Stream your Desktop to HTML5 Video Element Multi-User Video Conference with WebRTC Asynchronous calls with ES6 generators Binary Tree iterator with ES6 generators WebRTC chat with React.js AngularJS in Patterns (Part 3) AngularJS in Patterns (Part 2). Services. Using GitHub Pages with Jekyll! AngularJS in Patterns (Part 1). Overview of AngularJS Singleton in JavaScript Express over HTTPS What I get from the JavaScript MV* frameworks Remote Desktop Client with AngularJS and Yeoman The magic of $resource (or simply a client-side Active Record) AngularJS Inheritance Patterns AngularAOP v0.1.0 Advanced JavaScript at Sofia University AngularJS style guide Lazy prefetching of AngularJS partials VNC client on 200 lines of JavaScript Aspect-Oriented Programming with AngularJS CSS3 flipping effect Practical programming with JavaScript Why I should use publish/subscribe in JavaScript JavaScript, the weird parts Functional programming with JavaScript plainvm Looking for performance? Probably you should NOT use [].sort (V8) JavaScript image scaling ELang Caching CSS with localStorage Self-invoking functions in JavaScript (or Immediately Invoked Function Expressions) Asus N56VZ + Ubuntu 12.04 (en) Asus N56VZ + Ubuntu 12.04 Debian Squeeze + LXDE on Google Nexus S (or having some fun while suffering) HTML5 image editor Курсови проекти – ФМИ Carousel Gallery SofiaJS...
Even Faster AngularJS Data Structures
2015-04-20 · via Minko Gechev's blog

Edit · Apr 20, 2015 · 8 minutes read · Follow @mgechev Immutable.js JavaScript AngularJS Benchpress

This is the last post of the series “Boost the Performance of an AngularJS Application Using Immutable Data”. I strongly recommend you to take a look at the previous two parts before continue reading this content. You can find them at:

Introduction

Before about a month ago, I decided to experiment using immutable data structures in an AngularJS application. The goal behind my decision was quite simple - optimization of the $digest loop. How immutability could help? Immutable data cannot change after being created. The execution of each operation, which changes the immutable collection (add, delete, set) will create of a new data structure but will leave the initial data unchanged. This way we know that a watched expression, which evaluation results a collection, have changed if and only if its current reference differs from the previous one. This speeds up the change detection of the expression from O(n) to O(1), i.e. instead of watching collection with $watchCollection(expr, fn), we can afford $watch(expr, fn, false), instead.

So far so good, but creation of a new data structure will have two major performance impacts:

  • Copying the initial collection and making the change, before “freezing it”.
  • More work for the garbage collector.

In “Boost the Performance of an AngularJS Application Using Immutable Data - Part 2“we saw that the second point is not such a big concern but copying the entire data structure slows us down significantly.

The data structure described in this blog post could be found at my GitHub profile, it does further optimizations for even faster collection data bindings..

Optimization Options

In order to reduce the complexity of the code, which dirty checks the expression, from O(n) to something quite close to O(1), we have two main options:

  • Use $watch(expr, fn, false) - This way AngularJS will check for equality with ===, which is an operation with constant complexity.
  • Use $watchCollection(expr, fn) but reduce its running time. This means that we need to decrease the elements over which $watchCollection iterates.

$watch

The only way to take advantage of the first option is by receiving a new reference to the data structure once it changes or watch an internal property, which indicates a change. Unfortunately, we cannot change the value of the reference. What we can do is to create a wrapper of the native JavaScript collection and on each change to create a new wrapper, without copying the collection, i.e.:

let primitive = [1, 2, 3];
let list = new ListWrapper(primitive);
let changedList = list.append(4);
console.log(primitive === changedList); // false
// The primitive collection is changed
console.log(primitive); // [1, 2, 3, 4]

But this will also have one important side effect:

console.log(list.toJS()); // [ 1, 2, 3, 4]
console.log(changedList.toJS()); // [ 1, 2, 3, 4]

Keeping the same reference to the primitive list in both collections will cause change inside the underlaying data in both collections, once we perform a mutation operation. This solution looks like a dirty hack.

$watchCollection

How we can reduce the keys over which AngularJS’ dirty checker iterates? If we have a collection with 100,000,000 items, how to make AngularJS iterates only over a few of them in order to detect if the collection has changes? We will definitely need a wrapper but how to do the optimization?

AngularJS does not need to iterate over the entire collection but only need to check a control flag, which shows the change of the collection. What we can do is to have a wrapper, which keeps a reference to a standard JavaScript array. All operations over the wrapper will be forwarded to the array but the ones, which change the collection, will update the control flag, with a new value. On the next iteration of the $digest loop, AngularJS will check only the control flag and if it differs from its previous value the callbacks associated with the expression will be invoked.

But how we can limit AngularJS to watch only the control flag? We can do something like:

$watch(() => {
  return collection.isChanged;
}, cb, false);

There are three drawbacks of this approach:

  • We expose underlaying implementation details to the user of our library.
  • AngularJS needs to reset the isChanged flag once it detects a change. This requires changes in the AngularJS watch mechanism.
  • We can change isChanged outside the collection.

Lets take a look at AngularJS’ $watchCollection implementation:

// ...
for (key in newValue) {
  if (newValue.hasOwnProperty(key)) {
    newLength++;
    newItem = newValue[key];
    oldItem = oldValue[key];

    if (key in oldValue) {
      bothNaN = (oldItem !== oldItem) && (newItem !== newItem);
      if (!bothNaN && (oldItem !== newItem)) {
        changeDetected++;
        oldValue[key] = newItem;
      }
    } else {
      oldLength++;
      oldValue[key] = newItem;
      changeDetected++;
    }
  }
}
// ...

This part of the implementation is invoked, when the watched collection is not array-like. What we notice is that AngularJS iterates over the keys using for-in. In order to achieve complexity around O(1), we need to decrease the amount of keys over, which AngularJS will iterate and possibly leave only the change control flag as only key of the collection’ instances.

Here is how we achieve behavior result:

function defineProperty(obj, name, descriptor) {
  'use strict';
  Object.defineProperty(obj, name, descriptor);
}

function defineMethod(obj, name, method) {
  'use strict';
  defineProperty(obj, name, {
    enumerable: false,
    value: method
  });
}

function VersionableList(list) {
  'use strict';
  this._version = 0;
  Object.defineProperty(this, '_data', {
    enumerable: false,
    value: list || []
  });
}

'push pop shift unshift'.split(' ')
.forEach(function (key) {
  'use strict';
  defineMethod(VersionableList.prototype, key, function () {
    this._data[key].apply(this._data, arguments);
    this._updateVersion();
  });
});
defineMethod(VersionableList.prototype, 'set', function (idx, val) {
  this._data[idx] = val;
  this._updateVersion();
});

There are three main parts of the snippet above:

  • The way we take advantage of non-enumerable properties
  • The enumerable _version property
  • The way we update the version of the list once it has mutated

The update method does not have to do something complex, it can only increment the version property.

Here is sample usage of VersionableList:

let list = new VersionableList([1, 2]);

console.log(list.valueOf()); // [1, 2]
console.log(list._version); // 0

list.push(3);

console.log(list.valueOf()); // [1, 2, 3]
console.log(list._version); // 1

And here are the keys over which AngularJS will iterate:

Object.keys(list); // ['_version']

Benchmark Results

I compared the performance of VersionableList versus Immutable.js list and the built-in JavaScript arrays with the following variables in mind:

  • Collection Size
  • Bindings Count

For running the benchmarks and visualizing the results in charts, I used benchpress with a custom data formatter. The code for the benchmarks could be found here.

We can explore the results in the following sections. The x-axis shows the bindings count and the y-axis shows the running time.

5 entries

The two big competitors in this benchmark are the VersionableList and the native JavaScript array. As we see the built-in JavaScript list performs just a little bit better than VersionableList. Immutable.js list is slower because of the overhead caused by copying the entire data structure on change.

10 entries

When having a data structure with 10 items the built-in JavaScript array and VersionableList have almost the same performance.

100 entries

The kicking-ass winner in this benchmark is the VersionableList. With higher amount of bindings, the immutable list performs better than the built-in JavaScript array.

1,000 entries

With 1k collection size the immutable list performs better than the built-in JavaScript array and is with almost constant running time (the bindings almost don’t impact the running time, since they have constant complexity). The VersionableList performs even better since it doesn’t require copying of the collection on change.

10,000 entries

The supreme champion is the VersionableList. The interesting fact here is that the immutable list performs just slightly worst than the VersionableList list, although on each change a new collection with 10,000 items is created a lot of operations for maintaining the persistent data structure are performed.

Further Optimization

There’s no way to go much further without making any optimizations in the AngularJS’ internal change detection. We may gain slight performance improvement if AngularJS doesn’t invoke hasOwnProperty in the $watchCollection dirty checking strategy, since the poor performance of the method. This will require us to change VersionableList to something “array-like” in order to enter the second case of $watchCollection’s interceptor.

Conclusion

Using Immutable data we can gain performance improvements in the running time of our AngularJS applications. By workarounding the biggest drawback of using immutable data - copying the entire data structure on change, we can reduce the running time even further. In both cases we should keep in mind that Immutable.js and VersionableCollections are both only wrappers around the standard collections. If you make any change in the collection items, AngularJS will not detect any change in the collection, which on their other hand won’t force update of your view.

As I suggested in the previous blog post, before performing any of the optimization listed do application specific benchmarks using the data, which your application will process. Sample benchpress configurations could be found here and here.

References

Parts of the series: