beartype icon indicating copy to clipboard operation
beartype copied to clipboard

[Feature Request] Micro optimization for wrapper

Open minmax opened this issue 1 year ago • 4 comments

input:

@beartype
def foo() -> None:
    pass

beartype generates:

def foo(
    *args,
    __beartype_object_137314538699864=__beartype_object_137314538699864,
    __beartype_get_violation=__beartype_get_violation,
    __beartype_conf=__beartype_conf,
    __beartype_check_meta=__beartype_check_meta,0x7ce3061f0940>,
    __beartype_func=__beartype_func,
    **kwargs
):
    ...

witch can be optimized to:

def foo(
   *,
    __beartype_object_137314538699864=__beartype_object_137314538699864,
    __beartype_get_violation=__beartype_get_violation,
    __beartype_conf=__beartype_conf,
    __beartype_check_meta=__beartype_check_meta,0x7ce3061f0940>,
    __beartype_func=__beartype_func,
):
    ...

In general *args and **kwargs can be removed for functions with no arguments. Also **kwargs can be removed for a function with only positional arguments, and vice versa

minmax avatar Sep 22 '24 20:09 minmax

micro benchmark results (from ipython + python 3.10.15):

In [1]: def f(*args):
   ...:     pass
   ...: 

In [2]: def no_kwargs(*args):
   ...:     return f(*args)
   ...: 

In [4]: def with_kwargs(*args, **kwargs):
   ...:     return f(*args, **kwargs)
   ...: 

In [7]: %timeit no_kwargs(1, 2)
156 ns ± 3.4 ns per loop (mean ± std. dev. of 7 runs, 10,000,000 loops each)

In [8]: %timeit with_kwargs(1, 2)
186 ns ± 0.811 ns per loop (mean ± std. dev. of 7 runs, 10,000,000 loops each)

minmax avatar Sep 22 '24 20:09 minmax

Clever @minmax. You're absolutely right, of course. Excessively hardcore micro-optimization is the way. When a decorator is run as often as @beartype is run, there's no micro-optimization too small. Every optimization yields benefit here. Thanks so much for the superb suggestion!

Interestingly, some future @beartype release will completely abandon the standard *args, ..., **kwargs decorator paradigm. Instead, the @beartype decorator will perfectly replicate the signatures of all decorated callables – entirely eliminating the need for variadic positional and keyword parameters like *args`` and **kwargs`.

For example, given this:

@beartype
def wtf() -> None: ...

@beartype
def lol(this_is_fine: int) -> str: ...

...@beartype will dynamically generate function signatures resembling:

# Look, ma! No "*args" or "**kwargs"! WTFFFFFFF.
def wtf(
    __beartype_object_137314538699864=__beartype_object_137314538699864,
    __beartype_get_violation=__beartype_get_violation,
    __beartype_conf=__beartype_conf,
    __beartype_check_meta=__beartype_check_meta,0x7ce3061f0940>,
    __beartype_func=__beartype_func,
) -> None:
    ...

# Still no "*args" or "**kwargs"! lol.
def lol(
    this_is_fine: int,
    __beartype_object_137314538699864=__beartype_object_137314538699864,
    __beartype_get_violation=__beartype_get_violation,
    __beartype_conf=__beartype_conf,
    __beartype_check_meta=__beartype_check_meta,0x7ce3061f0940>,
    __beartype_func=__beartype_func,
) -> str:
    ...

Honestly, it would be saner to just do that. Perfectly replicating callable signatures micro-optimizes all possible use cases, right? Shockingly, existing feature request #350 discusses this in detail – complete with a concrete plan for achieving this within the known lifetime of the Universe.

If you'd like to thrust your gauntlet into the PR arena, see this exhausting doctoral thesis. The gist of the whole thing is that the @beartype codebase needs to define a substantially improved def make_func_signature() factory function for perfectly replicating callable signatures.

It's all specced out, designed, and partially implemented. Let me know if you'd like to take a stab at that! I'm sure @iamrecursion would be delighted for you to lift that heavy load off her aching shoulders. :smile:

leycec avatar Sep 22 '24 23:09 leycec

My guilt complex would thank you. :P

iamrecursion avatar Sep 23 '24 14:09 iamrecursion

@leycec i will try to implement this, ... but i really can't code without ruff \ vscode, sorry, it's my laziness. My plan for now is to resolve https://github.com/beartype/beartype/issues/366, and after that i can show some progress.

So please answer in https://github.com/beartype/beartype/issues/366 )

minmax avatar Sep 28 '24 11:09 minmax