Skip to content

Reduce overhead running with Mocking activated but no patches applied #139

@nickrobinson251

Description

@nickrobinson251

A colleague shared this profile, showing the Mocking's get_alternate call was taking the majority of the time despite there being no @patch applied:

I often experience that Mocking, once it had been activated indirectly in the shell by running some tests that use mocking, has perf degradation when subsequently running non-mocked code (see profile).

Image

I wonder if we can reduce this overhead?

You can see there are two things taking the time:

  1. the haskey call, from every function that is marked @mock checking if there is a corresponding @patch
  2. the the macro expand -> current_logger_… call, from the @debug log message (possibly made worse by Improve Mocking debugging #128)

I wonder if we can avoid calling get_alternative at all if we're not iinside Mocking.apply?

e.g. Mocking.apply could set a global / ScopedValue PATCH_ENV_ACTIVE::Bool that can be checked alongside activated() to avoid calling get_alternate at all. (Ofc, would need benchmarking to prove this would reduce the overhead.)

--- a/src/mock.jl
+++ b/src/mock.jl
@@ -40,7 +40,7 @@ macro mock(expr)
     # When `Mocking.activated() == false` then Julia will optimize the
     # code below to have zero-overhead by only executing the original expression.
     result = quote
-        if $activated()
+        if $activated() && $PATCH_ENV_ACTIVE[]
             args_var = tuple($(args...))
             alternate_var = $get_alternate($target, args_var...; call_loc=$call_loc)
             if alternate_var !== nothing

--- a/src/patch.jl
+++ b/src/patch.jl
@@ -206,17 +206,21 @@ end
 # https://github.com/JuliaLang/julia/pull/50958
 if VERSION >= v"1.11.0-DEV.482"
     const PATCH_ENV = ScopedValue(PatchEnv())
-    with_active_env(body::Function, pe::PatchEnv) = with(body, PATCH_ENV => pe)
+    const PATCH_ENV_ACTIVE = ScopedValue(false)
+    with_active_env(body::Function, pe::PatchEnv) = with(body, PATCH_ENV => pe, PATCH_ENV_ACTIVE => true)

Or perhaps even just checking isempty on the patch env would do?

--- a/src/mock.jl
+++ b/src/mock.jl
@@ -40,7 +40,7 @@ macro mock(expr)
     # When `Mocking.activated() == false` then Julia will optimize the
     # code below to have zero-overhead by only executing the original expression.
     result = quote
-        if $activated()
+        if $activated() && $(!($isempty($PATCH_ENV[])))
             args_var = tuple($(args...))
             alternate_var = $get_alternate($target, args_var...; call_loc=$call_loc)
             if alternate_var !== nothing

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions