viewmodel
                                
                                 viewmodel copied to clipboard
                                
                                    viewmodel copied to clipboard
                            
                            
                            
                        ViewModel decreases Blaze performance x3 ~ x10
@mitar built an UI benchmarking tool for Meteor. It included Blaze, vanilla JS, Blaze Components, Vue and React.
Blaze Components turned out to be really slow compared to plain Blaze.
I then created a fork of the tool with ViewModel included and found out ViewModel is even slower, in some tests a lot slower.
All frameworks compared:
Full results
[HMR] Vue 2.4.2
modules.js?hash=0b4346a…:8438 Download the Vue Devtools extension for a better development experience:
https://github.com/vuejs/vue-devtools
modules.js?hash=0b4346a…:8448 You are running Vue in development mode.
Make sure to turn on production mode when deploying for production.
See more tips at https://vuejs.org/guide/deployment.html
dev-client.js:88 [HMR] Dev client URL 10.0.1.2:3003
dev-client.js:95 [HMR] Dev client connected
favicon.ico:1 GET http://localhost:3000/favicon.ico 404 (Not Found)
main.js:265 Benchmark started using 2 loops. Mon Jul 24 2017 13:48:28 GMT+0300 (EEST)
main.js:243 blaze: null -> other: 3 ms
main.js:243 blaze: other -> table1: 2537 ms
main.js:243 blaze: table1 -> other: 1147 ms
main.js:243 blaze: other -> table1: 2344 ms
main.js:243 blaze: table1 -> other: 647 ms
main.js:243 blaze: other -> recursive: 8882 ms
main.js:243 blaze: recursive -> other: 4730 ms
main.js:243 blaze: other -> recursive: 13502 ms
main.js:243 blaze: recursive -> other: 3373 ms
main.js:243 blaze: other -> table1: 5856 ms
main.js:243 blaze: table1 -> table3: 9078 ms
main.js:243 blaze: table3 -> table1: 10642 ms
main.js:243 blaze: table1 -> table3: 4411 ms
main.js:243 blaze: table3 -> table1: 6167 ms
main.js:243 blaze: table1 -> table2: 8260 ms
main.js:243 blaze: table2 -> table1: 4394 ms
main.js:243 blaze: table1 -> table2: 6395 ms
main.js:243 blaze: table2 -> table1: 8327 ms
main.js:243 viewmodel: null -> other: 144 ms
main.js:243 viewmodel: other -> table1: 15761 ms
main.js:243 viewmodel: table1 -> other: 2607 ms
main.js:243 viewmodel: other -> table1: 16816 ms
main.js:243 viewmodel: table1 -> other: 2516 ms
main.js:243 viewmodel: other -> recursive: 104410 ms
main.js:243 viewmodel: recursive -> other: 16367 ms
main.js:243 viewmodel: other -> recursive: 105932 ms
main.js:243 viewmodel: recursive -> other: 14835 ms
main.js:243 viewmodel: other -> table1: 14592 ms
main.js:243 viewmodel: table1 -> table3: 16792 ms
main.js:243 viewmodel: table3 -> table1: 19754 ms
main.js:243 viewmodel: table1 -> table3: 17362 ms
main.js:243 viewmodel: table3 -> table1: 17412 ms
main.js:243 viewmodel: table1 -> table2: 19769 ms
main.js:243 viewmodel: table2 -> table1: 23314 ms
main.js:243 viewmodel: table1 -> table2: 19045 ms
main.js:243 viewmodel: table2 -> table1: 25242 ms
main.js:243 blaze-components: null -> other: 824 ms
main.js:243 blaze-components: other -> table1: 11025 ms
main.js:243 blaze-components: table1 -> other: 2054 ms
main.js:243 blaze-components: other -> table1: 9106 ms
main.js:243 blaze-components: table1 -> other: 2048 ms
main.js:243 blaze-components: other -> recursive: 32702 ms
main.js:243 blaze-components: recursive -> other: 15840 ms
main.js:243 blaze-components: other -> recursive: 29574 ms
main.js:243 blaze-components: recursive -> other: 13835 ms
main.js:243 blaze-components: other -> table1: 15389 ms
main.js:243 blaze-components: table1 -> table3: 14838 ms
main.js:243 blaze-components: table3 -> table1: 13041 ms
main.js:243 blaze-components: table1 -> table3: 15187 ms
main.js:243 blaze-components: table3 -> table1: 13378 ms
main.js:243 blaze-components: table1 -> table2: 16261 ms
main.js:243 blaze-components: table2 -> table1: 13427 ms
main.js:243 blaze-components: table1 -> table2: 17379 ms
main.js:243 blaze-components: table2 -> table1: 13973 ms
main.js:243 manual: null -> other: 1 ms
main.js:243 manual: other -> table1: 1252 ms
main.js:243 manual: table1 -> other: 74 ms
main.js:243 manual: other -> table1: 639 ms
main.js:243 manual: table1 -> other: 72 ms
main.js:243 manual: other -> recursive: 338 ms
main.js:243 manual: recursive -> other: 278 ms
main.js:243 manual: other -> recursive: 144 ms
main.js:243 manual: recursive -> other: 277 ms
main.js:243 manual: other -> table1: 1094 ms
main.js:243 manual: table1 -> table3: 912 ms
main.js:243 manual: table3 -> table1: 2317 ms
main.js:243 manual: table1 -> table3: 249 ms
main.js:243 manual: table3 -> table1: 228 ms
main.js:243 manual: table1 -> table2: 214 ms
main.js:243 manual: table2 -> table1: 210 ms
main.js:243 manual: table1 -> table2: 206 ms
main.js:243 manual: table2 -> table1: 213 ms
main.js:243 stateful-react: null -> other: 7 ms
main.js:243 stateful-react: other -> table1: 1920 ms
main.js:243 stateful-react: table1 -> other: 244 ms
main.js:243 stateful-react: other -> table1: 985 ms
main.js:243 stateful-react: table1 -> other: 229 ms
main.js:243 stateful-react: other -> recursive: 3778 ms
main.js:243 stateful-react: recursive -> other: 829 ms
main.js:243 stateful-react: other -> recursive: 2760 ms
main.js:243 stateful-react: recursive -> other: 852 ms
main.js:243 stateful-react: other -> table1: 865 ms
main.js:243 stateful-react: table1 -> table3: 1102 ms
main.js:243 stateful-react: table3 -> table1: 1092 ms
main.js:243 stateful-react: table1 -> table3: 1163 ms
main.js:243 stateful-react: table3 -> table1: 1086 ms
main.js:243 stateful-react: table1 -> table2: 1096 ms
main.js:243 stateful-react: table2 -> table1: 1088 ms
main.js:243 stateful-react: table1 -> table2: 1127 ms
main.js:243 stateful-react: table2 -> table1: 1152 ms
main.js:243 stateful-vue: null -> other: 5 ms
main.js:243 stateful-vue: other -> table1: 1077 ms
main.js:243 stateful-vue: table1 -> other: 1870 ms
main.js:243 stateful-vue: other -> table1: 936 ms
main.js:243 stateful-vue: table1 -> other: 1991 ms
main.js:243 stateful-vue: other -> recursive: 3111 ms
main.js:243 stateful-vue: recursive -> other: 10577 ms
main.js:243 stateful-vue: other -> recursive: 2875 ms
main.js:243 stateful-vue: recursive -> other: 12496 ms
main.js:243 stateful-vue: other -> table1: 1246 ms
main.js:243 stateful-vue: table1 -> table3: 6065 ms
main.js:243 stateful-vue: table3 -> table1: 7207 ms
main.js:243 stateful-vue: table1 -> table3: 6312 ms
main.js:243 stateful-vue: table3 -> table1: 7163 ms
main.js:243 stateful-vue: table1 -> table2: 6667 ms
main.js:243 stateful-vue: table2 -> table1: 7304 ms
main.js:243 stateful-vue: table1 -> table2: 6757 ms
main.js:243 stateful-vue: table2 -> table1: 6760 ms
main.js:243 stateless-vue: null -> other: 12 ms
main.js:243 stateless-vue: other -> table1: 400 ms
main.js:243 stateless-vue: table1 -> other: 66 ms
main.js:243 stateless-vue: other -> table1: 357 ms
main.js:243 stateless-vue: table1 -> other: 61 ms
main.js:243 stateless-vue: other -> recursive: 1228 ms
main.js:243 stateless-vue: recursive -> other: 231 ms
main.js:243 stateless-vue: other -> recursive: 591 ms
main.js:243 stateless-vue: recursive -> other: 229 ms
main.js:243 stateless-vue: other -> table1: 1188 ms
main.js:243 stateless-vue: table1 -> table3: 377 ms
main.js:243 stateless-vue: table3 -> table1: 722 ms
main.js:243 stateless-vue: table1 -> table3: 346 ms
main.js:243 stateless-vue: table3 -> table1: 363 ms
main.js:243 stateless-vue: table1 -> table2: 367 ms
main.js:243 stateless-vue: table2 -> table1: 356 ms
main.js:243 stateless-vue: table1 -> table2: 352 ms
main.js:243 stateless-vue: table2 -> table1: 463 ms
main.js:286 Benchmark ended. Mon Jul 24 2017 14:11:06 GMT+0300 (EEST)
main.js:292 Computing minimongo baseline. Mon Jul 24 2017 14:11:06 GMT+0300 (EEST)
main.js:301 Done. Mon Jul 24 2017 14:11:06 GMT+0300 (EEST)
main.js:305 Result minimongo 106
main.js:321 Result blaze other -> table1 2440.5 2.581173982020095
main.js:321 Result blaze table1 -> other 897 12.287671232876713
main.js:321 Result blaze other -> recursive 11192 46.439834024896264
main.js:321 Result blaze recursive -> other 4051.5 14.6
main.js:321 Result blaze table1 -> table3 6744.5 11.618432385874247
main.js:321 Result blaze table3 -> table1 8404.5 6.604715127701375
main.js:321 Result blaze table1 -> table2 7327.5 34.892857142857146
main.js:321 Result blaze table2 -> table1 6360.5 30.073286052009458
main.js:321 Result viewmodel other -> table1 16288.5 17.22739291380222
main.js:321 Result viewmodel table1 -> other 2561.5 35.08904109589041
main.js:321 Result viewmodel other -> recursive 105171 436.3941908713693
main.js:321 Result viewmodel recursive -> other 15601 56.21981981981982
main.js:321 Result viewmodel table1 -> table3 17077 29.417743324720067
main.js:321 Result viewmodel table3 -> table1 18583 14.603536345776032
main.js:321 Result viewmodel table1 -> table2 19407 92.41428571428571
main.js:321 Result viewmodel table2 -> table1 24278 114.78959810874704
main.js:321 Result blaze-components other -> table1 10065.5 10.645690111052353
main.js:321 Result blaze-components table1 -> other 2051 28.095890410958905
main.js:321 Result blaze-components other -> recursive 31138 129.2033195020747
main.js:321 Result blaze-components recursive -> other 14837.5 53.468468468468465
main.js:321 Result blaze-components table1 -> table3 15012.5 25.86132644272179
main.js:321 Result blaze-components table3 -> table1 13209.5 10.38074656188605
main.js:321 Result blaze-components table1 -> table2 16820 80.0952380952381
main.js:321 Result blaze-components table2 -> table1 13700 64.77541371158392
main.js:321 Result manual other -> table1 945.5 1
main.js:321 Result manual table1 -> other 73 1
main.js:321 Result manual other -> recursive 241 1
main.js:321 Result manual recursive -> other 277.5 1
main.js:321 Result manual table1 -> table3 580.5 1
main.js:321 Result manual table3 -> table1 1272.5 1
main.js:321 Result manual table1 -> table2 210 1
main.js:321 Result manual table2 -> table1 211.5 1
main.js:321 Result stateful-react other -> table1 1452.5 1.5362242199894236
main.js:321 Result stateful-react table1 -> other 236.5 3.23972602739726
main.js:321 Result stateful-react other -> recursive 3269 13.564315352697095
main.js:321 Result stateful-react recursive -> other 840.5 3.028828828828829
main.js:321 Result stateful-react table1 -> table3 1132.5 1.950904392764858
main.js:321 Result stateful-react table3 -> table1 1089 0.8557956777996071
main.js:321 Result stateful-react table1 -> table2 1111.5 5.292857142857143
main.js:321 Result stateful-react table2 -> table1 1120 5.295508274231678
main.js:321 Result stateful-vue other -> table1 1006.5 1.064516129032258
main.js:321 Result stateful-vue table1 -> other 1930.5 26.445205479452056
main.js:321 Result stateful-vue other -> recursive 2993 12.41908713692946
main.js:321 Result stateful-vue recursive -> other 11536.5 41.57297297297297
main.js:321 Result stateful-vue table1 -> table3 6188.5 10.660637381567614
main.js:321 Result stateful-vue table3 -> table1 7185 5.6463654223968565
main.js:321 Result stateful-vue table1 -> table2 6712 31.961904761904762
main.js:321 Result stateful-vue table2 -> table1 7032 33.248226950354606
main.js:321 Result stateless-vue other -> table1 378.5 0.40031729243786357
main.js:321 Result stateless-vue table1 -> other 63.5 0.8698630136986302
main.js:321 Result stateless-vue other -> recursive 909.5 3.7738589211618256
main.js:321 Result stateless-vue recursive -> other 230 0.8288288288288288
main.js:321 Result stateless-vue table1 -> table3 361.5 0.6227390180878553
main.js:321 Result stateless-vue table3 -> table1 542.5 0.4263261296660118
main.js:321 Result stateless-vue table1 -> table2 359.5 1.7119047619047618
main.js:321 Result stateless-vue table2 -> table1 409.5 1.9361702127659575
 
