Created
December 29, 2022 01:02
-
-
Save Micky774/d5c7ecbde3cbb90a7b05d5a680bb2bd0 to your computer and use it in GitHub Desktop.
Cython vtable workarounds
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Compile with annotated view to compare generated instructions | |
# and presence of `__pyx_vtabstruct_*` | |
from cython cimport final | |
# Typedef function pointer | |
ctypedef int (*f_type)(A) | |
# Ancestor, serves as interface/ABC | |
cdef class A: | |
cdef f_type func | |
def __init__(self): | |
self.func = self.test | |
cdef int test(self): | |
return 1 | |
# Entire class is finalized, therefore avoids vtable lookup | |
@final | |
cdef class B(A): | |
cdef int test(self): | |
return 2 | |
# Relevant method is finalized, therefore avoids vtable lookup | |
cdef class C(A): | |
@final | |
cdef int test(self): | |
return 3 | |
# Nothing is finalized, therefore still viable for vtable lookup | |
cdef class D(A): | |
cdef int test(self): | |
return 4 | |
# Fused type of all finalized classes. Such a list is potentially difficult to | |
# maintain, but also makes concrete implementations clear. | |
ctypedef fused FINALIZED: | |
B | |
C | |
cdef class Tester: | |
cdef A x | |
def __init__(self, A x): | |
self.x = x | |
cdef void run_test_ref(self, Py_ssize_t n): | |
""" | |
We use a stored reference to the method and feed it the object it ought | |
to be bound to in order to avoid vtable lookups. This avoids the need for | |
finalization entirely, although it is still be good practice. | |
""" | |
cdef: | |
Py_ssize_t idx | |
f_type f = self.x.test | |
A x = self.x | |
for idx in range(n): | |
# Since f is unbound, we must give it the object it originated from. | |
# Potentially risky from a maintenance perspective, but should be | |
# manageable. | |
f(x) | |
cdef void run_test_fused(self, Py_ssize_t n, FINALIZED x): | |
""" | |
We use a fused-type composed of only objects with finalized methods to | |
avoid vtable lookups. | |
""" | |
cdef Py_ssize_t idx | |
for idx in range(n): | |
x.test() | |
cdef void run_test_brute(self, Py_ssize_t n, A x): | |
""" | |
We do nothing to avoid vtable lookups. | |
""" | |
cdef Py_ssize_t idx | |
for idx in range(n): | |
x.test() | |
cdef: | |
B b = B() | |
C c = C() | |
D d = D() | |
Tester T_B = Tester(b) | |
Tester T_C = Tester(c) | |
Tester T_D = Tester(d) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Thanks for exploring this, @Micky774 and @thomasjpfan!
https://gist.github.com/jjerphan/bb6a9545e4bbbcb4a7d1b15af8e1b5d1 gives some information about the Cython Extension Types' Methods' resolution using vtable. It is extracted from a thread of discussion started by a relevant question from @thomasjpfan in scikit-learn/scikit-learn#20254.