perf: Use @@toStringTag and Map for better perf
There are 2 macro optimizations:
- Use
instanceofrather thantoString, this is about 100% faster, and makes things more accurate. - Use
Maprather thanObject, this is about 30% faster.
However, I am not sure about isPromise. According to the name, it only want to judge whether it's a Promise or not. But current logic is to judge whether it's thennable. This might be a breaking change.
instanceof cannot work across <iframe> boundaries. Using Object.prototype.toString is a common alternative to determine the type of an object. This covers more use cases.
@Justineo Thanks. I hardly use iframe and did not think about them...
Use Symbol.toStringTag instead if possible, and it's even faster. However, I found the profiling time vary a lot on my local machine, is there some robot I could call out to profile?
You could consider setting up some benchmarks on platforms like jsbench.me.
Here is my benchmark example: https://jsbench.me/krm45pp7nd/1
And here is the result:
- Current: 2266万 ops/s ± 3.28%
- instanceof: 2.3亿 ops/s ± 1.22%
- @@toStringTag: 2.2亿 ops/s ± 2.1%
Looks good.
And here is benchmark comparation result, I only picked the result that is changed larger than 5% and rme is less than 1%:
I do not know which part is important, but it looks good to me, especially there are 3 cases that improves 20%+
· write ref, read computed (with effect) 7,683,786.00 0.0001 0.3473 0.0001 0.0002 0.0002 0.0003 0.0004 ±0.24% 3841893 [0.93x] ⇓
write ref, read computed (with effect) 8,265,654.35 0.0001 0.7701 0.0001 0.0001 0.0002 0.0003 0.0004 ±0.80% 4132828 (baseline)
· write ref, don't read 1000 computeds (without effect) 22,545,853.49 0.0000 0.4810 0.0000 0.0001 0.0001 0.0001 0.0003 ±0.30% 11272929 [1.06x] ⇑
write ref, don't read 1000 computeds (without effect) 21,235,565.75 0.0000 0.8568 0.0000 0.0001 0.0001 0.0001 0.0003 ±0.61% 10617785 (baseline)
· write ref, read 1000 computeds (with multiple effects) 13,229.05 0.0680 0.9690 0.0756 0.0739 0.1381 0.1837 0.3030 ±0.64% 6615 [0.91x] ⇓
write ref, read 1000 computeds (with multiple effects) 14,535.18 0.0650 0.2618 0.0688 0.0684 0.0814 0.0944 0.1181 ±0.15% 7268 (baseline)
· 1000 refs, read 1 computed (with effect) 57,845.38 0.0153 0.3155 0.0173 0.0177 0.0258 0.0286 0.0484 ±0.25% 28923 [0.94x] ⇓
1000 refs, read 1 computed (with effect) 61,730.02 0.0148 0.1841 0.0162 0.0166 0.0229 0.0245 0.0271 ±0.11% 30866 (baseline)
· write reactive map, don't read computed (invoked) 6,280,659.49 0.0001 0.0844 0.0002 0.0002 0.0003 0.0003 0.0005 ±0.10% 3140331 [1.36x] ⇑
write reactive map, don't read computed (invoked) 4,616,937.08 0.0001 0.3780 0.0002 0.0002 0.0004 0.0005 0.0007 ±0.46% 2308469 (baseline)
· write reactive map, read computed 3,317,786.00 0.0002 0.1023 0.0003 0.0003 0.0005 0.0005 0.0008 ±0.08% 1658893 [1.21x] ⇑
write reactive map, read computed 2,751,338.35 0.0003 0.2377 0.0004 0.0004 0.0007 0.0008 0.0010 ±0.35% 1375670 (baseline)
· write reactive map, don't read 1000 computeds (invoked) 5,989,028.00 0.0001 0.0963 0.0002 0.0002 0.0003 0.0004 0.0005 ±0.10% 2994514 [1.41x] ⇑
write reactive map, don't read 1000 computeds (invoked) 4,245,770.30 0.0001 1.2880 0.0002 0.0003 0.0004 0.0005 0.0007 ±0.84% 2122886 (baseline)
· 1 ref invoking 10 effects 3,628,439.27 0.0002 0.3025 0.0003 0.0003 0.0005 0.0005 0.0007 ±0.38% 1814220 [1.10x] ⇑
1 ref invoking 10 effects 3,289,414.00 0.0002 0.1333 0.0003 0.0003 0.0004 0.0005 0.0007 ±0.14% 1644707 (baseline)
· 1 ref invoking 100 effects 410,433.67 0.0023 0.2580 0.0024 0.0024 0.0028 0.0036 0.0048 ±0.12% 205217 [1.10x] ⇑
1 ref invoking 100 effects 374,439.98 0.0025 1.0689 0.0027 0.0027 0.0035 0.0042 0.0051 ±0.43% 187221 (baseline)
· update ref to trigger watchEffect (scheduled but not executed) 2,781,636.33 0.0002 0.1086 0.0004 0.0004 0.0007 0.0008 0.0010 ±0.10% 1390819 [1.06x] ⇑
update ref to trigger watchEffect (scheduled but not executed) 2,628,404.42 0.0003 0.1979 0.0004 0.0004 0.0007 0.0008 0.0009 ±0.13% 1314203 (baseline)
· read reactive obj property 14,950,800.00 0.0000 0.2837 0.0001 0.0001 0.0001 0.0002 0.0003 ±0.30% 7475400 [1.07x] ⇑
read reactive obj property 13,940,586.00 0.0000 0.2386 0.0001 0.0001 0.0001 0.0001 0.0003 ±0.24% 6970293 (baseline)
Size Report
Bundles
| File | Size | Gzip | Brotli |
|---|---|---|---|
| runtime-dom.global.prod.js | 100 kB (+144 B) | 38 kB (+27 B) | 34.3 kB (+55 B) |
| vue.global.prod.js | 158 kB (+142 B) | 57.8 kB (+33 B) | 51.5 kB (+54 B) |
Usages
| Name | Size | Gzip | Brotli |
|---|---|---|---|
| createApp (CAPI only) | 47.2 kB (+76 B) | 18.4 kB (+9 B) | 16.8 kB (+17 B) |
| createApp | 55.3 kB (+89 B) | 21.4 kB (+14 B) | 19.5 kB (+24 B) |
| createSSRApp | 59.4 kB (+91 B) | 23.1 kB (+17 B) | 21 kB (+13 B) |
| defineCustomElement | 60.2 kB (+91 B) | 22.9 kB (+11 B) | 20.9 kB (+31 B) |
| overall | 69.2 kB (+87 B) | 26.5 kB (+8 B) | 24.1 kB (+25 B) |
@vue/compiler-core
npm i https://pkg.pr.new/@vue/compiler-core@12491
@vue/compiler-dom
npm i https://pkg.pr.new/@vue/compiler-dom@12491
@vue/compiler-sfc
npm i https://pkg.pr.new/@vue/compiler-sfc@12491
@vue/compiler-ssr
npm i https://pkg.pr.new/@vue/compiler-ssr@12491
@vue/runtime-core
npm i https://pkg.pr.new/@vue/runtime-core@12491
@vue/reactivity
npm i https://pkg.pr.new/@vue/reactivity@12491
@vue/runtime-dom
npm i https://pkg.pr.new/@vue/runtime-dom@12491
@vue/server-renderer
npm i https://pkg.pr.new/@vue/server-renderer@12491
@vue/shared
npm i https://pkg.pr.new/@vue/shared@12491
vue
npm i https://pkg.pr.new/vue@12491
@vue/compat
npm i https://pkg.pr.new/@vue/compat@12491
commit: c46cd24
/ecosystem-ci run
📝 Ran ecosystem CI: Open
| suite | result | latest scheduled |
|---|---|---|
| language-tools | :x: failure | :x: failure |
| nuxt | :x: failure | :x: failure |
| pinia | :white_check_mark: success | :white_check_mark: success |
| primevue | :white_check_mark: success | :white_check_mark: success |
| quasar | :white_check_mark: success | :white_check_mark: success |
| radix-vue | :white_check_mark: success | :white_check_mark: success |
| router | :white_check_mark: success | :white_check_mark: success |
| test-utils | :white_check_mark: success | :white_check_mark: success |
| vant | :white_check_mark: success | :white_check_mark: success |
| vite-plugin-vue | :white_check_mark: success | :white_check_mark: success |
| vitepress | :white_check_mark: success | :white_check_mark: success |
| vue-i18n | :white_check_mark: success | :white_check_mark: success |
| vue-macros | :white_check_mark: success | :white_check_mark: success |
| vuetify | :white_check_mark: success | :white_check_mark: success |
| vueuse | :white_check_mark: success | :white_check_mark: success |
| vue-simple-compiler | :white_check_mark: success | :white_check_mark: success |
Use Map rather than Object, this is about 30% faster.
I got different performance data, see https://measurethat.net/Benchmarks/Show/32894/1/test-map-vs-object
@edison1105
here is my 5 cents about benchmark: https://measurethat.net/Benchmarks/Show/32902/0/test-map-vs-object-with-string-which-use-string-as-key
For set-value, the perf is similar, for get-value, Map is 200% faster(wow...) as shown in this case. If for the cache in Vue, the key is more frequently to be different with the key last time visited, then this test case might reflect real world better.
LGTM