Detailed comparison of just Blaze and ViewModel:
Full results
[HMR] Vue 2.4.1
akryum_vue-component-dev-client.js:174 [HMR] Dev client URL 10.0.1.3:4003
modules.js:8417 Download the Vue Devtools extension for a better development experience:
https://github.com/vuejs/vue-devtools
modules.js:8427 You are running Vue in development mode.
Make sure to turn on production mode when deploying for production.
See more tips at https://vuejs.org/guide/deployment.html
akryum_vue-component-dev-client.js:181 [HMR] Dev client connected
main.js:257 Benchmark started. Mon Jul 24 2017 14:38:22 GMT+0300 (EEST)
main.js:238 blaze: null -> other: 2 ms
main.js:238 blaze: other -> table1: 2633 ms
main.js:238 blaze: table1 -> other: 1066 ms
main.js:238 blaze: other -> table1: 3838 ms
main.js:238 blaze: table1 -> other: 702 ms
main.js:238 blaze: other -> table1: 4021 ms
main.js:238 blaze: table1 -> other: 1153 ms
main.js:238 blaze: other -> recursive: 13006 ms
main.js:238 blaze: recursive -> other: 5486 ms
main.js:238 blaze: other -> recursive: 16816 ms
main.js:238 blaze: recursive -> other: 3731 ms
main.js:238 blaze: other -> recursive: 16692 ms
main.js:238 blaze: recursive -> other: 6881 ms
main.js:238 blaze: other -> table1: 11134 ms
main.js:238 blaze: table1 -> table3: 5288 ms
main.js:238 blaze: table3 -> table1: 7305 ms
main.js:238 blaze: table1 -> table3: 9565 ms
main.js:238 blaze: table3 -> table1: 5145 ms
main.js:238 blaze: table1 -> table3: 7301 ms
main.js:238 blaze: table3 -> table1: 9281 ms
main.js:238 blaze: table1 -> table2: 5199 ms
main.js:238 blaze: table2 -> table1: 7197 ms
main.js:238 blaze: table1 -> table2: 9419 ms
main.js:238 blaze: table2 -> table1: 5161 ms
main.js:238 blaze: table1 -> table2: 7414 ms
main.js:238 blaze: table2 -> table1: 9448 ms
main.js:238 viewmodel: null -> other: 151 ms
main.js:238 viewmodel: other -> table1: 15573 ms
main.js:238 viewmodel: table1 -> other: 3213 ms
main.js:238 viewmodel: other -> table1: 17095 ms
main.js:238 viewmodel: table1 -> other: 3182 ms
main.js:238 viewmodel: other -> table1: 16375 ms
main.js:238 viewmodel: table1 -> other: 3753 ms
main.js:238 viewmodel: other -> recursive: 106261 ms
main.js:238 viewmodel: recursive -> other: 17294 ms
main.js:238 viewmodel: other -> recursive: 99806 ms
main.js:238 viewmodel: recursive -> other: 13226 ms
main.js:238 viewmodel: other -> recursive: 99385 ms
main.js:238 viewmodel: recursive -> other: 15117 ms
main.js:238 viewmodel: other -> table1: 13720 ms
main.js:238 viewmodel: table1 -> table3: 16988 ms
main.js:238 viewmodel: table3 -> table1: 19877 ms
main.js:238 viewmodel: table1 -> table3: 20706 ms
main.js:238 viewmodel: table3 -> table1: 27242 ms
main.js:238 viewmodel: table1 -> table3: 18666 ms
main.js:238 viewmodel: table3 -> table1: 18674 ms
main.js:238 viewmodel: table1 -> table2: 20651 ms
main.js:238 viewmodel: table2 -> table1: 26188 ms
main.js:238 viewmodel: table1 -> table2: 17672 ms
main.js:238 viewmodel: table2 -> table1: 20641 ms
main.js:238 viewmodel: table1 -> table2: 23957 ms
main.js:238 viewmodel: table2 -> table1: 20232 ms
main.js:278 Benchmark ended. Mon Jul 24 2017 14:55:10 GMT+0300 (EEST)
main.js:284 Computing minimongo baseline. Mon Jul 24 2017 14:55:10 GMT+0300 (EEST)
main.js:293 Done. Mon Jul 24 2017 14:55:12 GMT+0300 (EEST)
main.js:297 Result minimongo 496
main.js:313 Result blaze other -> table1 3497.3333333333335 NaN
main.js:313 Result blaze table1 -> other 973.6666666666666 NaN
main.js:313 Result blaze other -> recursive 15504.666666666666 NaN
main.js:313 Result blaze recursive -> other 5366 NaN
main.js:313 Result blaze table1 -> table3 7384.666666666667 NaN
main.js:313 Result blaze table3 -> table1 7243.666666666667 NaN
main.js:313 Result blaze table1 -> table2 7344 NaN
main.js:313 Result blaze table2 -> table1 7268.666666666667 NaN
main.js:313 Result viewmodel other -> table1 16347.666666666666 NaN
main.js:313 Result viewmodel table1 -> other 3382.6666666666665 NaN
main.js:313 Result viewmodel other -> recursive 101817.33333333333 NaN
main.js:313 Result viewmodel recursive -> other 15212.333333333334 NaN
main.js:313 Result viewmodel table1 -> table3 18786.666666666668 NaN
main.js:313 Result viewmodel table3 -> table1 21931 NaN
main.js:313 Result viewmodel table1 -> table2 20760 NaN
main.js:313 Result viewmodel table2 -> table1 22353.666666666668 NaN

