Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

perf(core): getType uses a cache for well known types. #12123

Open
wants to merge 1 commit into
base: dev
Choose a base branch
from

Conversation

Glandos
Copy link

@Glandos Glandos commented Jun 8, 2021

What kind of change does this PR introduce? (check at least one)

  • Bugfix
  • Feature
  • Code style update
  • Refactor
  • Build-related changes
  • Other, please describe: Performance

Does this PR introduce a breaking change? (check one)

  • Yes
  • No

If yes, please describe the impact and migration path for existing applications:

The PR fulfills these requirements:

If adding a new feature, the PR's description includes:

  • A convincing reason for adding this feature (to avoid wasting your time, it's best to open a suggestion issue first and wait for approval before working on it)

Other information:

getType is called a lot, and it just run the same regexp over and over on the same base types.
A cache increase its own efficiency by more than 80% for basic types.
On a real world application with a lot of components, getType was profiled for 8% of call duration before this patch, and about 0.5% after.
The impact is more limited for smaller applications.

Light application

Before:
Profile light without cache
After:
Profile light with cache

Heavy application

Before:
Profile heavy without cache
After:
Profile heavy with cache

Test was done on a laptop with Intel(R) Core(TM) i7-8650U CPU @ 1.90GHz, Chromium Version 90.0.4430.212

@posva
Copy link
Member

posva commented Jun 8, 2021

Thank you. Do you have a runnable sample in a single index.html file to check the perf gain?

@Glandos
Copy link
Author

Glandos commented Jun 8, 2021

No :( I performed the test on my company's application that I'm currently developing. If I don't have the time to build one myself, the result were done when there is a lot of reactivity in a lot of component instances.

@myfreeer
Copy link

Quick sample here:
image
image

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Vue 2 demo for https://github.com/vuejs/vue/pull/12123</title>
    <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
</head>
<body>
<button id="mount" type="button">click to mount</button>
<div id="app"></div>
<script>
    var exampleLength = 50000;
    var app = new Vue(({
        render(h) {
            var children = []
            for (let i = 0, len = this.arr.length; i < len; i++) {
                children.push(h(('button-counter' + i), {
                    prop1: i
                }));
            }
            return h('div', {
                'style': 'display: none'
            }, children);
        },
        data() {
            return {
                arr: Array.from({length: exampleLength}, (_, i) => i)
            }
        }
    }));
    {
        for (let i = 0; i < exampleLength; i++) {
            Vue.component('button-counter' + i, {
                props: {
                    prop1: [Boolean, String, Object, Number],

                    // Basic type check (`null` and `undefined` values will pass any type validation)
                    propA: Number,
                    // Multiple possible types
                    propB: [String, Number],
                    // string
                    propC: {
                        type: String,
                        // required: true
                    },
                    // Number with a default value
                    propD: {
                        type: Number,
                        default: 100
                    },
                    // Object with a default value
                    propE: {
                        type: Object,
                        // Object or array defaults must be returned from
                        // a factory function
                        default() {
                            return { message: 'hello' }
                        }
                    },
                    // Custom validator function
                    propF: {
                        validator(value) {
                            // The value must match one of these strings
                            return ['success', 'warning', 'danger'].includes(value)
                        }
                    },
                    // Function with a default value
                    propG: {
                        type: Function,
                        // Unlike object or array default, this is not a factory function - this is a function to serve as a default value
                        default() {
                            return 'Default function'
                        }
                    }
                },
                render(h) {
                    return h('div', {})
                }
            })
        }
    }

    document.getElementById('mount').onclick = () => {
        if (console.timeStamp) console.timeStamp('mount-start');
        var time = Date.now();
        app.$mount(document.getElementById('app'))
        console.log('mount', Date.now() - time)
        if (console.timeStamp) console.timeStamp('mount-end');
    }

</script>
</body>
</html>

@Glandos
Copy link
Author

Glandos commented Dec 7, 2021

I am really new to Vue internals, but I'm wondering if the test-e2e failed because of this patch, or some other external cause. Is it useful to re-run them?

@leadingGo
Copy link

leadingGo commented Dec 7, 2021 via email

@ycw984512
Copy link

ycw984512 commented Dec 7, 2021 via email

@FFWLei
Copy link

FFWLei commented Dec 7, 2021 via email

@Glandos
Copy link
Author

Glandos commented Sep 20, 2022

Is there anything I can do to finalize this work? Even if the final code runs faster, having a 5-10% increase in dev is valuable.
And it seems it can also be ported to Vue 3 in https://github.com/vuejs/vue/blob/main/src/core/util/props.ts#L193

@myfreeer
Copy link

The getType function for vue 3 is here:

https://github.com/vuejs/core/blob/96eb7452548293c343613ab778248a5da9619f45/packages/runtime-core/src/componentProps.ts#L559-L562

And the code at https://github.com/vuejs/vue/blob/main/src/core/util/props.ts#L193 is currently for vue 2.7.x

@Havunen
Copy link

Havunen commented Mar 6, 2023

@Glandos Please get latest changes from the upstream and that should fix the error

getType is called a lot, and it just run the same regexp over and over
on the same base types.
A cache increase its own efficiency by more than 80% for basic types.
On a real world application with a lot of components,
getType was profiled for 8% of call duration before this patch,
and about 0.5% after.
The impact is more limited for smaller applications.
@Glandos
Copy link
Author

Glandos commented Mar 6, 2023

@Havunen I've rebased against dev branch, as this PR was opened against it, but I see that the most active branch is now main. Should I try to reopen this against main instead?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants