summaryrefslogtreecommitdiff
path: root/src/gpgpu-sim/gpu-cache.cc
diff options
context:
space:
mode:
authorWilson Fung <[email protected]>2013-07-25 14:06:34 -0800
committerAndrew Boktor <[email protected]>2014-08-14 13:50:58 -0700
commit84f63f6996db657fe1291b4cc6e08b66422918c4 (patch)
treea11db8fb7ca10363ef512496130fd5e95a7f32af /src/gpgpu-sim/gpu-cache.cc
parentb5e2e7003f5d628d1c9baef08e6f6ae8b43e2ee5 (diff)
Adding bandwidth modeling to the cache model.
[git-p4: depot-paths = "//depot/gpgpu_sim_research/fermi/distribution/": change = 16671]
Diffstat (limited to 'src/gpgpu-sim/gpu-cache.cc')
-rw-r--r--src/gpgpu-sim/gpu-cache.cc160
1 files changed, 143 insertions, 17 deletions
diff --git a/src/gpgpu-sim/gpu-cache.cc b/src/gpgpu-sim/gpu-cache.cc
index 4c5c5c9..f927e9a 100644
--- a/src/gpgpu-sim/gpu-cache.cc
+++ b/src/gpgpu-sim/gpu-cache.cc
@@ -281,6 +281,15 @@ bool was_write_sent( const std::list<cache_event> &events )
return false;
}
+bool was_writeback_sent( const std::list<cache_event> &events )
+{
+ for( std::list<cache_event>::const_iterator e=events.begin(); e!=events.end(); e++ ) {
+ if( *e == WRITE_BACK_REQUEST_SENT )
+ return true;
+ }
+ return false;
+}
+
bool was_read_sent( const std::list<cache_event> &events )
{
for( std::list<cache_event>::const_iterator e=events.begin(); e!=events.end(); e++ ) {
@@ -362,6 +371,9 @@ cache_stats::cache_stats(){
for(unsigned i=0; i<NUM_MEM_ACCESS_TYPE; ++i){
m_stats[i].resize(NUM_CACHE_REQUEST_STATUS, 0);
}
+ m_cache_port_available_cycles = 0;
+ m_cache_data_port_busy_cycles = 0;
+ m_cache_fill_port_busy_cycles = 0;
}
void cache_stats::clear(){
@@ -371,6 +383,9 @@ void cache_stats::clear(){
for(unsigned i=0; i<NUM_MEM_ACCESS_TYPE; ++i){
std::fill(m_stats[i].begin(), m_stats[i].end(), 0);
}
+ m_cache_port_available_cycles = 0;
+ m_cache_data_port_busy_cycles = 0;
+ m_cache_fill_port_busy_cycles = 0;
}
void cache_stats::inc_stats(int access_type, int access_outcome){
@@ -425,6 +440,9 @@ cache_stats cache_stats::operator+(const cache_stats &cs){
ret(type, status) = m_stats[type][status] + cs(type, status);
}
}
+ ret.m_cache_port_available_cycles = m_cache_port_available_cycles + cs.m_cache_port_available_cycles;
+ ret.m_cache_data_port_busy_cycles = m_cache_data_port_busy_cycles + cs.m_cache_data_port_busy_cycles;
+ ret.m_cache_fill_port_busy_cycles = m_cache_fill_port_busy_cycles + cs.m_cache_fill_port_busy_cycles;
return ret;
}
@@ -437,6 +455,9 @@ cache_stats &cache_stats::operator+=(const cache_stats &cs){
m_stats[type][status] += cs(type, status);
}
}
+ m_cache_port_available_cycles += cs.m_cache_port_available_cycles;
+ m_cache_data_port_busy_cycles += cs.m_cache_data_port_busy_cycles;
+ m_cache_fill_port_busy_cycles += cs.m_cache_fill_port_busy_cycles;
return *this;
}
@@ -461,6 +482,20 @@ void cache_stats::print_stats(FILE *fout, const char *cache_name) const{
}
}
+void cache_sub_stats::print_port_stats(FILE *fout, const char *cache_name) const
+{
+ float data_port_util = 0.0f;
+ if (port_available_cycles > 0) {
+ data_port_util = (float) data_port_busy_cycles / port_available_cycles;
+ }
+ fprintf(fout, "%s_data_port_util = %.3f\n", cache_name, data_port_util);
+ float fill_port_util = 0.0f;
+ if (port_available_cycles > 0) {
+ fill_port_util = (float) fill_port_busy_cycles / port_available_cycles;
+ }
+ fprintf(fout, "%s_fill_port_util = %.3f\n", cache_name, fill_port_util);
+}
+
unsigned cache_stats::get_stats(enum mem_access_type *access_type, unsigned num_access_type, enum cache_request_status *access_status, unsigned num_access_status) const{
///
/// Returns a sum of the stats corresponding to each "access_type" and "access_status" pair.
@@ -499,6 +534,11 @@ void cache_stats::get_sub_stats(struct cache_sub_stats &css) const{
t_css.res_fails += m_stats[type][status];
}
}
+
+ t_css.port_available_cycles = m_cache_port_available_cycles;
+ t_css.data_port_busy_cycles = m_cache_data_port_busy_cycles;
+ t_css.fill_port_busy_cycles = m_cache_fill_port_busy_cycles;
+
css = t_css;
}
@@ -512,6 +552,85 @@ bool cache_stats::check_valid(int type, int status) const{
return false;
}
+void cache_stats::sample_cache_port_utility(bool data_port_busy, bool fill_port_busy)
+{
+ m_cache_port_available_cycles += 1;
+ if (data_port_busy) {
+ m_cache_data_port_busy_cycles += 1;
+ }
+ if (fill_port_busy) {
+ m_cache_fill_port_busy_cycles += 1;
+ }
+}
+
+baseline_cache::bandwidth_management::bandwidth_management(cache_config &config)
+: m_config(config)
+{
+ m_data_port_occupied_cycles = 0;
+ m_fill_port_occupied_cycles = 0;
+}
+
+/// use the data port based on the outcome and events generated by the mem_fetch request
+void baseline_cache::bandwidth_management::use_data_port(mem_fetch *mf, enum cache_request_status outcome, const std::list<cache_event> &events)
+{
+ unsigned data_size = mf->get_data_size();
+ unsigned port_width = m_config.m_data_port_width;
+ switch (outcome) {
+ case HIT: {
+ unsigned data_cycles = data_size / port_width + ((data_size % port_width > 0)? 1 : 0);
+ m_data_port_occupied_cycles += data_cycles;
+ } break;
+ case HIT_RESERVED:
+ case MISS: {
+ // the data array is accessed to read out the entire line for write-back
+ if (was_writeback_sent(events)) {
+ unsigned data_cycles = m_config.m_line_sz / port_width;
+ m_data_port_occupied_cycles += data_cycles;
+ }
+ } break;
+ case RESERVATION_FAIL:
+ // Does not consume any port bandwidth
+ break;
+ default:
+ assert(0);
+ break;
+ }
+}
+
+/// use the fill port
+void baseline_cache::bandwidth_management::use_fill_port(mem_fetch *mf)
+{
+ // assume filling the entire line with the returned request
+ unsigned fill_cycles = m_config.m_line_sz / m_config.m_data_port_width;
+ m_fill_port_occupied_cycles += fill_cycles;
+}
+
+/// called every cache cycle to free up the ports
+void baseline_cache::bandwidth_management::replenish_port_bandwidth()
+{
+ if (m_data_port_occupied_cycles > 0) {
+ m_data_port_occupied_cycles -= 1;
+ }
+ assert(m_data_port_occupied_cycles >= 0);
+
+ if (m_fill_port_occupied_cycles > 0) {
+ m_fill_port_occupied_cycles -= 1;
+ }
+ assert(m_fill_port_occupied_cycles >= 0);
+}
+
+/// query for data port availability
+bool baseline_cache::bandwidth_management::data_port_free() const
+{
+ return (m_data_port_occupied_cycles == 0);
+}
+
+/// query for fill port availability
+bool baseline_cache::bandwidth_management::fill_port_free() const
+{
+ return (m_fill_port_occupied_cycles == 0);
+}
+
/// Sends next request to lower level of memory
void baseline_cache::cycle(){
if ( !m_miss_queue.empty() ) {
@@ -521,27 +640,32 @@ void baseline_cache::cycle(){
m_memport->push(mf);
}
}
+ bool data_port_busy = !m_bandwidth_management.data_port_free();
+ bool fill_port_busy = !m_bandwidth_management.fill_port_free();
+ m_stats.sample_cache_port_utility(data_port_busy, fill_port_busy);
+ m_bandwidth_management.replenish_port_bandwidth();
}
/// Interface for response from lower memory level (model bandwidth restictions in caller)
void baseline_cache::fill(mem_fetch *mf, unsigned time){
- extra_mf_fields_lookup::iterator e = m_extra_mf_fields.find(mf);
- assert( e != m_extra_mf_fields.end() );
- assert( e->second.m_valid );
- mf->set_data_size( e->second.m_data_size );
- if ( m_config.m_alloc_policy == ON_MISS )
- m_tag_array->fill(e->second.m_cache_index,time);
- else if ( m_config.m_alloc_policy == ON_FILL )
- m_tag_array->fill(e->second.m_block_addr,time);
- else abort();
- bool has_atomic = false;
- m_mshrs.mark_ready(e->second.m_block_addr, has_atomic);
- if (has_atomic) {
- assert(m_config.m_alloc_policy == ON_MISS);
- cache_block_t &block = m_tag_array->get_block(e->second.m_cache_index);
- block.m_status = MODIFIED; // mark line as dirty for atomic operation
- }
- m_extra_mf_fields.erase(mf);
+ extra_mf_fields_lookup::iterator e = m_extra_mf_fields.find(mf);
+ assert( e != m_extra_mf_fields.end() );
+ assert( e->second.m_valid );
+ mf->set_data_size( e->second.m_data_size );
+ if ( m_config.m_alloc_policy == ON_MISS )
+ m_tag_array->fill(e->second.m_cache_index,time);
+ else if ( m_config.m_alloc_policy == ON_FILL )
+ m_tag_array->fill(e->second.m_block_addr,time);
+ else abort();
+ bool has_atomic = false;
+ m_mshrs.mark_ready(e->second.m_block_addr, has_atomic);
+ if (has_atomic) {
+ assert(m_config.m_alloc_policy == ON_MISS);
+ cache_block_t &block = m_tag_array->get_block(e->second.m_cache_index);
+ block.m_status = MODIFIED; // mark line as dirty for atomic operation
+ }
+ m_extra_mf_fields.erase(mf);
+ m_bandwidth_management.use_fill_port(mf);
}
/// Checks if mf is waiting to be filled by lower memory level
@@ -880,6 +1004,8 @@ data_cache::process_tag_probe( bool wr,
mf, time, events, probe_status );
}
}
+
+ m_bandwidth_management.use_data_port(mf, access_status, events);
return access_status;
}