346 lines
10 KiB
C++
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);
|
|
}
|
|
}
|