mem: Add a simple adaptive version of the open-page policy
authorAndreas Hansson <andreas.hansson@arm.com>
Fri, 1 Nov 2013 15:56:26 +0000 (11:56 -0400)
committerAndreas Hansson <andreas.hansson@arm.com>
Fri, 1 Nov 2013 15:56:26 +0000 (11:56 -0400)
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.

src/mem/SimpleDRAM.py
src/mem/simple_dram.cc

index c450f89924bc6fa6bfdadda01ed4ff43cabe2563..b16aa600302a37a12bae553422bc5d61b2e06403 100644 (file)
@@ -53,8 +53,8 @@ class MemSched(Enum): vals = ['fcfs', 'frfcfs']
 # open row. For a closed-page policy, CoRaBaCh maximises parallelism.
 class AddrMap(Enum): vals = ['RaBaChCo', 'RaBaCoCh', 'CoRaBaCh']
 
-# Enum for the page policy, either open or close.
-class PageManage(Enum): vals = ['open', 'close']
+# Enum for the page policy, either open, open_adaptive or close.
+class PageManage(Enum): vals = ['open', 'open_adaptive', 'close']
 
 # SimpleDRAM is a single-channel single-ported DRAM controller model
 # that aims to model the most important system-level performance
index 2d46b8522d85aa335e5f42e7295b0f90b156e423..69209fb8a183ecb435f48bdabf45d41f82ae8e70 100644 (file)
@@ -598,7 +598,8 @@ SimpleDRAM::printParams() const
     string scheduler =  memSchedPolicy == Enums::fcfs ? "FCFS" : "FR-FCFS";
     string address_mapping = addrMapping == Enums::RaBaChCo ? "RaBaChCo" :
         (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,
             "Memory controller %s characteristics\n"    \
@@ -924,7 +925,8 @@ SimpleDRAM::estimateLatency(DRAMPacket* dram_pkt, Tick inTime)
     Tick potentialActTick;
 
     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) {
             // When we have a row-buffer hit,
             // we don't care about tRAS having expired or not,
@@ -955,13 +957,17 @@ SimpleDRAM::estimateLatency(DRAMPacket* dram_pkt, Tick inTime)
             if (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
-            potentialActTick = inTime + accLat + tRP;
+            potentialActTick = inTime + accLat + precharge_delay;
             if (potentialActTick < bank.actAllowedAt)
                 accLat += bank.actAllowedAt - potentialActTick;
 
-            accLat += tRP + tRCD + tCL;
-            bankLat += tRP + tRCD + tCL;
+            accLat += precharge_delay + tRCD + tCL;
+            bankLat += precharge_delay + tRCD + tCL;
         }
     } else if (pageMgmt == Enums::close) {
         // With a close page policy, no notion of
@@ -1067,7 +1073,7 @@ SimpleDRAM::doDRAMAccess(DRAMPacket* dram_pkt)
     Bank& bank = dram_pkt->bankRef;
 
     // Update bank state
-    if (pageMgmt == Enums::open) {
+    if (pageMgmt == Enums::open || pageMgmt == Enums::open_adaptive) {
         bank.openRow = dram_pkt->row;
         bank.freeAt = curTick() + addDelay + accessLat;
         bank.bytesAccessed += burstSize;
@@ -1085,6 +1091,41 @@ SimpleDRAM::doDRAMAccess(DRAMPacket* dram_pkt)
             bytesPerActivate.sample(bank.bytesAccessed);
             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);
     } else if (pageMgmt == Enums::close) {
         actTick = curTick() + addDelay + accessLat - tRCD - tCL;