Skip to content

[Perf] - Consider not GCing function environments which don’t need it #4259

@jasonwilliams

Description

@jasonwilliams

Describe the bug
I did some profiling on one of the benchmarks Richards. I don’t have the profile data to hand right now but around 20% of time was spent making function environments. A function environment is created on every function call.

The reason for the slow down is because every new function environment is wrapped in GC::New(), most of the GC congestion is coming from creating function environments. The GC eventually needs to collect, so has to stop the world and collect very often.

The benchmarks accentuate the problem because many functions are being called in a loop, so push_function is being called a lot.

Potential Solution

My question is, do we need to wrap all function environments in a GC? We need to keep them around if there’s re-entry (such as async or generators) or if we have a closure escape, but if a function has neither of these we could have a “fast path” where we bypass the GC wrapping.

We already perform escape analysis on functions at the AST level, but we don’t keep the result.

We could have some “is_reentry” field that is passed down into the code block to tell us if the function has any escape or any reentry. This boolean would be sent into push_function here

Then we can set how we want to store the environment inside of push_function This would require a refactor where we have an enum which represents an environment on the stack or in a GC:

#[derive(Clone, Trace, Finalize)]
pub enum EnvironmentPointer {
 NonGcAllocated(Box<DeclarativeEnvironment>),
 GcAllocated(Gc<DeclarativeEnvironment>),
}

Then used in the outer Environment enum as:

#[derive(Clone, Trace, Finalize)]
pub enum Environment {
    Declarative(EnvironmentPointer),
    Object(Gc<ObjectEnvironment>)
}

To Reproduce

  1. Checkout the boa/data repo. You can do a partial checkout of just the bench/bench-v8 subdirectory.
  2. build Boa with the release-dbg profile.
  3. in the Makefile from the data repo remove the tests but keep Richard’s, run Make
  4. Use flamegraph or valgrind and run Boa against combined.js

Expected behavior
We see less time spent in push_function an GC Contention.

Metadata

Metadata

Assignees

Labels

A-BugSomething isn't workingA-PerformancePerformance related changes and issues

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions