Skip to content

Commit 6aebcf2

Browse files
committed
EGL Multifile Blobcache: Clean up key reuse
This CL contains multiple fixes that allow key reuse. * set() now checks whether an entry is already present, and if so removes it from hotcache and tracking before adding them. * trackEntry and removeEntry now update the total cache size. * applyLRU now correctly removes entries from all tracking. Additional tests: * SameKeyDifferentValues * SameKeyLargeValues Based on work by: Igor Nazarov <i.nazarov@samsung.com> Test: libEGL_test, EGL_test, ANGLE trace tests, apps Bug: b/351867582, b/380483358 Flag: com.android.graphics.egl.flags.multifile_blobcache_advanced_usage Change-Id: I66fe9cde18e468e541a80296c80f2234ac61acb0
1 parent 4ee6386 commit 6aebcf2

2 files changed

Lines changed: 134 additions & 16 deletions

File tree

opengl/libs/EGL/MultifileBlobCache.cpp

Lines changed: 36 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -246,12 +246,9 @@ MultifileBlobCache::MultifileBlobCache(size_t maxKeySize, size_t maxValueSize, s
246246

247247
ALOGV("INIT: Entry %u is good, tracking it now.", entryHash);
248248

249-
// Track details for rapid lookup later
249+
// Track details for rapid lookup later and update total size
250250
trackEntry(entryHash, header.valueSize, fileSize, st.st_atime);
251251

252-
// Track the total size
253-
increaseTotalCacheSize(fileSize);
254-
255252
// Preload the entry for fast retrieval
256253
if ((mHotCacheSize + fileSize) < mHotCacheLimit) {
257254
ALOGV("INIT: Populating hot cache with fd = %i, cacheEntry = %p for "
@@ -326,6 +323,15 @@ void MultifileBlobCache::set(const void* key, EGLsizeiANDROID keySize, const voi
326323
// Generate a hash of the key and use it to track this entry
327324
uint32_t entryHash = android::JenkinsHashMixBytes(0, static_cast<const uint8_t*>(key), keySize);
328325

326+
// See if we already have this file
327+
if (flags::multifile_blobcache_advanced_usage() && contains(entryHash)) {
328+
// Remove previous entry from hot cache
329+
removeFromHotCache(entryHash);
330+
331+
// Remove previous entry and update the overall cache size
332+
removeEntry(entryHash);
333+
}
334+
329335
size_t fileSize = sizeof(MultifileHeader) + keySize + valueSize;
330336

331337
// If we're going to be over the cache limit, kick off a trim to clear space
@@ -350,12 +356,9 @@ void MultifileBlobCache::set(const void* key, EGLsizeiANDROID keySize, const voi
350356

351357
std::string fullPath = mMultifileDirName + "/" + std::to_string(entryHash);
352358

353-
// Track the size and access time for quick recall
359+
// Track the size and access time for quick recall and update the overall cache size
354360
trackEntry(entryHash, valueSize, fileSize, time(0));
355361

356-
// Update the overall cache size
357-
increaseTotalCacheSize(fileSize);
358-
359362
// Keep the entry in hot cache for quick retrieval
360363
ALOGV("SET: Adding %u to hot cache.", entryHash);
361364

@@ -638,6 +641,27 @@ void MultifileBlobCache::trackEntry(uint32_t entryHash, EGLsizeiANDROID valueSiz
638641
time_t accessTime) {
639642
mEntries.insert(entryHash);
640643
mEntryStats[entryHash] = {valueSize, fileSize, accessTime};
644+
645+
increaseTotalCacheSize(fileSize);
646+
}
647+
648+
bool MultifileBlobCache::removeEntry(uint32_t entryHash) {
649+
auto entryIter = mEntries.find(entryHash);
650+
if (entryIter == mEntries.end()) {
651+
return false;
652+
}
653+
654+
auto entryStatsIter = mEntryStats.find(entryHash);
655+
if (entryStatsIter == mEntryStats.end()) {
656+
ALOGE("Failed to remove entryHash (%u) from mEntryStats", entryHash);
657+
return false;
658+
}
659+
decreaseTotalCacheSize(entryStatsIter->second.fileSize);
660+
661+
mEntryStats.erase(entryStatsIter);
662+
mEntries.erase(entryIter);
663+
664+
return true;
641665
}
642666

643667
bool MultifileBlobCache::contains(uint32_t hashEntry) const {
@@ -728,13 +752,10 @@ bool MultifileBlobCache::applyLRU(size_t cacheSizeLimit, size_t cacheEntryLimit)
728752
// Walk through our map of sorted last access times and remove files until under the limit
729753
for (auto cacheEntryIter = mEntryStats.begin(); cacheEntryIter != mEntryStats.end();) {
730754
uint32_t entryHash = cacheEntryIter->first;
755+
const MultifileEntryStats& entryStats = cacheEntryIter->second;
731756

732757
ALOGV("LRU: Removing entryHash %u", entryHash);
733758

734-
// Track the overall size
735-
MultifileEntryStats entryStats = getEntryStats(entryHash);
736-
decreaseTotalCacheSize(entryStats.fileSize);
737-
738759
// Remove it from hot cache if present
739760
removeFromHotCache(entryHash);
740761

@@ -748,10 +769,9 @@ bool MultifileBlobCache::applyLRU(size_t cacheSizeLimit, size_t cacheEntryLimit)
748769
// Increment the iterator before clearing the entry
749770
cacheEntryIter++;
750771

751-
// Delete the entry from our tracking
752-
size_t count = mEntryStats.erase(entryHash);
753-
if (count != 1) {
754-
ALOGE("LRU: Failed to remove entryHash (%u) from mEntryStats", entryHash);
772+
// Delete the entry from our tracking and update the overall cache size
773+
if (!removeEntry(entryHash)) {
774+
ALOGE("LRU: Failed to remove entryHash %u", entryHash);
755775
return false;
756776
}
757777

opengl/libs/EGL/MultifileBlobCache_test.cpp

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -509,4 +509,102 @@ TEST_F(MultifileBlobCacheTest, MismatchedBuildIdClears) {
509509
ASSERT_EQ(getCacheEntries().size(), 0);
510510
}
511511

512+
// Ensure cache is correct when a key is reused
513+
TEST_F(MultifileBlobCacheTest, SameKeyDifferentValues) {
514+
if (!flags::multifile_blobcache_advanced_usage()) {
515+
GTEST_SKIP() << "Skipping test that requires multifile_blobcache_advanced_usage flag";
516+
}
517+
518+
unsigned char buf[4] = {0xee, 0xee, 0xee, 0xee};
519+
520+
size_t startingSize = mMBC->getTotalSize();
521+
522+
// New cache should be empty
523+
ASSERT_EQ(startingSize, 0);
524+
525+
// Set an initial value
526+
mMBC->set("ab", 2, "cdef", 4);
527+
528+
// Grab the new size
529+
size_t firstSize = mMBC->getTotalSize();
530+
531+
// Ensure the size went up
532+
// Note: Checking for an exact size is challenging, as the
533+
// file size can differ between platforms.
534+
ASSERT_GT(firstSize, startingSize);
535+
536+
// Verify the cache is correct
537+
ASSERT_EQ(size_t(4), mMBC->get("ab", 2, buf, 4));
538+
ASSERT_EQ('c', buf[0]);
539+
ASSERT_EQ('d', buf[1]);
540+
ASSERT_EQ('e', buf[2]);
541+
ASSERT_EQ('f', buf[3]);
542+
543+
// Now reuse the key with a smaller value
544+
mMBC->set("ab", 2, "gh", 2);
545+
546+
// Grab the new size
547+
size_t secondSize = mMBC->getTotalSize();
548+
549+
// Ensure it decreased in size
550+
ASSERT_LT(secondSize, firstSize);
551+
552+
// Verify the cache is correct
553+
ASSERT_EQ(size_t(2), mMBC->get("ab", 2, buf, 2));
554+
ASSERT_EQ('g', buf[0]);
555+
ASSERT_EQ('h', buf[1]);
556+
557+
// Now put back the original value
558+
mMBC->set("ab", 2, "cdef", 4);
559+
560+
// And we should get back a stable size
561+
size_t finalSize = mMBC->getTotalSize();
562+
ASSERT_EQ(firstSize, finalSize);
563+
}
564+
565+
// Ensure cache is correct when a key is reused with large value size
566+
TEST_F(MultifileBlobCacheTest, SameKeyLargeValues) {
567+
if (!flags::multifile_blobcache_advanced_usage()) {
568+
GTEST_SKIP() << "Skipping test that requires multifile_blobcache_advanced_usage flag";
569+
}
570+
571+
// Create the cache with larger limits to stress test reuse
572+
constexpr uint32_t kLocalMaxKeySize = 1 * 1024 * 1024;
573+
constexpr uint32_t kLocalMaxValueSize = 4 * 1024 * 1024;
574+
constexpr uint32_t kLocalMaxTotalSize = 32 * 1024 * 1024;
575+
mMBC.reset(new MultifileBlobCache(kLocalMaxKeySize, kLocalMaxValueSize, kLocalMaxTotalSize,
576+
kMaxTotalEntries, &mTempFile->path[0]));
577+
578+
constexpr uint32_t kLargeValueCount = 8;
579+
constexpr uint32_t kLargeValueSize = 64 * 1024;
580+
581+
// Create a several really large values
582+
unsigned char largeValue[kLargeValueCount][kLargeValueSize];
583+
for (int i = 0; i < kLargeValueCount; i++) {
584+
for (int j = 0; j < kLargeValueSize; j++) {
585+
// Fill the value with the index for uniqueness
586+
largeValue[i][j] = i;
587+
}
588+
}
589+
590+
size_t startingSize = mMBC->getTotalSize();
591+
592+
// New cache should be empty
593+
ASSERT_EQ(startingSize, 0);
594+
595+
// Cycle through the values and set them all in sequence
596+
for (int i = 0; i < kLargeValueCount; i++) {
597+
mMBC->set("abcd", 4, largeValue[i], kLargeValueSize);
598+
}
599+
600+
// Ensure we get the last one back
601+
unsigned char outBuf[kLargeValueSize];
602+
mMBC->get("abcd", 4, outBuf, kLargeValueSize);
603+
604+
for (int i = 0; i < kLargeValueSize; i++) {
605+
// Buffer should contain highest index value
606+
ASSERT_EQ(kLargeValueCount - 1, outBuf[i]);
607+
}
608+
}
609+
512610
} // namespace android

0 commit comments

Comments
 (0)