blob: 3c74907f25ef4fc0ab12c0cff0e1b9b6947b0f3e [file]
// Copyright 2024 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "mpact/sim/util/memory/cache.h"
#include <cstdint>
#include "absl/log/check.h"
#include "googlemock/include/gmock/gmock.h" // IWYU pragma: keep
#include "googletest/include/gtest/gtest.h"
#include "mpact/sim/generic/counters.h"
#include "mpact/sim/generic/data_buffer.h"
namespace {
using ::mpact::sim::generic::DataBuffer;
using ::mpact::sim::generic::DataBufferFactory;
using ::mpact::sim::generic::SimpleCounter;
using ::mpact::sim::util::Cache;
class CacheTest : public testing::Test {
protected:
CacheTest() : cycle_counter_("cycle_counter", 0ULL) {
// Create a cache 16kB, 16B blocks, direct mapped.
cache_ = new Cache("cache");
db_ = db_factory_.Allocate<uint32_t>(1);
read_hits_ = reinterpret_cast<SimpleCounter<uint64_t> *>(
cache_->GetCounter("read_hit"));
read_misses_ = reinterpret_cast<SimpleCounter<uint64_t> *>(
cache_->GetCounter("read_miss"));
write_hits_ = reinterpret_cast<SimpleCounter<uint64_t> *>(
cache_->GetCounter("write_hit"));
write_misses_ = reinterpret_cast<SimpleCounter<uint64_t> *>(
cache_->GetCounter("write_miss"));
dirty_line_writebacks_ = reinterpret_cast<SimpleCounter<uint64_t> *>(
cache_->GetCounter("dirty_line_writeback"));
read_arounds_ = reinterpret_cast<SimpleCounter<uint64_t> *>(
cache_->GetCounter("read_around"));
write_arounds_ = reinterpret_cast<SimpleCounter<uint64_t> *>(
cache_->GetCounter("write_around"));
}
~CacheTest() override {
delete cache_;
db_->DecRef();
}
DataBufferFactory db_factory_;
DataBuffer *db_;
Cache *cache_;
SimpleCounter<uint64_t> *read_hits_;
SimpleCounter<uint64_t> *read_misses_;
SimpleCounter<uint64_t> *write_hits_;
SimpleCounter<uint64_t> *write_misses_;
SimpleCounter<uint64_t> *dirty_line_writebacks_;
SimpleCounter<uint64_t> *read_arounds_;
SimpleCounter<uint64_t> *write_arounds_;
SimpleCounter<uint64_t> cycle_counter_;
};
TEST_F(CacheTest, DirectMappedReadsCold) {
// Create a cache 16kB, 16B blocks, direct mapped.
CHECK_OK(cache_->Configure("1k,16,1,true", &cycle_counter_));
for (uint64_t address = 0; address < 1024; address += 4) {
cache_->Load(address, db_, nullptr, nullptr);
}
uint64_t refs = 1024 / 4;
EXPECT_EQ(read_misses_->GetValue(), refs / 4);
EXPECT_EQ(read_hits_->GetValue(), (refs / 4) * 3);
}
TEST_F(CacheTest, DirectMappedReadsWarm) {
// Create a cache 16kB, 16B blocks, direct mapped.
CHECK_OK(cache_->Configure("1k,16,1,true", &cycle_counter_));
// Warm the cache.
for (uint64_t address = 0; address < 1024; address += 4) {
cache_->Load(address, db_, nullptr, nullptr);
}
// Clear the counters.
read_misses_->SetValue(0);
read_hits_->SetValue(0);
// Access the cache again. Should be all hits.
for (uint64_t address = 0; address < 1024; address += 4) {
cache_->Load(address, db_, nullptr, nullptr);
}
uint64_t refs = 1024 / 4;
EXPECT_EQ(read_misses_->GetValue(), 0);
EXPECT_EQ(read_hits_->GetValue(), refs);
// Clear the counters.
read_misses_->SetValue(0);
read_hits_->SetValue(0);
// Access the next 1k, should be like a cold cache.
for (uint64_t address = 1024; address < 2048; address += 4) {
cache_->Load(address, db_, nullptr, nullptr);
}
EXPECT_EQ(read_misses_->GetValue(), refs / 4);
EXPECT_EQ(read_hits_->GetValue(), (refs / 4) * 3);
}
TEST_F(CacheTest, TwoWayReads) {
// Create a cache 16kB, 16B blocks, two way set associative.
CHECK_OK(cache_->Configure("1k,16,2,true", &cycle_counter_));
// Fill half the cache.
for (uint64_t address = 0; address < 512; address += 16) {
cache_->Load(address, db_, nullptr, nullptr);
cache_->Load(address + 1024, db_, nullptr, nullptr);
}
EXPECT_EQ(read_misses_->GetValue(), 2 * 512 / 16);
EXPECT_EQ(read_hits_->GetValue(), 0);
// Clear the counters.
read_misses_->SetValue(0);
read_hits_->SetValue(0);
// All these references should hit.
for (uint64_t address = 0; address < 512; address += 16) {
cache_->Load(address, db_, nullptr, nullptr);
cache_->Load(address + 1024, db_, nullptr, nullptr);
}
EXPECT_EQ(read_misses_->GetValue(), 0);
EXPECT_EQ(read_hits_->GetValue(), 2 * 512 / 16);
}
} // namespace