As you can see, ViewModel's results seem to be about 4x-5x larger than those of plain Blaze (for example, rendering table1, Blaze takes about ~3500ms, ViewModel variant takes ~ 16000ms).
However, the biggest difference is in the recursive test, in which Blaze succeeds in approximately 16 000ms, ViewModel takes a whopping ~100 000 ms.
Profiling the execution with Chrome's Dev Tools Performance tab, there seems to be a few time hogs, one related to firing a lot of timers.
Also, this isn't just a benchmarking problem. I had a real world app with a dynamic table with typically ~100 rows. First draft used ViewModel, with approximate rendering time of 16 seconds. After refactoring to pure Blaze the rendering time dropped to about 3-4 seconds.
Maybe there's a low hanging fruit with a tenfold speed improvement for ViewModel in some scenarios?
Yeah, there has to be plenty of low hanging fruit since I've spent exactly 0s optimizing VM =/
Thank you for setting this up! I really appreciate it.
+1 this, any improvements you can do Manuel are greatly appreciated!
I have a very large app running Blaze and Viewmodel, most of the time it is not an issue, but sometimes I would enjoy some performance benefits. @ManuelDeLeon I don't know how much help I can be but could you point me towards some of those "low hanging fruits" and maybe I can create some pull requests?
I don't have anything in mind right now (haven't worked on the Blaze version in years). You can just create a page and repeat an object hundreds of times and see what the hot paths are. Repeat the same process for other things like bindings, handling events, etc.