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

ModuleAccelerator performance: cache the result of checking if a caller is in the denylist #15056

Merged
merged 5 commits into from
Apr 9, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 15 additions & 9 deletions python/cudf/cudf/pandas/module_accelerator.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
from abc import abstractmethod
from importlib._bootstrap import _ImportLockContext as ImportLock
from types import ModuleType
from typing import Any, ContextManager, Dict, List, NamedTuple
from typing import Any, ContextManager, Dict, NamedTuple, Tuple

from typing_extensions import Self

Expand Down Expand Up @@ -377,7 +377,7 @@ class ModuleAccelerator(ModuleAcceleratorBase):
attempts to call the fast version first).
"""

_denylist: List[str]
_denylist: Tuple[str]
_use_fast_lib: bool
_use_fast_lib_lock: threading.RLock
_module_cache_prefix: str = "_slow_lib_"
Expand Down Expand Up @@ -407,7 +407,7 @@ def __new__(
if mod.startswith(self.slow_lib):
sys.modules[self._module_cache_prefix + mod] = sys.modules[mod]
del sys.modules[mod]
self._denylist = [*slow_module.__path__, *fast_module.__path__]
self._denylist = (*slow_module.__path__, *fast_module.__path__)

# Lock to manage temporarily disabling delivering wrapped attributes
self._use_fast_lib_lock = threading.RLock()
Expand Down Expand Up @@ -551,17 +551,13 @@ def getattr_real_or_wrapped(
# release the lock after reading this value)
use_real = not loader._use_fast_lib
if not use_real:
CUDF_PANDAS_PATH = __file__.rsplit("/", 1)[0]
# Only need to check the denylist if we're not turned off.
frame = sys._getframe()
# We cannot possibly be at the top level.
assert frame.f_back
calling_module = pathlib.PurePath(frame.f_back.f_code.co_filename)
use_real = not calling_module.is_relative_to(
CUDF_PANDAS_PATH
) and any(
calling_module.is_relative_to(path)
for path in loader._denylist
use_real = _caller_in_denylist(
calling_module, tuple(loader._denylist)
)
try:
if use_real:
Expand Down Expand Up @@ -623,3 +619,13 @@ def disable_module_accelerator() -> contextlib.ExitStack:
stack.enter_context(finder.disabled())
return stack.pop_all()
assert False # pacify type checker


# because this function gets called so often and is quite
# expensive to run, we cache the results:
@functools.lru_cache(maxsize=1024)
def _caller_in_denylist(calling_module, denylist):
CUDF_PANDAS_PATH = __file__.rsplit("/", 1)[0]
return not calling_module.is_relative_to(CUDF_PANDAS_PATH) and any(
calling_module.is_relative_to(path) for path in denylist
)
Loading