From 3e628dd1c0e81906bdb0aea9d5c66761b64fd78d Mon Sep 17 00:00:00 2001 From: Giacomo Travaglini Date: Tue, 5 Nov 2024 08:49:17 +0000 Subject: [PATCH] arch-arm: Cache a pointer to previously matched TLB entry (#1752) One of the perks of the previous TLB storage implementation [1] is that its custom implementation of LRU exploited temporal locality to speed up simulation performance TlbEntry tmp_entry = *entry; for (int i = idx; i > 0; i--) table[i] = table[i - 1]; table[0] = tmp_entry; return &table[0]; In other words the matching entry was placed as the first entry of the TLB table (table[0], top of LRU stack). In this way a following lookup would encounter it as the first entry while looping over the TLB table, therefore massively reducing simulation time when temporal locality is present (most of TLB table loops would find a match in the first iteration). int x = 0; while (x < size) { if (table[x].match(lookup_data)) { With the new implementation we decouple TLB storage from the replacement policy. The result is a more flexible implementation but with the drawback of a slower lookup/search. We therefore we need to find another way to exploit temporal locality. This patch addresses it by caching a previously matched entry in the TLB table [1]: https://github.com/gem5/gem5/blob/v24.0.0.0/src/arch/arm/tlb.cc Change-Id: Id7dedf5411ea6f6724d1e4bdb51635417a6d5363 Signed-off-by: Giacomo Travaglini --- src/arch/arm/tlb.cc | 23 ++++++++++++++++++++--- src/arch/arm/tlb.hh | 15 +++++++++++++++ 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/src/arch/arm/tlb.cc b/src/arch/arm/tlb.cc index 24819d6ec0..927dc17ae8 100644 --- a/src/arch/arm/tlb.cc +++ b/src/arch/arm/tlb.cc @@ -73,21 +73,31 @@ TLB::Table::accessEntry(const KeyType &key) TlbEntry* TLB::Table::findEntry(const KeyType &key) const { - auto candidates = indexingPolicy->getPossibleEntries(key); + // Exploiting locality to maximaize simulator performance + if (prev && prev->N == key.pageSize && prev->match(key)) { + return prev; + } - for (auto candidate : candidates) { + for (auto candidate : indexingPolicy->getPossibleEntries(key)) { auto entry = static_cast(candidate); // We check for pageSize match outside of the Entry::match // as the latter is also used to match entries in TLBI invalidation // where we don't care about the pageSize if (entry->N == key.pageSize && entry->match(key)) { - return entry; + return prev = entry; } } return nullptr; } +void +TLB::Table::invalidatePrev(const TlbEntry *invalid) +{ + if (!invalid || invalid == prev) + prev = nullptr; +} + TLB::TLB(const ArmTLBParams &p) : BaseTLB(p), table(name().c_str(), p.size, p.assoc, @@ -239,6 +249,8 @@ TLB::insert(const Lookup &lookup_data, TlbEntry &entry) table.insertEntry(lookup_data, victim); + table.invalidatePrev(victim); + observedPageSizes.insert(entry.N); stats.inserts++; ppRefills->notify(1); @@ -280,6 +292,8 @@ TLB::flushAll() } } + table.invalidatePrev(); + stats.flushTlb++; observedPageSizes.clear(); } @@ -292,6 +306,9 @@ TLB::flush(const TLBIOp& tlbi_op) if (tlbi_op.match(&te, vmid)) { DPRINTF(TLB, " - %s\n", te.print()); table.invalidate(&te); + + table.invalidatePrev(&te); + stats.flushedEntries++; } valid_entry = valid_entry || te.valid; diff --git a/src/arch/arm/tlb.hh b/src/arch/arm/tlb.hh index e3307df3f0..6e4babb4a6 100644 --- a/src/arch/arm/tlb.hh +++ b/src/arch/arm/tlb.hh @@ -110,6 +110,21 @@ class TLB : public BaseTLB using AssociativeCache::accessEntry; TlbEntry* accessEntry(const KeyType &key) override; TlbEntry* findEntry(const KeyType &key) const override; + + /** + * Invalidate the last matched entry + * The method has an optional param, which means: invalidate + * cached prev only if matches the entry argument. This is + * to be used for example on TLB entry invalidations + * + * @param invalid flush prev if param is nullptr, otherwise + * only if prev == invalid + */ + void invalidatePrev(const TlbEntry *invalid=nullptr); + + private: + /** Last matched entry */ + mutable TlbEntry *prev = nullptr; } table; /** TLB Size */