Cache替換策略


LRU, Least Recently Used, LRU算法根據各block(cache line)使用的情況, 總是選擇那個最長時間未被使用的block進行替換。這種策略比較好的反映了程序局部性規律。

gem5中該替換策略的代碼:

void
LRURP::invalidate(const std::shared_ptr<ReplacementData>& replacement_data)
const
{
    // Reset last touch timestamp
    std::static_pointer_cast<LRUReplData>(
        replacement_data)->lastTouchTick = Tick(0);
}

void
LRURP::touch(const std::shared_ptr<ReplacementData>& replacement_data) const
{
    // Update last touch timestamp
    std::static_pointer_cast<LRUReplData>(
        replacement_data)->lastTouchTick = curTick();
}

void
LRURP::reset(const std::shared_ptr<ReplacementData>& replacement_data) const
{
    // Set last touch timestamp
    std::static_pointer_cast<LRUReplData>(
        replacement_data)->lastTouchTick = curTick();
}

ReplaceableEntry*
LRURP::getVictim(const ReplacementCandidates& candidates) const
{
    // There must be at least one replacement candidate
    assert(candidates.size() > 0);

    // Visit all candidates to find victim
    ReplaceableEntry* victim = candidates[0];
    for (const auto& candidate : candidates) {
        // Update victim entry if necessary
        if (std::static_pointer_cast<LRUReplData>(
                    candidate->replacementData)->lastTouchTick <
                std::static_pointer_cast<LRUReplData>(
                    victim->replacementData)->lastTouchTick) {
            victim = candidate;
        }
    }

    return victim;
}

View Code

MRU(Most Recently Used)和LRU類似,差別在於選擇最近被使用的block進行替換。

gem5中該替換策略的代碼:

void
MRURP::invalidate(const std::shared_ptr<ReplacementData>& replacement_data)
const
{
    // Reset last touch timestamp
    std::static_pointer_cast<MRUReplData>(
        replacement_data)->lastTouchTick = Tick(0);
}

void
MRURP::touch(const std::shared_ptr<ReplacementData>& replacement_data) const
{
    // Update last touch timestamp
    std::static_pointer_cast<MRUReplData>(
        replacement_data)->lastTouchTick = curTick();
}

void
MRURP::reset(const std::shared_ptr<ReplacementData>& replacement_data) const
{
    // Set last touch timestamp
    std::static_pointer_cast<MRUReplData>(
        replacement_data)->lastTouchTick = curTick();
}

ReplaceableEntry*
MRURP::getVictim(const ReplacementCandidates& candidates) const
{
    // There must be at least one replacement candidate
    assert(candidates.size() > 0);

    // Visit all candidates to find victim
    ReplaceableEntry* victim = candidates[0];
    for (const auto& candidate : candidates) {
        std::shared_ptr<MRUReplData> candidate_replacement_data =
            std::static_pointer_cast<MRUReplData>(candidate->replacementData);

        // Stop searching entry if a cache line that doesn't warm up is found.
        if (candidate_replacement_data->lastTouchTick == 0) {
            victim = candidate;
            break;
        } else if (candidate_replacement_data->lastTouchTick >
                std::static_pointer_cast<MRUReplData>(
                    victim->replacementData)->lastTouchTick) {
            victim = candidate;
        }
    }

    return victim;
}

View Code

Random,隨機選擇一個block進行替換。

gem5中該替換策略的代碼:

void
RandomRP::invalidate(const std::shared_ptr<ReplacementData>& replacement_data)
const
{
    // Unprioritize replacement data victimization
    std::static_pointer_cast<RandomReplData>(
        replacement_data)->valid = false;
}

void
RandomRP::touch(const std::shared_ptr<ReplacementData>& replacement_data) const
{
}

void
RandomRP::reset(const std::shared_ptr<ReplacementData>& replacement_data) const
{
    // Unprioritize replacement data victimization
    std::static_pointer_cast<RandomReplData>(
        replacement_data)->valid = true;
}

