Files
DRAMSys/DRAMSys/simulator/src/controller/scheduler/SMS.cpp

346 lines
10 KiB
C++

#include "SMS.h"
#include <random>
using namespace std;
void SMS::schedule(gp *payload)
{
Thread thread = DramExtension::getExtension(payload).getThread();
bool wasEmpty = isRequestBuffersEmpty();
requestBuffers[thread].emplace_back(payload);
if (inFlightMemRequestCounter.find(thread) == inFlightMemRequestCounter.end()) {
inFlightMemRequestCounter[thread] = 0;
cacheMisses[thread] = 0;
}
inFlightMemRequestCounter[thread]++;
cacheMisses[thread]++;
if (wasEmpty) {
newRequest.notify(SC_ZERO_TIME);
}
}
std::pair<Command, gp*> SMS::getNextRequest(Bank bank)
{
if (bankBuffers[bank].empty())
{
debugManager.printDebugMessage(name(),
"Get next request on bank " + to_string(bank.ID()) + " : EMPTY buffer");
return pair<Command, tlm::tlm_generic_payload*>(Command::NOP, NULL);
}
else
{
gp* payload = bankBuffers[bank].front();
Command command = IScheduler::getNextCommand(*payload);
if (command == Command::Read || command == Command::ReadA || command == Command::Write
|| command == Command::WriteA)
{
inFlightMemRequestCounter[DramExtension::getExtension(payload).getThread()]--;
bankBuffers[bank].pop_front();
}
debugManager.printDebugMessage(name(), "Get next request on bank " + to_string(bank.ID()));
return pair<Command, tlm::tlm_generic_payload*>(command, payload);
}
}
void SMS::batchScheduler()
{
sc_time memClk = Configuration::getInstance().memSpec.clk;
std::default_random_engine generator;
std::bernoulli_distribution distribution((double) SJFprobability / 100.0);
while (true)
{
updateMPKCs(memClk);
if (isRequestBuffersEmpty()) {
wait(newRequest);
} else {
multiBatchFormation(memClk);
if (existReadyBatches()) {
if (!isSystemLightlyLoaded() && (existLowIntensityThread() || distribution(generator))) {
if (pickSJF()) {
drain(memClk, (*lastSelectedThread).second.front());
(*lastSelectedThread).second.pop_front();
}
} else {
if (pickRR()) {
drain(memClk, (*lastSelectedThread).second.front());
(*lastSelectedThread).second.pop_front();
}
}
} else {
wait(memClk);
}
}
}
}
/**
* Pick a Thread according to Shortest-Job Policy
* Save the picked one into lastSelectedThread
* @return true if it can, otherwise false
*/
bool SMS::pickSJF()
{
// find threads with ready batches
std::vector<Thread> threadsWithReadyBatches;
for (auto& each : readyBatchIters)
{
if (!each.second.empty())
{
// marked as thread with non-empty request buffer
threadsWithReadyBatches.push_back(each.first);
}
}
if (!threadsWithReadyBatches.empty())
{
// pick shortest-job thread among threads with non-empty request buffer
Thread minThread = threadsWithReadyBatches.front();
for (auto& thread : threadsWithReadyBatches)
{
if (inFlightMemRequestCounter[thread] < inFlightMemRequestCounter[minThread])
{
minThread = thread;
}
}
// save selected thread
lastSelectedThread = readyBatchIters.find(minThread);
debugManager.printDebugMessage(name(),
"[SJF] Select ready batch of thread " + to_string(minThread.ID()));
return true;
}
else
{
return false;
}
}
/**
* Drain the picked request buffer into bank buffers
* by move request one-by-one from start of the request buffer till last parameter
* @param memClk
* @param last
*/
void SMS::drain(sc_time memClk, std::deque<gp*>::iterator last)
{
Thread selectedThread = (*lastSelectedThread).first;
unsigned int batchSize = std::distance(requestBuffers[selectedThread].begin(), last) + 1;
for (unsigned int i = 1; i <= batchSize; i++)
{
Bank bank = DramExtension::getExtension(requestBuffers[selectedThread].front()).getBank();
// if(bankBuffers[bank].size() == Configuration::getInstance().BankBufferSize)
// {
// wait(bankBufferIsNotFull);
// }
wait(memClk);
bankBuffers[bank].emplace_back(requestBuffers[selectedThread].front());
requestBuffers[selectedThread].pop_front();
debugManager.printDebugMessage(name(),
"[SJF] Drain request in the ready batch of thread "
+ to_string((*lastSelectedThread).first.ID()) + " to bankbuffer "
+ to_string(bank.ID()));
}
}
/**
* Pick a Thread according to Round-Robin Policy
* Save the picked one into lastSelectedThread
* @return true if it can pick one, otherwise false
*/
bool SMS::pickRR()
{
std::map<Thread, std::deque<gp_deque_iterator>>::iterator nextSelectedThread;
if (lastSelectedThread == readyBatchIters.end())
{
lastSelectedThread = readyBatchIters.begin();
nextSelectedThread = lastSelectedThread;
}
else
{
nextSelectedThread = lastSelectedThread;
nextSelectedThread++;
if (nextSelectedThread == readyBatchIters.end())
nextSelectedThread = readyBatchIters.begin();
}
std::map<Thread, std::deque<gp_deque_iterator>>::iterator savedOriginalNextSelectedThread = nextSelectedThread;
while ((*nextSelectedThread).second.empty())
{
nextSelectedThread++;
if (nextSelectedThread == readyBatchIters.end())
{
nextSelectedThread = readyBatchIters.begin();
}
if (nextSelectedThread == savedOriginalNextSelectedThread)
{
// the next thread is the original thread, that mean req buffer are totally empty
// non-existed ready batch to be picked up & drained
return false;
}
}
// save last selected thread
lastSelectedThread = nextSelectedThread;
debugManager.printDebugMessage(name(),
"[RR] Select ready batch of thread " + to_string((*nextSelectedThread).first.ID()));
return true;
}
bool SMS::isSystemLightlyLoaded() {
unsigned int totalRequest = 0;
for (auto& bankBuffer : bankBuffers) {
totalRequest += bankBuffer.second.size();
}
return (totalRequest <= LOW_SYSTEM_LOAD);
}
bool SMS::existLowIntensityThread() {
for (auto& mpkcPerThread : MPKCs) {
if (mpkcPerThread.second < LOW_MPKC) {
return true;
}
}
return false;
}
bool SMS::isThresholdAgeExceeded(Thread thread, sc_time memClk, std::deque<gp*>::iterator begin, std::deque<gp*>::iterator end) {
// find the oldest request in the thread's batch
sc_time oldestGenerationTime = sc_time_stamp();
for (auto reqIter = begin; reqIter != end; reqIter++) {
sc_time reqGenerationTime = GenerationExtension::getExtension(*reqIter).TimeOfGeneration();
if (reqGenerationTime < oldestGenerationTime) {
oldestGenerationTime = reqGenerationTime;
}
}
// check threshold age according to the thread's MPKC
sc_time oldestRequestAge = sc_time_stamp() - oldestGenerationTime;
if ((MPKCs[thread] <= MEDIUM_MPKC) && (oldestRequestAge > (MEDIUM_THRESHOLD_AGE * memClk))) {
return true;
} else if ((MPKCs[thread] > MEDIUM_MPKC) && (oldestRequestAge > (HIGH_THRESHOLD_AGE * memClk))) {
return true;
} else {
return false;
}
}
void SMS::updateMPKCs(sc_time memClk) {
if (sc_time_stamp() % (MPKC_RESET_CYCLE * memClk) <= memClk) {
// reset for every 10k clk cycles
for (auto& cacheMiss : cacheMisses) {
MPKCs[cacheMiss.first] = 0;
}
debugManager.printDebugMessage(name(), "Reset MKKCs");
} else {
// update MPKC for every thread
for (auto& cacheMiss : cacheMisses) {
MPKCs[cacheMiss.first] = (cacheMiss.second * 1000.0 * memClk) / (sc_time_stamp());
}
debugManager.printDebugMessage(name(), "Update MPKCs");
}
}
bool SMS::isExceededReqBufferSize(Thread thread) {
return requestBuffers[thread].size() == Configuration::getInstance().RequestBufferSize;
}
bool SMS::isRequestBuffersEmpty() {
for (auto& requestBuffer : requestBuffers) {
if (!requestBuffer.second.empty()) {
return false;
}
}
return true;
}
bool SMS::existReadyBatches() {
for (auto& each : readyBatchIters) {
if (!each.second.empty()) {
return true;
}
}
return false;
}
/**
* Form batch from begin iterator parameter of a request buffer
* If this batch is deemed ready, save the iterator pointing to its last element
* @param memClk
* @param begin
* @return true if this batch is ready, otherwise false
*/
bool SMS::batchFormation(sc_time memClk, std::pair<Thread, std::deque<gp*>> &requestBuffer,
std::deque<gp*>::iterator beginIter)
{
if (requestBuffer.second.empty())
{
return false;
}
if (requestBuffer.second.end() == beginIter)
{
return false;
}
if (MPKCs[requestBuffer.first] < LOW_MPKC || isSystemLightlyLoaded())
{
// bypass requests by forming batch with only one request (threshold age is ZERO)
readyBatchIters[requestBuffer.first].push_back(beginIter);
return true;
}
else
{
// forming batch with FIFO size & threshold age constraints
auto firstDifferentRowAccessReqIter = beginIter;
Row firstRow = DramExtension::getRow(*beginIter);
while (firstDifferentRowAccessReqIter != requestBuffer.second.end()
&& DramExtension::getRow(*firstDifferentRowAccessReqIter) == firstRow)
{
firstDifferentRowAccessReqIter++;
}
// deem this batch ready
if ((firstDifferentRowAccessReqIter != requestBuffer.second.end())
|| isExceededReqBufferSize(requestBuffer.first)
|| isThresholdAgeExceeded(requestBuffer.first, memClk, beginIter,
firstDifferentRowAccessReqIter))
{
firstDifferentRowAccessReqIter--;
readyBatchIters[requestBuffer.first].push_back(firstDifferentRowAccessReqIter);
debugManager.printDebugMessage(name(),
"Deem batch ready - thread " + to_string(requestBuffer.first.ID()));
return true;
}
else
{
return false;
}
}
}
void SMS::multiBatchFormation(sc_time memClk)
{
for (auto& requestBuffer : requestBuffers)
{
bool formed;
do
{
if (readyBatchIters[requestBuffer.first].empty())
{
formed = batchFormation(memClk, (std::pair<Thread, std::deque<gp*>>&)requestBuffer, requestBuffer.second.begin());
}
else
{
formed = batchFormation(memClk, (std::pair<Thread, std::deque<gp*>>&)requestBuffer, readyBatchIters[requestBuffer.first].back() + 1);
}
} while (formed);
}
}