Comparisons / Benchmarks

Unique features

Current as of May 2025: I have investigated and benchmarked five other multiple/single dispatch libraries. Performance-wise, ovld is faster than all of them, ranging from 1.5x to 100x less overhead. Feature-wise, ovld is among the most featureful. Some features I could not find elsewhere:

  • Support for keyword arguments.
  • Variants, especially working with recursion.
  • call_next, to call down in the resolution order.
  • Proper performance when using Literal/dependent types.
  • Very easy definition of new types (it can also be done with plum).

Other libraries

  • plum: The most featureful alternative. Supports convert/promote functionality. Unfortunately, plum's code paths for Literal or Union appear to have massive overhead. ovld is much faster.
  • multimethod: Also pretty featureful. Performs a bit worse than plum in simple cases, but better in more complicated cases.
  • multipledispatch: Fair performance, interface is a bit dated, does not support dependent types.
  • runtype: Fair performance. Runtype supports Literal in theory, but it unfortunately bugged out on the calc and fib benchmarks.
  • singledispatch: Comes native in Python, but only supports dispatch on a single argument.

Benchmarks

Applicable libraries were tested on the following problems:

  • trivial: Basic single dispatch test with a bunch of types.
  • multer: Recursively multiply lists and dictionaries element-wise by a number, but using a class. This tests dispatch on methods.
  • add: Recursively add lists and dictionaries element-wise.
  • ast: Simple transform on a Python AST.
  • calc: Calculator implementation. Dispatches using op: Literal[<opname>].
  • regexp: Dispatch based on a regular expression (ovld.dependent.Regexp[<regex>]).
  • fib: Fibonacci numbers. The base cases are implemented by dispatching on n: Literal[0] and n: Literal[1].
  • tweaknum: Dispatching on keyword arguments.

Results

Time relative to the fastest implementation (1.00) (lower is better). Python version: 3.13.0

The custom column represents custom implementations using isinstance, match, a dispatch dict, etc. They are usually the fastest, but that should not be surprising. ovld's performance ranges from 1.5x faster to 3.3x slower.

Benchmark custom ovld plum multim multid runtype sd
trivial 1.56 1.00 3.38 4.92 2.00 2.38 2.15
multer 1.22 1.00 11.06 4.67 9.22 2.24 3.92
add 1.27 1.00 3.61 4.93 2.24 2.62 x
ast 1.01 1.00 22.98 2.72 1.52 1.70 1.57
calc 1.00 1.28 57.86 29.79 x x x
regexp 1.00 2.28 22.71 x x x x
fib 1.00 3.39 403.38 114.69 x x x
tweaknum 1.00 1.86 x x x x x

Comments

A big part of ovld's advantage comes from generating custom dispatch methods depending on the set of signatures that were registered. If you can avoid looping over *args, you're basically halving your overhead. Regarding dispatch on Literal, ovld also generates custom dispatch methods that unroll a series of specific if/else statements, which is the only way you'll ever get spitting distance from a normal implementation.

I haven't benchmarked the overhead of registering and compiling the methods, nor cache miss resolves, but I expect ovld will do pretty badly in that regard. That'll be the next big push, probably.