diff options
| author | 2023-10-24 10:11:21 -0600 | |
|---|---|---|
| committer | 2023-11-13 19:10:05 -0700 | |
| commit | b5267034cbf2b8eafcae4d76aac57fd0460a1cc5 (patch) | |
| tree | fa10047f68be3cf25b2262b7da0fdef7abcb9fff | |
| parent | c7e18277da56d51912874f75a9089055eb57b5f3 (diff) | |
EGL Multifile BlobCache: Limit entry count
Limit the number of entries to 4096.
This is an empirical number based on app behavior. Some using many
small entries are taking a long time to load the cache.
Test: MultifileBlobCacheTest.CacheMaxEntrySucceeds
Bug: b/295051628
Bug: b/310535559
Change-Id: Ibc671cec25dd7c9bc10b9d1ee1fb837180eb7551
| -rw-r--r-- | opengl/libs/EGL/MultifileBlobCache.cpp | 21 | ||||
| -rw-r--r-- | opengl/libs/EGL/MultifileBlobCache.h | 7 | ||||
| -rw-r--r-- | opengl/libs/EGL/MultifileBlobCache_test.cpp | 35 | ||||
| -rw-r--r-- | opengl/libs/EGL/egl_cache.cpp | 3 | ||||
| -rw-r--r-- | opengl/libs/EGL/fuzzer/MultifileBlobCache_fuzzer.cpp | 11 |
5 files changed, 53 insertions, 24 deletions
diff --git a/opengl/libs/EGL/MultifileBlobCache.cpp b/opengl/libs/EGL/MultifileBlobCache.cpp index ed3c616b92..13a5e7baa7 100644 --- a/opengl/libs/EGL/MultifileBlobCache.cpp +++ b/opengl/libs/EGL/MultifileBlobCache.cpp @@ -62,12 +62,14 @@ void freeHotCacheEntry(android::MultifileHotCache& entry) { namespace android { MultifileBlobCache::MultifileBlobCache(size_t maxKeySize, size_t maxValueSize, size_t maxTotalSize, - const std::string& baseDir) + size_t maxTotalEntries, const std::string& baseDir) : mInitialized(false), mMaxKeySize(maxKeySize), mMaxValueSize(maxValueSize), mMaxTotalSize(maxTotalSize), + mMaxTotalEntries(maxTotalEntries), mTotalCacheSize(0), + mTotalCacheEntries(0), mHotCacheLimit(0), mHotCacheSize(0), mWorkerThreadIdle(true) { @@ -270,7 +272,7 @@ void MultifileBlobCache::set(const void* key, EGLsizeiANDROID keySize, const voi size_t fileSize = sizeof(MultifileHeader) + keySize + valueSize; // If we're going to be over the cache limit, kick off a trim to clear space - if (getTotalSize() + fileSize > mMaxTotalSize) { + if (getTotalSize() + fileSize > mMaxTotalSize || getTotalEntries() + 1 > mMaxTotalEntries) { ALOGV("SET: Cache is full, calling trimCache to clear space"); trimCache(); } @@ -485,10 +487,12 @@ MultifileEntryStats MultifileBlobCache::getEntryStats(uint32_t entryHash) { void MultifileBlobCache::increaseTotalCacheSize(size_t fileSize) { mTotalCacheSize += fileSize; + mTotalCacheEntries++; } void MultifileBlobCache::decreaseTotalCacheSize(size_t fileSize) { mTotalCacheSize -= fileSize; + mTotalCacheEntries--; } bool MultifileBlobCache::addToHotCache(uint32_t newEntryHash, int newFd, uint8_t* newEntryBuffer, @@ -557,7 +561,7 @@ bool MultifileBlobCache::removeFromHotCache(uint32_t entryHash) { return false; } -bool MultifileBlobCache::applyLRU(size_t cacheLimit) { +bool MultifileBlobCache::applyLRU(size_t cacheSizeLimit, size_t cacheEntryLimit) { // Walk through our map of sorted last access times and remove files until under the limit for (auto cacheEntryIter = mEntryStats.begin(); cacheEntryIter != mEntryStats.end();) { uint32_t entryHash = cacheEntryIter->first; @@ -590,9 +594,10 @@ bool MultifileBlobCache::applyLRU(size_t cacheLimit) { // See if it has been reduced enough size_t totalCacheSize = getTotalSize(); - if (totalCacheSize <= cacheLimit) { + size_t totalCacheEntries = getTotalEntries(); + if (totalCacheSize <= cacheSizeLimit && totalCacheEntries <= cacheEntryLimit) { // Success - ALOGV("LRU: Reduced cache to %zu", totalCacheSize); + ALOGV("LRU: Reduced cache to size %zu entries %zu", totalCacheSize, totalCacheEntries); return true; } } @@ -603,6 +608,7 @@ bool MultifileBlobCache::applyLRU(size_t cacheLimit) { // When removing files, what fraction of the overall limit should be reached when removing files // A divisor of two will decrease the cache to 50%, four to 25% and so on +// We use the same limit to manage size and entry count constexpr uint32_t kCacheLimitDivisor = 2; // Calculate the cache size and remove old entries until under the limit @@ -611,8 +617,9 @@ void MultifileBlobCache::trimCache() { ALOGV("TRIM: Waiting for work to complete."); waitForWorkComplete(); - ALOGV("TRIM: Reducing multifile cache size to %zu", mMaxTotalSize / kCacheLimitDivisor); - if (!applyLRU(mMaxTotalSize / kCacheLimitDivisor)) { + ALOGV("TRIM: Reducing multifile cache size to %zu, entries %zu", + mMaxTotalSize / kCacheLimitDivisor, mMaxTotalEntries / kCacheLimitDivisor); + if (!applyLRU(mMaxTotalSize / kCacheLimitDivisor, mMaxTotalEntries / kCacheLimitDivisor)) { ALOGE("Error when clearing multifile shader cache"); return; } diff --git a/opengl/libs/EGL/MultifileBlobCache.h b/opengl/libs/EGL/MultifileBlobCache.h index 5e527dcf35..9a396f01c3 100644 --- a/opengl/libs/EGL/MultifileBlobCache.h +++ b/opengl/libs/EGL/MultifileBlobCache.h @@ -92,7 +92,7 @@ private: class MultifileBlobCache { public: MultifileBlobCache(size_t maxKeySize, size_t maxValueSize, size_t maxTotalSize, - const std::string& baseDir); + size_t maxTotalEntries, const std::string& baseDir); ~MultifileBlobCache(); void set(const void* key, EGLsizeiANDROID keySize, const void* value, @@ -103,6 +103,7 @@ public: void finish(); size_t getTotalSize() const { return mTotalCacheSize; } + size_t getTotalEntries() const { return mTotalCacheEntries; } private: void trackEntry(uint32_t entryHash, EGLsizeiANDROID valueSize, size_t fileSize, @@ -121,7 +122,7 @@ private: bool removeFromHotCache(uint32_t entryHash); void trimCache(); - bool applyLRU(size_t cacheLimit); + bool applyLRU(size_t cacheSizeLimit, size_t cacheEntryLimit); bool mInitialized; std::string mMultifileDirName; @@ -133,7 +134,9 @@ private: size_t mMaxKeySize; size_t mMaxValueSize; size_t mMaxTotalSize; + size_t mMaxTotalEntries; size_t mTotalCacheSize; + size_t mTotalCacheEntries; size_t mHotCacheLimit; size_t mHotCacheEntryLimit; size_t mHotCacheSize; diff --git a/opengl/libs/EGL/MultifileBlobCache_test.cpp b/opengl/libs/EGL/MultifileBlobCache_test.cpp index 1639be6480..8e27f5a738 100644 --- a/opengl/libs/EGL/MultifileBlobCache_test.cpp +++ b/opengl/libs/EGL/MultifileBlobCache_test.cpp @@ -31,13 +31,14 @@ using sp = std::shared_ptr<T>; constexpr size_t kMaxKeySize = 2 * 1024; constexpr size_t kMaxValueSize = 6 * 1024; constexpr size_t kMaxTotalSize = 32 * 1024; +constexpr size_t kMaxTotalEntries = 64; class MultifileBlobCacheTest : public ::testing::Test { protected: virtual void SetUp() { mTempFile.reset(new TemporaryFile()); mMBC.reset(new MultifileBlobCache(kMaxKeySize, kMaxValueSize, kMaxTotalSize, - &mTempFile->path[0])); + kMaxTotalEntries, &mTempFile->path[0])); } virtual void TearDown() { mMBC.reset(); } @@ -211,6 +212,23 @@ TEST_F(MultifileBlobCacheTest, CacheMaxKeyAndValueSizeSucceeds) { } } +TEST_F(MultifileBlobCacheTest, CacheMaxEntrySucceeds) { + // Fill the cache with max entries + int i = 0; + for (i = 0; i < kMaxTotalEntries; i++) { + mMBC->set(std::to_string(i).c_str(), sizeof(i), std::to_string(i).c_str(), sizeof(i)); + } + + // Ensure it is full + ASSERT_EQ(mMBC->getTotalEntries(), kMaxTotalEntries); + + // Add another entry + mMBC->set(std::to_string(i).c_str(), sizeof(i), std::to_string(i).c_str(), sizeof(i)); + + // Ensure total entries is cut in half + 1 + ASSERT_EQ(mMBC->getTotalEntries(), kMaxTotalEntries / 2 + 1); +} + TEST_F(MultifileBlobCacheTest, CacheMinKeyAndValueSizeSucceeds) { unsigned char buf[1] = {0xee}; mMBC->set("x", 1, "y", 1); @@ -234,8 +252,7 @@ int MultifileBlobCacheTest::getFileDescriptorCount() { TEST_F(MultifileBlobCacheTest, EnsureFileDescriptorsClosed) { // Populate the cache with a bunch of entries - size_t kLargeNumberOfEntries = 1024; - for (int i = 0; i < kLargeNumberOfEntries; i++) { + for (int i = 0; i < kMaxTotalEntries; i++) { // printf("Caching: %i", i); // Use the index as the key and value @@ -247,27 +264,27 @@ TEST_F(MultifileBlobCacheTest, EnsureFileDescriptorsClosed) { } // Ensure we don't have a bunch of open fds - ASSERT_LT(getFileDescriptorCount(), kLargeNumberOfEntries / 2); + ASSERT_LT(getFileDescriptorCount(), kMaxTotalEntries / 2); // Close the cache so everything writes out mMBC->finish(); mMBC.reset(); // Now open it again and ensure we still don't have a bunch of open fds - mMBC.reset( - new MultifileBlobCache(kMaxKeySize, kMaxValueSize, kMaxTotalSize, &mTempFile->path[0])); + mMBC.reset(new MultifileBlobCache(kMaxKeySize, kMaxValueSize, kMaxTotalSize, kMaxTotalEntries, + &mTempFile->path[0])); // Check after initialization - ASSERT_LT(getFileDescriptorCount(), kLargeNumberOfEntries / 2); + ASSERT_LT(getFileDescriptorCount(), kMaxTotalEntries / 2); - for (int i = 0; i < kLargeNumberOfEntries; i++) { + for (int i = 0; i < kMaxTotalEntries; i++) { int result = 0; ASSERT_EQ(sizeof(i), mMBC->get(&i, sizeof(i), &result, sizeof(result))); ASSERT_EQ(i, result); } // And again after we've actually used it - ASSERT_LT(getFileDescriptorCount(), kLargeNumberOfEntries / 2); + ASSERT_LT(getFileDescriptorCount(), kMaxTotalEntries / 2); } } // namespace android diff --git a/opengl/libs/EGL/egl_cache.cpp b/opengl/libs/EGL/egl_cache.cpp index 1b68344cb1..98ff1d12cc 100644 --- a/opengl/libs/EGL/egl_cache.cpp +++ b/opengl/libs/EGL/egl_cache.cpp @@ -41,6 +41,7 @@ static const unsigned int kDeferredMonolithicSaveDelay = 4; constexpr uint32_t kMaxMultifileKeySize = 1 * 1024 * 1024; constexpr uint32_t kMaxMultifileValueSize = 8 * 1024 * 1024; constexpr uint32_t kMaxMultifileTotalSize = 32 * 1024 * 1024; +constexpr uint32_t kMaxMultifileTotalEntries = 4 * 1024; namespace android { @@ -277,7 +278,7 @@ MultifileBlobCache* egl_cache_t::getMultifileBlobCacheLocked() { if (mMultifileBlobCache == nullptr) { mMultifileBlobCache.reset(new MultifileBlobCache(kMaxMultifileKeySize, kMaxMultifileValueSize, mCacheByteLimit, - mFilename)); + kMaxMultifileTotalEntries, mFilename)); } return mMultifileBlobCache.get(); } diff --git a/opengl/libs/EGL/fuzzer/MultifileBlobCache_fuzzer.cpp b/opengl/libs/EGL/fuzzer/MultifileBlobCache_fuzzer.cpp index 633cc9c51b..2d9ee3a49e 100644 --- a/opengl/libs/EGL/fuzzer/MultifileBlobCache_fuzzer.cpp +++ b/opengl/libs/EGL/fuzzer/MultifileBlobCache_fuzzer.cpp @@ -28,6 +28,7 @@ namespace android { constexpr size_t kMaxKeySize = 2 * 1024; constexpr size_t kMaxValueSize = 6 * 1024; constexpr size_t kMaxTotalSize = 32 * 1024; +constexpr size_t kMaxTotalEntries = 64; extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { // To fuzz this, we're going to create a key/value pair from data @@ -79,8 +80,8 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { std::unique_ptr<MultifileBlobCache> mbc; tempFile.reset(new TemporaryFile()); - mbc.reset( - new MultifileBlobCache(kMaxKeySize, kMaxValueSize, kMaxTotalSize, &tempFile->path[0])); + mbc.reset(new MultifileBlobCache(kMaxKeySize, kMaxValueSize, kMaxTotalSize, kMaxTotalEntries, + &tempFile->path[0])); // With remaining data, select different paths below int loopCount = 1; uint8_t bumpCount = 0; @@ -131,8 +132,8 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { // Place the maxKey/maxValue twice // The first will fit, the second will trigger hot cache trimming tempFile.reset(new TemporaryFile()); - mbc.reset( - new MultifileBlobCache(kMaxKeySize, kMaxValueSize, kMaxTotalSize, &tempFile->path[0])); + mbc.reset(new MultifileBlobCache(kMaxKeySize, kMaxValueSize, kMaxTotalSize, kMaxTotalEntries, + &tempFile->path[0])); uint8_t* buffer = new uint8_t[kMaxValueSize]; mbc->set(maxKey1.data(), kMaxKeySize, maxValue1.data(), kMaxValueSize); mbc->set(maxKey2.data(), kMaxKeySize, maxValue2.data(), kMaxValueSize); @@ -145,7 +146,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { // overflow tempFile.reset(new TemporaryFile()); mbc.reset(new MultifileBlobCache(kMaxKeySize, kMaxValueSize, 2 * (kMaxKeySize + kMaxValueSize), - &tempFile->path[0])); + kMaxTotalEntries, &tempFile->path[0])); mbc->set(maxKey1.data(), kMaxKeySize, maxValue1.data(), kMaxValueSize); mbc->set(maxKey2.data(), kMaxKeySize, maxValue2.data(), kMaxValueSize); mbc->get(maxKey1.data(), kMaxKeySize, buffer, kMaxValueSize); |