diff --git a/loopy/target/c/c_execution.py b/loopy/target/c/c_execution.py index c382e2debc8986de43355b34f9114a65bbbaf881..5efc58bb7cd8692594018a5f7a9bcf75278a3b9b 100644 --- a/loopy/target/c/c_execution.py +++ b/loopy/target/c/c_execution.py @@ -241,6 +241,8 @@ class CCompiler(object): if recompiled: logger.debug('Kernel {0} compiled from source'.format(name)) + else: + logger.debug('Kernel {0} retrieved from cache'.format(name)) # and return compiled return ctypes.CDLL(ext_file) diff --git a/test/test_c_execution.py b/test/test_c_execution.py index 7631f0c01ea014b9e02377d3e1c1c2fd519f7e94..d1b3c95caa034191b4b29c49076fc101cd318950 100644 --- a/test/test_c_execution.py +++ b/test/test_c_execution.py @@ -25,6 +25,9 @@ THE SOFTWARE. import numpy as np import loopy as lp import sys +import six +import pytest +from loopy import CACHING_ENABLED import logging logger = logging.getLogger(__name__) @@ -192,6 +195,77 @@ def test_function_decl_extractor(): assert np.allclose(knl(b=np.arange(10), v=-1)[1], np.arange(10) - 1) +@pytest.mark.skipif(not CACHING_ENABLED, reason="Can't test caching when disabled") +def test_c_caching(): + # ensure that codepy is correctly caching the code + from loopy.target.c import ExecutableCTarget + + class TestingLogger(object): + def start_capture(self, loglevel=logging.DEBUG): + """ Start capturing log output to a string buffer. + @param newLogLevel: Optionally change the global logging level, e.g. + logging.DEBUG + """ + self.buffer = six.StringIO() + self.buffer.write("Log output") + + logger = logging.getLogger() + if loglevel: + self.oldloglevel = logger.getEffectiveLevel() + logger.setLevel(loglevel) + else: + self.oldloglevel = None + + self.loghandler = logging.StreamHandler(self.buffer) + formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s " + "- %(message)s") + self.loghandler.setFormatter(formatter) + logger.addHandler(self.loghandler) + + def stop_capture(self): + """ Stop capturing log output. + + @return: Collected log output as string + """ + + # Remove our handler + logger = logging.getLogger() + + # Restore logging level (if any) + if self.oldloglevel is not None: + logger.setLevel(self.oldloglevel) + logger.removeHandler(self.loghandler) + + self.loghandler.flush() + self.buffer.flush() + + return self.buffer.getvalue() + + def __get_knl(): + return lp.make_kernel('{[i]: 0 <= i < 10}', + """ + a[i] = b[i] + """, + [lp.GlobalArg('a', shape=(10,), dtype=np.int32), + lp.ConstantArg('b', shape=(10))], + target=ExecutableCTarget(), + name='cache_test') + + knl = __get_knl() + # compile + assert np.allclose(knl(b=np.arange(10))[1], np.arange(10)) + # setup test logger to check logs + tl = TestingLogger() + tl.start_capture() + # remake kernel to clear cache + knl = __get_knl() + assert np.allclose(knl(b=np.arange(10))[1], np.arange(10)) + # and get logs + logs = tl.stop_capture() + # check that we didn't recompile + assert 'Kernel cache_test retrieved from cache' in logs + + def test_c_execution_with_global_temporaries(): # ensure that the "host" code of a bare ExecutableCTarget with # global constant temporaries is None