Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

the results of benchmarking small functions are malformed #16

Open
thautwarm opened this issue Aug 10, 2022 · 1 comment
Open

the results of benchmarking small functions are malformed #16

thautwarm opened this issue Aug 10, 2022 · 1 comment
Labels
enhancement New feature or request

Comments

@thautwarm
Copy link
Member

Correctly maintaining the gabarge collection mechanisms of the two languages causes benchmarking issues, which can be observed when using Python's timeit.

# julia
using TyPython
using TyPython.CPython

@export_py function add(a::Int, b::Int)::Int
    return a + b
end

@export_pymodule myops begin
      add = Pyfunc(add)
end
# python

import operators
%timeit myops.add(1, 2)  # 500ns
%timeit operator.add(1, 2)  # 49ns

Above benchmark for Python operator.add is fine, but quite unfair for the Julia one, as in the realworld cases, myops.add works much faster:

import time
import sys

def realworld_timeit(code: str, N = 100000, globals: 'dict | None' = None):
    code = compile(f"for i in range({N}): {code}", '<string>', 'exec')
    t = time.time_ns()
    exec(code, globals or sys._getframe().f_globals)
    print((time.time_ns() - t) / N)


realworld_timeit("myops.add(1, 2)")  # 120ns
realworld_timeit("operator.add(1, 2)")  # 80ns

This is because Julia uses a real GC which will scan the heap instead of Python's reference counting. Calling into a small function from a foreign language (like CPython) can trigger a long GC operation.

Besides, WITH_GIL is so far a bit slow as it calls is_calling_julia_from_python. Such process is required when finalizing a Julia wrapped Python object.

function Py(::UnsafeNew, ptr::C.Ptr{PyObject}=Py_NULLPTR)
self = new(ptr)
finalizer(self) do x
if G_IsInitialized[]
WITH_GIL(GILNoRaise()) do

if is_calling_julia_from_python() && G_IsInitialized[]
g = PyAPI.PyGILState_Ensure()
try
return f()

@thautwarm thautwarm added the enhancement New feature or request label Aug 10, 2022
@thautwarm
Copy link
Member Author

After #18 and #19 get merged, this issue will get eased a lot:

In [9]: %timeit mylib.add(1, 2)
196 ns ± 159 ns per loop (mean ± std. dev. of 7 runs, 10,000,000 loops each)

In [10]: %timeit mylib.add(1, 2)
209 ns ± 155 ns per loop (mean ± std. dev. of 7 runs, 10,000,000 loops each)

In [11]: %timeit operator.add(1, 2)
48.5 ns ± 0.313 ns per loop (mean ± std. dev. of 7 runs, 10,000,000 loops each)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

1 participant