Skip to content

Commit

Permalink
[libc++] Persistently cache memoized operations during Lit configuration
Browse files Browse the repository at this point in the history
When invoking Lit repeatedly, we perform all the configuration checks
over and over again, which takes a lot of time. This patch allows caching
the result of configuration checks persistently across Lit invocations to
speed this up.

In theory, this should still be functionally correct since the cache
key should contain everything that determines the output of the
configuration check. However, in cases where e.g. the compiler has
changed but is at the same path as previously, the Lit configuration
checks will be cached even though technically the cache should have
been invalidated.

Differential Revision: https://reviews.llvm.org/D117361
  • Loading branch information
ldionne committed Jan 18, 2022
1 parent ce2345d commit 4afa9c1
Showing 1 changed file with 25 additions and 4 deletions.
29 changes: 25 additions & 4 deletions libcxx/utils/libcxx/test/dsl.py
Expand Up @@ -37,13 +37,34 @@ def _memoizeExpensiveOperation(extractCacheKey):
We pickle the cache key to make sure we store an immutable representation
of it. If we stored an object and the object was referenced elsewhere, it
could be changed from under our feet, which would break the cache.
We also store the cache for a given function persistently across invocations
of Lit. This dramatically speeds up the configuration of the test suite when
invoking Lit repeatedly, which is important for developer workflow. However,
with the current implementation that does not synchronize updates to the
persistent cache, this also means that one should not call a memoized
operation from multiple threads. This should normally not be a problem
since Lit configuration is single-threaded.
"""
def decorator(function):
cache = {}
def f(*args, **kwargs):
cacheKey = pickle.dumps(extractCacheKey(*args, **kwargs))
def f(config, *args, **kwargs):
cacheRoot = os.path.join(config.test_exec_root, '__config_cache__')
persistentCache = os.path.join(cacheRoot, function.__name__)
if not os.path.exists(cacheRoot):
os.makedirs(cacheRoot)

cache = {}
# Load a cache from a previous Lit invocation if there is one.
if os.path.exists(persistentCache):
with open(persistentCache, 'rb') as cacheFile:
cache = pickle.load(cacheFile)

cacheKey = pickle.dumps(extractCacheKey(config, *args, **kwargs))
if cacheKey not in cache:
cache[cacheKey] = function(*args, **kwargs)
cache[cacheKey] = function(config, *args, **kwargs)
# Update the persistent cache so it knows about the new key
with open(persistentCache, 'wb') as cacheFile:
pickle.dump(cache, cacheFile)
return cache[cacheKey]
return f
return decorator
Expand Down

0 comments on commit 4afa9c1

Please sign in to comment.