View Source Performance tips
Repatch
was built with performance in mind and it tries to keep the runtime overhead as thin as possible.
Here is a list of all actions you may take in order to improve performance of your tests.
While some of these tips are very simple to adopt, others may require rewrites of your tests. But in any way,
treat these as tips, not rules, because Repatch
must work correctly and efficiently without them.
Use setup with recompile option
When Repatch.patch/4
or Repatch.fake/3
is called on the target module for the first time, this module will be
recompiled. Depending on the size of the module, it may take some time. Therefore it is recommended to recompile these modules
before starting the test suite (during the Repatch.setup/1
call). It may not reduce overall test suite time, but it will
definitely help with having more consistent results when tracking slowest test cases.
Disable unused modes
Repatch
has optimizations to not lookup into the dispatch tables for modes which are disabled.
This advice also applies to async tests. If you use global
test mode in async, feel free to
disable shared
mode and then return shared
and disable global
during the on_exit
callback.
Prefer local to shared to global
In contrast to other tools, Repatch
was designed with async: true
in mind, that's why
shared and global modes perform more expensive lookups into shared memory, while local
one is the most efficient and simple.
Disable history
When module is recompiled, history is enabled for every function in this module. And by default history tracks calls to any function in the module from all processes. However, it is possible to track calls to the patched function manually.
For example
history_agent = Agent.start_link(fn -> [] end)
Repatch.patch(MapSet, :new, fn list ->
result = Repatch.super(MapSet.new(list))
Agent.update(history_agent, fn h -> [{MapSet, :new, [list], result} | h] end)
end)
MapSet.new([1])
MapSet.new([1, 2])
history = Agent.get(history_agent, &Function.identity/1)
assert Enum.any?(history, &match?({MapSet, :new, [1 | _], %MapSet{}}, &1))
Cleanup processes
If you're using processes which will outlive the test suite, and these processes call the patched modules, you should call Repatch.cleanup/1
on them from time to time to clean history.
Don't to patch kernel or recursive functions
That means if you have a function which returns result of Enum.reduce
call, try not to patch it and
patch the private/public function which calls it. Otherwise, every call to this function will hit disptacher
and will be slowed down. This tip is especially useful for recursive functions.
It also applies to Erlang stdlib functions and modules and frequently called libraries like :telemetry
Use recompile_only
and recompile_except
options
These options control the functions which are recompiled in the module. Functions, which are not recompiled are not affected by patches and their history is no tracked, but on the other hand they are not affected by additional runtime overhead like recompiled functions do.