Based off our investigation, we can make the following conclusions:
-
Scripting: Angular spends less time Scripting than React
-
Memory: Angular utilizes less Heap with less memory volatility. We've also observed that running Angular results in fewer Major GCs.
-
Visuals: Angular processes updates more quickly from the time they are generated
This repo contains 2 projects with feature parity, one based on React v15 and one based on Angular 4. Aside from RxJS, there are no additional libraries. The Angular app was generated with the Angular CLI and the React app was generated with create-react-app. Performance Results were calculated for each using optimized production builds.
The purpose of these 2 projects is to compare Angular and React rendering performance in scenarios where there are many DOM elements updating extremely quickly, which is a common scenario in industries consuming real-time data feeds.
Each app instantiates a preset number of simple components (configured through
initialQuantity
property in app.component.ts and
App.js). Each component kicks off an RxJS Observable
data stream
that publishes every 50ms. Each component contains 2 bindings that update on each RxJS emit,
where the first binding represents the time interval in ms from the previous update to the
current and the 2nd binding contains the updated data it received from the Observable
.
- Close as many open apps as possible on the machine to prevent possible resource contention (CPU, Memory, I/O, ...) that could compromise test results.
- Prepare a fresh Chrome: Disable any relevant extensions, clear caches, close all other tabs/windows, etc.
- Run
npm install
from both Angular and React app project roots. - Set the
initialQuantity
property in both app.component.ts and App.js) to the desired number of controls you'd like to test. - Run production optimized builds of both apps from each project root
npm run build
- Serve up the generated assets using your favorite http-server for each project (one
project at a time). I use
npm install -g serve
serve -s build
- We are focused on update speed and not initial load so wait until the DOM loads and then run the Performance Test from Chrome Developer Tools for your test duration.
- For each test run, record the exact scripting time and total test duration.
- Make sure to start fresh for each test, close the page, clear the cache, clear the performance profiler.
We currently include results both stats and screenshots for 20 test runs found under test-results
- macOS Sierra (10.12.5)
- Chrome 58.0.3029.110 (64-bit)
- MacBook Pro (Retina, Mid 2012) 2.6 GHz i7
- 512gb SSD
- 16 GB DDR3
- nVidia GeForce GT 650M 1024 MB
- Test 1: 500 Components, 50 ms updates, ~30 seconds
- Test 2: 100 Components, 50 ms updates, ~30 seconds
- 5 Runs of each test per framework
- Take screenshot of profile output.
- Record total test duration, scripting duration, and percentage of total test duration spent Scripting. We use the percentage of the total time instead of an absolute number since the tooling makes it difficult to set an precise test duration.
We have identified 3 separate areas of concern regarding React performance:
- Scripting
- Memory
- Noticable delay in data freshness on the UI
Angular spends a smaller percentage of time scripting than React.
For Test 1 (500 Components), we found that React averaged 21% more time scripting than Angular.
For Test 2 (100 Components), we found that React averaged 92% more time scripting than Angular.
Components | Library | Avg - % of Test Time spent Scripting | Avg - Scripting Time (ms) | StdDev - % of Test Time spent Scripting |
---|---|---|---|---|
100 | Angular | 6.3% | 1,905 | 0.3% |
100 | React | 12.1% 🔺 | 3,669 | 0.5% |
100 | React v16 Alpha | 11.6% | 3,547 | 1.2% |
500 | Angular | 44.5% | 13,594 | 0.5% |
500 | React | 53.9% | 16,494 | 2.1% |
500 | React v16 Alpha | 58.7% 🔺 | 17,803 | 1.3% |
See the Test Results folder for screenshots of the entire result set.
TestId | Type | Components | Run # | Total Duration (ms) | Scripting (ms) | % of Test Time Spent Scripting |
---|---|---|---|---|---|---|
01 (img) | React v15 | 500 | 1 | 30,452 | 16,727 | 54.9% |
02 (img) | React v15 | 500 | 2 | 30,871 | 17,609 | 57.0% |
03 (img) | React v15 | 500 | 3 | 30,862 | 15,893 | 51.5% |
04 (img) | React v15 | 500 | 4 | 30,348 | 16,087 | 53.0% |
05 (img) | React v15 | 500 | 5 | 30,512 | 16,154 | 52.9% |
21 (img) | React v16 | 500 | 1 | 30,369 | 17,886 | 58.9% |
22 (img) | React v16 | 500 | 2 | 30,434 | 18,472 | 60.7% |
23 (img) | React v16 | 500 | 3 | 30,040 | 17,618 | 58.6% |
24 (img) | React v16 | 500 | 4 | 30,312 | 17,311 | 57.1% |
25 (img) | React v16 | 500 | 5 | 30,392 | 17,730 | 58.3% |
11 (img) | Angular | 500 | 1 | 30,451 | 13,750 | 45.2% |
12 (img) | Angular | 500 | 2 | 30,611 | 13,592 | 44.4% |
13 (img) | Angular | 500 | 3 | 31,021 | 13,694 | 44.1% |
14 (img) | Angular | 500 | 4 | 30,383 | 13,583 | 44.7% |
15 (img) | Angular | 500 | 5 | 30,369 | 13,349 | 44.0% |
TestId | Type | Components | Run # | Total Duration (ms) | Scripting (ms) | % of Test Time Spent Scripting |
---|---|---|---|---|---|---|
06 (img) | React v15 | 100 | 1 | 30,570 | 3,895 | 12.7% |
07 (img) | React v15 | 100 | 2 | 29,890 | 3,564 | 11.9% |
08 (img) | React v15 | 100 | 3 | 30,615 | 3,575 | 11.7% |
09 (img) | React v15 | 100 | 4 | 30,486 | 3,564 | 11.7% |
10 (img) | React v15 | 100 | 5 | 30,271 | 3,745 | 12.4% |
26 (img) | React v16 | 100 | 1 | 30,391 | 3,963 | 13.0% |
27 (img) | React v16 | 100 | 2 | 30,436 | 3,146 | 10.3% |
28 (img) | React v16 | 100 | 3 | 30,499 | 3,176 | 10.4% |
29 (img) | React v16 | 100 | 4 | 30,414 | 3,604 | 11.8% |
30 (img) | React v16 | 100 | 5 | 30,722 | 3,848 | 12.5% |
16 (img) | Angular | 100 | 1 | 30,084 | 1,986 | 6.6% |
17 (img) | Angular | 100 | 2 | 29,801 | 1,915 | 6.4% |
18 (img) | Angular | 100 | 3 | 30,257 | 1,956 | 6.5% |
19 (img) | Angular | 100 | 4 | 30,292 | 1,817 | 6.0% |
20 (img) | Angular | 100 | 5 | 30,406 | 1,851 | 6.1% |
Using RxJS timeInterval() as a simple visual representation of throughput, Angular components consistently show lower time intervals than React. Due to the single-threaded nature of JavaScript, the RxJS timeInterval (1st binding) is a simple visual representation of how quickly each framework can process a data update given a consistent number of microtasks on the event loop - the closer it is to the publisher's 50ms rate, the better.
Note that while this screenshot displays only a small lag through the time interval field, that lag is noticably amplified in a complex enterprise application where the event loop is further bogged down by many factors including UI component complexity, streaming data serialization/deserialization, and DOM events.
React on left. Note that the time interval for nearly all 100 React components display a time interval of 59 or above, while the Angular components are consistently lower and are closer to the 50ms rate that RxJS is configured to publish.