Repository navigation
Improve performance of accessing globals in scripts running in vm modules #31658
Description
Activity
- addedperf_hooksIssues and PRs related to the perf_hooks module and performance measurement APIs.Issues and PRs related to the perf_hooks module and performance measurement APIs.vmIssues and PRs related to the vm subsystem.Issues and PRs related to the vm subsystem.
on Feb 6, 2020 There is nothing node can do to help performance here. The problem is that V8's global proxy design forces the embedder to indirect through c++ for every lookup, which adds a lot of overhead.
- addedv8 engineIssues and PRs related to the V8 dependency.Issues and PRs related to the V8 dependency.and removedperf_hooksIssues and PRs related to the perf_hooks module and performance measurement APIs.Issues and PRs related to the perf_hooks module and performance measurement APIs.
on Feb 6, 2020 And there's no way to cache/circumvent that lookup i node? If not, do you think there's any chance of v8 making changes so we can work around it?
Or are there any workarounds similar to the current workaround we can for
SourceTextModule(or whatever the ESM vm API ends up being)?@SimenB You could do
const context = createContext(); context.console = consoleinstead ofconst context = createContext({ console }).In the long term V8 might need to refactor this system anyway because of the upcoming realms API, so maybe one day
createContext({})will be fast too.Reacted by ExE Boss@devsnek I mentioned that in the OP - it takes the time from 1600ms to 800ms (so halves the time), but it's still 2 orders of magnitude slower than injecting it (which is less than 10ms).
I see no difference between
createContext({ someGlobal })andcreateContext().someGlobal = someGlobal- both are twice as fast as not assigning the global at all, but still suuuuper slow compared to running outside ofvmall together.@SimenB it seems that at some point we started creating the internal proxy unconditionally, which is why you're not seeing a speedup with what i suggested.
Using the
vm.Contextconstructor from #30709 also fixes this. Hopefully I can get that merged soon.Reacted by Simen Bekkhus, Andrei Karushev, ExE Boss, shitpoet and ErikAh, wonderful!
- addedperformanceIssues and PRs related to the performance of Node.js.Issues and PRs related to the performance of Node.js.
on Feb 7, 2020 I just compiled your branch and can confirm it does indeed fix the issue 😀
🤞 you're able to land it! Might be a bit premature, but do you think it'll be backported to v10 and v12? Since it's a different API graceful degradation should be fine, but the free performance boost for all release line would be wonderful (and Jest could remove the option we added to allow people to work around this)
Fixed on 4725ac6 :)
Sorry, @SimenB is this issue solved?
I don't think so, #30709 was closed unmerged?
Reacted by Juan José and ribxPlease land it, it would make me very happy 😀
Reacted by ExE Boss and C. T. LinAlso note that this issue is known as far back as 2016: nodejs/benchmarking#75 (comment).
Reacted by ExE Boss, Kirill A. Korinsky, bl-ue, shitpoet and ErikJust tidying up the issue tracker, and I think
vm.constants.DONT_CONTEXTIFYshould help eliminating the interceptor overhead - #62459 will make it the default when no argument is passed tovm.createContext(), too, though you could already use it now as far back as Node.js 20 (didnt' check but there might be lower versions with it too)github-actions commented
on Jul 20, 2026 on Jul 20, 2026 – with GitHub ActionsContributorMore actionsThis issue has been marked as stale due to 90 days of inactivity.
It will be automatically closed in 30 days if no further activity occurs. If this is still relevant, please leave a comment or update it to keep it open.- addedstaleIssues and PRs marked stale due to inactivity and scheduled for automatic closure.Issues and PRs marked stale due to inactivity and scheduled for automatic closure.
on Jul 20, 2026 This issue slipped through the cracks because our previous stale bot only tracked issues and couldn't catch all the issues.
Our new stale bot flagged this, and would have closed it shortly after RenderATL, but I'm just doing it a bit early so
maintainer's can focus on new code-and-learn PRs during the event.If this is still relevant, feel free to reopen it or leave a comment with additional details so we can continue the discussion.
Is your feature request related to a problem? Please describe.
Accessing globals from within a
vm.Scriptis guarded by interceptors which makes accessing them slow, (from my understanding, @fhinkel has an excellent comment explaining some of this here: jestjs/jest#5163 (comment)). The suggested workaround is to evaluate and cache the global you want access to (Mathin my example case below) and inject that into thevm.Script. This is an acceptable workaround forvm.Scriptandvm.compileFunctionbut it cannot be done when using ESMvm.SourceTextModuleas there is no wrapping function call.I've put together this script you can run to see the numbers:
Running this gives the following results on my machine:
So ~1600ms if not using the workaround, which reduces it to ~7ms.
Describe the solution you'd like
I'd like for accessing globals to be as fast, or as close to as possible, as fast as accessing globals outside of
vm. Would it be possible for Node'svmimplementation to cache the global property lookups, so that the price is only paid once instead of on every single access?Describe alternatives you've considered
As mentioned, caching and injecting the global manually works when there is a function wrapper, but this doesn't work with ESM. I've tried assigning the
Mathglobal to thecontext, and while that halves the time spent (~800ms), it's still 2 orders of magnitude slower than injecting a reference.