Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b5267034 authored by Cody Northrop's avatar Cody Northrop
Browse files

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
parent c7e18277
Loading
Loading
Loading
Loading
+14 −7
Original line number Diff line number Diff line
@@ -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;
    }
+5 −2
Original line number Diff line number Diff line
@@ -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;
+26 −9
Original line number Diff line number Diff line
@@ -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
+2 −1
Original line number Diff line number Diff line
@@ -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();
}
+6 −5
Original line number Diff line number Diff line
@@ -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);