ReplaceableEntry*
RandomRP::getVictim(const ReplacementCandidates& candidates) const
{
    // There must be at least one replacement candidate
    assert(candidates.size() > 0);

    // Choose one candidate at random
    ReplaceableEntry* victim = candidates[random_mt.random<unsigned>(0,
                                    candidates.size() - 1)];

    // Visit all candidates to search for an invalid entry. If one is found,
    // its eviction is prioritized
    for (const auto& candidate : candidates) {
        if (!std::static_pointer_cast<RandomReplData>(
                    candidate->replacementData)->valid) {
            victim = candidate;
            break;
        }
    }

View Code

LFU(Least Frequently Used),最近最少被使用次數的block被替換,每個block都有一個引用計數,每次替換該block,都會對該計數加1。

gem5中該替換策略的代碼:

void
LFURP::invalidate(const std::shared_ptr<ReplacementData>& replacement_data)
const
{
    // Reset reference count
    std::static_pointer_cast<LFUReplData>(replacement_data)->refCount = 0;
}

void
LFURP::touch(const std::shared_ptr<ReplacementData>& replacement_data) const
{
    // Update reference count
    std::static_pointer_cast<LFUReplData>(replacement_data)->refCount++;
}

void
LFURP::reset(const std::shared_ptr<ReplacementData>& replacement_data) const
{
    // Reset reference count
    std::static_pointer_cast<LFUReplData>(replacement_data)->refCount = 1;
}

ReplaceableEntry*
LFURP::getVictim(const ReplacementCandidates& candidates) const
{
    // There must be at least one replacement candidate
    assert(candidates.size() > 0);

    // Visit all candidates to find victim
    ReplaceableEntry* victim = candidates[0];
    for (const auto& candidate : candidates) {
        // Update victim entry if necessary
        if (std::static_pointer_cast<LFUReplData>(
                    candidate->replacementData)->refCount <
                std::static_pointer_cast<LFUReplData>(
                    victim->replacementData)->refCount) {
            victim = candidate;
        }
    }

    return victim;
}
View Code

FIFO(First in First out), 最先使用過的block,最先被替換。

gem5中該替換策略的代碼:

void
FIFORP::invalidate(const std::shared_ptr<ReplacementData>& replacement_data)
const
{
    // Reset insertion tick
    std::static_pointer_cast<FIFOReplData>(
        replacement_data)->tickInserted = Tick(0);
}

void
FIFORP::touch(const std::shared_ptr<ReplacementData>& replacement_data) const
{
    // A touch does not modify the insertion tick
}

void
FIFORP::reset(const std::shared_ptr<ReplacementData>& replacement_data) const
{
    // Set insertion tick
    std::static_pointer_cast<FIFOReplData>(
        replacement_data)->tickInserted = curTick();
}

ReplaceableEntry*
FIFORP::getVictim(const ReplacementCandidates& candidates) const
{
    // There must be at least one replacement candidate
    assert(candidates.size() > 0);

    // Visit all candidates to find victim
    ReplaceableEntry* victim = candidates[0];
    for (const auto& candidate : candidates) {
        // Update victim entry if necessary
        if (std::static_pointer_cast<FIFOReplData>(
                    candidate->replacementData)->tickInserted <
                std::static_pointer_cast<FIFOReplData>(
                    victim->replacementData)->tickInserted) {
            victim = candidate;
        }
    }

    return victim;
}
View Code

BIP,(Bimodal Insertion Policy)替換策略,是LRU和MRU的結合體,大概率采用MRU替換,小概率采用LRU策略。

gem5中該替換策略的代碼

struct BIPRPParams;

class BIPRP : public LRURP
{
  protected:
    /**
     * Bimodal throtle parameter. Value in the range [0,100] used to decide
     * if a new entry is inserted at the MRU or LRU position.
     */
    const unsigned btp;

  public:
    /** Convenience typedef. */
    typedef BIPRPParams Params;

    /**
     * Construct and initiliaze this replacement policy.
     */
    BIPRP(const Params *p);

    /**
     * Destructor.
     */
    ~BIPRP() {}

    /**
     * Reset replacement data for an entry. Used when an entry is inserted.
     * Uses the bimodal throtle parameter to decide whether the new entry
     * should be inserted as MRU, or LRU.
     *
     * @param replacement_data Replacement data to be reset.
     */
    void reset(const std::shared_ptr<ReplacementData>& replacement_data) const
                                                                     override;
};
void
BIPRP::reset(const std::shared_ptr<ReplacementData>& replacement_data) const
{
    std::shared_ptr<LRUReplData> casted_replacement_data =
        std::static_pointer_cast<LRUReplData>(replacement_data);

    // Entries are inserted as MRU if lower than btp, LRU otherwise
    if (random_mt.random<unsigned>(1, 100) <= btp) {
        casted_replacement_data->lastTouchTick = curTick();
    } else {
        // Make their timestamps as old as possible, so that they become LRU
        casted_replacement_data->lastTouchTick = 1;
    }
}
View Code


NRU(Not Recent Used) 是LRU的一個近似策略,被廣泛應用於現代高性能處理器中。應用NRU策略的cache,需要在每個cache block中增加一位標記,該標記(NRU bit)“0”表示最近可能被訪問到的,“1”表示最近不能訪問到的。

每當一個cache hit,該cache block的NRU bit被設置為“0”表示在最近的將來,該cache block很有可能再被訪問到;每當一個cache miss,替換算法會從左至右掃描NRU bit為“1”的block,如果找到則替換出該cache block,並將新插入的cache block 的NRU bit置為“0”,如果沒有找到,那么將所有cache block的NRU bit置為“1”,重新從左至右掃描。

STATIC RRIP, 該替換策略是對NRU的擴展,其將NRU bit擴展成M位,當M=1時,該算法蛻化成NRU。而擴展成M位的原因是為了更細粒度的區分cache block,而不是只有兩個狀態(最近將要訪問和最近最遠將要訪問)。

該算法的描述和NRU相同,每當一個cache hit,該cache block的NRU bit被設置為“0”表示在最近的將來,該cache block很有可能再被訪問到;每當一個cache miss,替換算法會從左至右掃描NRU bit為“2^M -1”的block,如果找到則替換出該cache block,並將新插入的cache block 的NRU bit置為“2^M -2”,如果沒有找到,那么將所有cache block的NRU bit增加1,重新從左至右掃描。

上面將新插入的cache block設置為“2^M -2”,主要是為了防止那些很久才能被再次使用到的cache block長期占用cache空間, 但這樣確實會影響那些空間局部性很好的程序的性能。

在RRIP類的策略中,NRU bit被描述為RRPV(Re- reference Prediction Values),可以理解為當前block被替換出去的可能性,越高越容易被替換出去。


DYNAMIC RRIP(Bimodal RRIP),  對Static RRIP來講,如果程序的工作集大於cache容量,那么將會頻繁的換進換出,造成抖動。為此,Bimodal RRIP提出,對於新插入的cache block,以較大概率設置NRU bits為“2^M -1",同時以較小概率設置為”2^M -2",一次來避免抖動。

那么對於混合的訪存序列,應該使用SRRIP還是BRRIP的問題,一種稱之為“set Dueling”的技術將兩種技術應用到不同的兩個cache set上,然后統計兩個set上的運行情況(主要是命中率),然后來決斷到底使用兩種技術中的哪一個,然后將該算法策略部署到其余各個set上。

GEM5中也有BRRIP替換策略的實現。











免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM