mem: Add a simple adaptive version of the open-page policy

This patch adds a basic adaptive version of the open-page policy that
guides the decision to keep open or close by looking at the contents
of the controller queues. If no row hits are found, and bank conflicts
are present, then the row is closed by means of an auto
precharge. This is a well-known technique that should improve
performance in most use-cases.
This commit is contained in:
Andreas Hansson 2013-11-01 11:56:26 -04:00
parent da6fd72f62
commit bb572663cf
2 changed files with 49 additions and 8 deletions

View file

@ -53,8 +53,8 @@ class MemSched(Enum): vals = ['fcfs', 'frfcfs']
# open row. For a closed-page policy, CoRaBaCh maximises parallelism. # open row. For a closed-page policy, CoRaBaCh maximises parallelism.
class AddrMap(Enum): vals = ['RaBaChCo', 'RaBaCoCh', 'CoRaBaCh'] class AddrMap(Enum): vals = ['RaBaChCo', 'RaBaCoCh', 'CoRaBaCh']
# Enum for the page policy, either open or close. # Enum for the page policy, either open, open_adaptive or close.
class PageManage(Enum): vals = ['open', 'close'] class PageManage(Enum): vals = ['open', 'open_adaptive', 'close']
# SimpleDRAM is a single-channel single-ported DRAM controller model # SimpleDRAM is a single-channel single-ported DRAM controller model
# that aims to model the most important system-level performance # that aims to model the most important system-level performance

View file

@ -598,7 +598,8 @@ SimpleDRAM::printParams() const
string scheduler = memSchedPolicy == Enums::fcfs ? "FCFS" : "FR-FCFS"; string scheduler = memSchedPolicy == Enums::fcfs ? "FCFS" : "FR-FCFS";
string address_mapping = addrMapping == Enums::RaBaChCo ? "RaBaChCo" : string address_mapping = addrMapping == Enums::RaBaChCo ? "RaBaChCo" :
(addrMapping == Enums::RaBaCoCh ? "RaBaCoCh" : "CoRaBaCh"); (addrMapping == Enums::RaBaCoCh ? "RaBaCoCh" : "CoRaBaCh");
string page_policy = pageMgmt == Enums::open ? "OPEN" : "CLOSE"; string page_policy = pageMgmt == Enums::open ? "OPEN" :
(pageMgmt == Enums::open_adaptive ? "OPEN (adaptive)" : "CLOSE");
DPRINTF(DRAM, DPRINTF(DRAM,
"Memory controller %s characteristics\n" \ "Memory controller %s characteristics\n" \
@ -924,7 +925,8 @@ SimpleDRAM::estimateLatency(DRAMPacket* dram_pkt, Tick inTime)
Tick potentialActTick; Tick potentialActTick;
const Bank& bank = dram_pkt->bankRef; const Bank& bank = dram_pkt->bankRef;
if (pageMgmt == Enums::open) { // open-page policy // open-page policy
if (pageMgmt == Enums::open || pageMgmt == Enums::open_adaptive) {
if (bank.openRow == dram_pkt->row) { if (bank.openRow == dram_pkt->row) {
// When we have a row-buffer hit, // When we have a row-buffer hit,
// we don't care about tRAS having expired or not, // we don't care about tRAS having expired or not,
@ -955,13 +957,17 @@ SimpleDRAM::estimateLatency(DRAMPacket* dram_pkt, Tick inTime)
if (freeTime > inTime) if (freeTime > inTime)
accLat += freeTime - inTime; accLat += freeTime - inTime;
// If the there is no open row (open adaptive), then there
// is no precharge delay, otherwise go with tRP
Tick precharge_delay = bank.openRow == -1 ? 0 : tRP;
//The bank is free, and you may be able to activate //The bank is free, and you may be able to activate
potentialActTick = inTime + accLat + tRP; potentialActTick = inTime + accLat + precharge_delay;
if (potentialActTick < bank.actAllowedAt) if (potentialActTick < bank.actAllowedAt)
accLat += bank.actAllowedAt - potentialActTick; accLat += bank.actAllowedAt - potentialActTick;
accLat += tRP + tRCD + tCL; accLat += precharge_delay + tRCD + tCL;
bankLat += tRP + tRCD + tCL; bankLat += precharge_delay + tRCD + tCL;
} }
} else if (pageMgmt == Enums::close) { } else if (pageMgmt == Enums::close) {
// With a close page policy, no notion of // With a close page policy, no notion of
@ -1067,7 +1073,7 @@ SimpleDRAM::doDRAMAccess(DRAMPacket* dram_pkt)
Bank& bank = dram_pkt->bankRef; Bank& bank = dram_pkt->bankRef;
// Update bank state // Update bank state
if (pageMgmt == Enums::open) { if (pageMgmt == Enums::open || pageMgmt == Enums::open_adaptive) {
bank.openRow = dram_pkt->row; bank.openRow = dram_pkt->row;
bank.freeAt = curTick() + addDelay + accessLat; bank.freeAt = curTick() + addDelay + accessLat;
bank.bytesAccessed += burstSize; bank.bytesAccessed += burstSize;
@ -1085,6 +1091,41 @@ SimpleDRAM::doDRAMAccess(DRAMPacket* dram_pkt)
bytesPerActivate.sample(bank.bytesAccessed); bytesPerActivate.sample(bank.bytesAccessed);
bank.bytesAccessed = 0; bank.bytesAccessed = 0;
} }
if (pageMgmt == Enums::open_adaptive) {
// a twist on the open page policy is to not blindly keep the
// page open, but close it if there are no row hits, and there
// are bank conflicts in the queue
bool got_more_hits = false;
bool got_bank_conflict = false;
// either look at the read queue or write queue
const deque<DRAMPacket*>& queue = dram_pkt->isRead ? readQueue :
writeQueue;
auto p = queue.begin();
// make sure we are not considering the packet that we are
// currently dealing with (which is the head of the queue)
++p;
// keep on looking until we have found both or reached
// the end
while (!(got_more_hits && got_bank_conflict) &&
p != queue.end()) {
bool same_rank_bank = (dram_pkt->rank == (*p)->rank) &&
(dram_pkt->bank == (*p)->bank);
bool same_row = dram_pkt->row == (*p)->row;
got_more_hits |= same_rank_bank && same_row;
got_bank_conflict |= same_rank_bank && !same_row;
++p;
}
// auto pre-charge
if (!got_more_hits && got_bank_conflict) {
bank.openRow = -1;
bank.freeAt = std::max(bank.freeAt, bank.tRASDoneAt) + tRP;
}
}
DPRINTF(DRAM, "doDRAMAccess::bank.freeAt is %lld\n", bank.freeAt); DPRINTF(DRAM, "doDRAMAccess::bank.freeAt is %lld\n", bank.freeAt);
} else if (pageMgmt == Enums::close) { } else if (pageMgmt == Enums::close) {
actTick = curTick() + addDelay + accessLat - tRCD - tCL; actTick = curTick() + addDelay + accessLat - tRCD - tCL;