mem-ruby: dequeue rate limit for message buffers

The 'max_dequeue_rate' parameter limits the rate at which messages can
be dequeued in a single cycle. When set, 'isReady' returns false if
after max_dequeue_rate is reached.

This can be used to fine tune the performance of cache controllers.

For the record, other ways of achieving a similar effect could be:
1) Modifying the SLICC compiler to limit message consumption in the
   generated wakeup() function
2) Set the buffer size to max_dequeue_rate. This can potentially cut the
   the expected throughput in half. For instance if a producer can
   enqueue every cycle, and a consumer can dequeue every cycle, a
   message can only be actually enqueued every two (assuming
   buffer_size=1) since the buffer entries available after dequeue
   are only visible in the next cycle (even if the consumer executes
   before the producer).

JIRA: https://gem5.atlassian.net/browse/GEM5-920

Change-Id: I3a446c7276b80a0e3f409b4fbab0ab65ff5c1f81
Signed-off-by: Tiago Mück <tiago.muck@arm.com>
Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/41862
Reviewed-by: Meatboy 106 <garbage2collector@gmail.com>
Maintainer: Bobby Bruce <bbruce@ucdavis.edu>
Tested-by: kokoro <noreply+kokoro@google.com>
This commit is contained in:
Tiago Mück
2020-07-22 19:20:08 -05:00
committed by Tiago Muck
parent 2fed34d099
commit 87cdf354be
3 changed files with 28 additions and 5 deletions

View File

@@ -58,8 +58,9 @@ namespace ruby
using stl_helpers::operator<<;
MessageBuffer::MessageBuffer(const Params &p)
: SimObject(p), m_stall_map_size(0),
m_max_size(p.buffer_size), m_time_last_time_size_checked(0),
: SimObject(p), m_stall_map_size(0), m_max_size(p.buffer_size),
m_max_dequeue_rate(p.max_dequeue_rate), m_dequeues_this_cy(0),
m_time_last_time_size_checked(0),
m_time_last_time_enqueue(0), m_time_last_time_pop(0),
m_last_arrival_time(0), m_strict_fifo(p.ordered),
m_randomization(p.randomization),
@@ -312,7 +313,9 @@ MessageBuffer::dequeue(Tick current_time, bool decrement_messages)
m_size_at_cycle_start = m_prio_heap.size();
m_stalled_at_cycle_start = m_stall_map_size;
m_time_last_time_pop = current_time;
m_dequeues_this_cy = 0;
}
++m_dequeues_this_cy;
pop_heap(m_prio_heap.begin(), m_prio_heap.end(), std::greater<MsgPtr>());
m_prio_heap.pop_back();
@@ -507,8 +510,17 @@ MessageBuffer::print(std::ostream& out) const
bool
MessageBuffer::isReady(Tick current_time) const
{
return ((m_prio_heap.size() > 0) &&
(m_prio_heap.front()->getLastEnqueueTime() <= current_time));
assert(m_time_last_time_pop <= current_time);
bool can_dequeue = (m_max_dequeue_rate == 0) ||
(m_time_last_time_pop < current_time) ||
(m_dequeues_this_cy < m_max_dequeue_rate);
bool is_ready = (m_prio_heap.size() > 0) &&
(m_prio_heap.front()->getLastEnqueueTime() <= current_time);
if (!can_dequeue && is_ready) {
// Make sure the Consumer executes next cycle to dequeue the ready msg
m_consumer->scheduleEvent(Cycles(1));
}
return can_dequeue && is_ready;
}
uint32_t

View File

@@ -240,6 +240,14 @@ class MessageBuffer : public SimObject
*/
const unsigned int m_max_size;
/**
* When != 0, isReady returns false once m_max_dequeue_rate
* messages have been dequeued in the same cycle.
*/
const unsigned int m_max_dequeue_rate;
unsigned int m_dequeues_this_cy;
Tick m_time_last_time_size_checked;
unsigned int m_size_last_time_size_checked;

View File

@@ -1,4 +1,4 @@
# Copyright (c) 2020 ARM Limited
# Copyright (c) 2020-2021 ARM Limited
# All rights reserved.
#
# The license below extends only to copyright in the software and shall
@@ -67,3 +67,6 @@ class MessageBuffer(SimObject):
master = DeprecatedParam(out_port, '`master` is now called `out_port`')
in_port = ResponsePort("Response port from MessageBuffer sender")
slave = DeprecatedParam(in_port, '`slave` is now called `in_port`')
max_dequeue_rate = Param.Unsigned(0, "Maximum number of messages that can \
be dequeued per cycle \
(0 allows dequeueing all ready messages)")