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

support aborting on cache miss #828

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
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
5 changes: 5 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -107,10 +107,15 @@ jobs:
- uses: actions/checkout@v4
- name: "Main Script"
run: |
# This test makes sure that loopy can run with kernels loaded from disk cache.
curl -L -O https://tiker.net/ci-support-v0
. ./ci-support-v0
build_py_project_in_conda_env
( test_py_project )

# See https://github.com/inducer/loopy/pull/828 why this is disabled.
# export LOOPY_ABORT_ON_CACHE_MISS=1

( test_py_project )
Copy link
Collaborator Author

@matthiasdiener matthiasdiener Feb 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@inducer do you remember what the purpose of this pytest_twice CI check was/is? Is it just about seeing the time difference between the first pytest run and the second run?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, it's supposed to test that things still work with data pulled out of a cache, since the first round of tests runs on a cold cache by default.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it expected that running pytest like this will lead to cache misses on the second run (demonstrated by the CI in this PR)? I've tried to go back to earlier loopy versions, and it seems like these cache misses are not caused by recent changes.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Huh, I would not expect there to be cache misses on the second go.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suspect code like this (from test_c_execution.py) is responsible for the cache misses:

def __get_kernel(order="C"):
    indices = ["i", "j", "k"]
    sizes = tuple(np.random.randint(1, 11, size=len(indices)))
    # ...

Would you prefer me to make these sizes constant across runs, or should we just ignore the cache miss?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Setting a seed (and converting to the new-style numpy RNG interface) seems like the way to go.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Considering the multitude of issues encountered, it is probably not realistic to enable this in CI for the near future. I'll disable the export for now.


examples:
Expand Down
7 changes: 7 additions & 0 deletions doc/ref_other.rst
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,13 @@ Controlling caching
is suppressed.


.. envvar:: LOOPY_ABORT_ON_CACHE_MISS

If set to a string that :func:`pytools.strtobool` evaluates as ``True``,
loopy will raise an exception if a cache miss occurs. This can be useful
for debugging cache-related issues. For example, it can be used to automatically test whether caching is successful for a particular code, by setting this variable to ``True`` and re-running the code.


.. autofunction:: set_caching_enabled

.. autoclass:: CacheMode
Expand Down
3 changes: 3 additions & 0 deletions loopy/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -509,6 +509,9 @@ def register_symbol_manglers(kernel, manglers):
not strtobool(os.environ.get("CG_NO_CACHE", "false")))


ABORT_ON_CACHE_MISS = strtobool(os.environ.get("LOOPY_ABORT_ON_CACHE_MISS", "false"))


def set_caching_enabled(flag):
"""Set whether :mod:`loopy` is allowed to use disk caching for its various
code generation stages.
Expand Down
5 changes: 4 additions & 1 deletion loopy/codegen/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -549,7 +549,8 @@ def all_code(self):

def generate_code_v2(t_unit: TranslationUnit) -> CodeGenerationResult:
# {{{ cache retrieval
from loopy import CACHING_ENABLED

from loopy import ABORT_ON_CACHE_MISS, CACHING_ENABLED
from loopy.kernel import LoopKernel
from loopy.translation_unit import make_program

Expand All @@ -563,6 +564,8 @@ def generate_code_v2(t_unit: TranslationUnit) -> CodeGenerationResult:
except KeyError:
logger.debug(f"TranslationUnit with entrypoints {t_unit.entrypoints}:"
" code generation cache miss")
if ABORT_ON_CACHE_MISS:
raise

# }}}

Expand Down
7 changes: 5 additions & 2 deletions test/test_c_execution.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,10 +93,11 @@ def __get_kernel(order="C"):

def test_c_target_strides_nonsquare():
from loopy.target.c import ExecutableCTarget
rng = np.random.default_rng(seed=42)

def __get_kernel(order="C"):
indices = ["i", "j", "k"]
sizes = tuple(np.random.randint(1, 11, size=len(indices)))
sizes = tuple(rng.integers(1, 11, size=len(indices)))
# create domain strings
domain_template = "{{ [{iname}]: 0 <= {iname} < {size} }}"
domains = []
Expand Down Expand Up @@ -141,9 +142,11 @@ def __get_kernel(order="C"):
def test_c_optimizations():
from loopy.target.c import ExecutableCTarget

rng = np.random.default_rng(seed=42)

def __get_kernel(order="C"):
indices = ["i", "j", "k"]
sizes = tuple(np.random.randint(1, 11, size=len(indices)))
sizes = tuple(rng.integers(1, 11, size=len(indices)))
# create domain strings
domain_template = "{{ [{iname}]: 0 <= {iname} < {size} }}"
domains = []
Expand Down
Loading