diff options
Diffstat (limited to 'src/gpgpu-sim/local_interconnect.cc')
| -rw-r--r-- | src/gpgpu-sim/local_interconnect.cc | 432 |
1 files changed, 432 insertions, 0 deletions
diff --git a/src/gpgpu-sim/local_interconnect.cc b/src/gpgpu-sim/local_interconnect.cc new file mode 100644 index 0000000..0e20462 --- /dev/null +++ b/src/gpgpu-sim/local_interconnect.cc @@ -0,0 +1,432 @@ +// Copyright (c) 2019, Mahmoud Khairy +// Purdue University +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. Neither the name of +// The University of British Columbia nor the names of its contributors may be +// used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +#include <algorithm> +#include <cmath> +#include <fstream> +#include <iomanip> +#include <iostream> +#include <sstream> +#include <utility> + +#include "local_interconnect.h" +#include "mem_fetch.h" + +xbar_router::xbar_router(unsigned router_id, enum Interconnect_type m_type, + unsigned n_shader, unsigned n_mem, + const struct inct_config& m_localinct_config) { + m_id = router_id; + router_type = m_type; + _n_mem = n_mem; + _n_shader = n_shader; + total_nodes = n_shader + n_mem; + verbose = m_localinct_config.verbose; + grant_cycles = m_localinct_config.grant_cycles; + grant_cycles_count = m_localinct_config.grant_cycles; + in_buffers.resize(total_nodes); + out_buffers.resize(total_nodes); + next_node.resize(total_nodes, 0); + in_buffer_limit = m_localinct_config.in_buffer_limit; + out_buffer_limit = m_localinct_config.out_buffer_limit; + arbit_type = m_localinct_config.arbiter_algo; + next_node_id = 0; + if (m_type == REQ_NET) { + active_in_buffers = n_shader; + active_out_buffers = n_mem; + } else if (m_type == REPLY_NET) { + active_in_buffers = n_mem; + active_out_buffers = n_shader; + } + + cycles = 0; + conflicts = 0; + out_buffer_full = 0; + in_buffer_full = 0; + out_buffer_util = 0; + in_buffer_util = 0; + packets_num = 0; + conflicts_util = 0; + cycles_util = 0; + reqs_util = 0; +} + +xbar_router::~xbar_router() {} + +void xbar_router::Push(unsigned input_deviceID, unsigned output_deviceID, + void* data, unsigned int size) { + assert(input_deviceID < total_nodes); + in_buffers[input_deviceID].push(Packet(data, output_deviceID)); + packets_num++; +} + +void* xbar_router::Pop(unsigned ouput_deviceID) { + assert(ouput_deviceID < total_nodes); + void* data = NULL; + + if (!out_buffers[ouput_deviceID].empty()) { + data = out_buffers[ouput_deviceID].front().data; + out_buffers[ouput_deviceID].pop(); + } + + return data; +} + +bool xbar_router::Has_Buffer_In(unsigned input_deviceID, unsigned size, + bool update_counter) { + assert(input_deviceID < total_nodes); + + bool has_buffer = + (in_buffers[input_deviceID].size() + size <= in_buffer_limit); + if (update_counter && !has_buffer) in_buffer_full++; + + return has_buffer; +} + +bool xbar_router::Has_Buffer_Out(unsigned output_deviceID, unsigned size) { + return (out_buffers[output_deviceID].size() + size <= out_buffer_limit); +} + +void xbar_router::Advance() { + if (arbit_type == NAIVE_RR) + RR_Advance(); + else if (arbit_type == iSLIP) + iSLIP_Advance(); + else + assert(0); +} + +void xbar_router::RR_Advance() { + bool active = false; + vector<bool> issued(total_nodes, false); + unsigned conflict_sub = 0; + unsigned reqs = 0; + + for (unsigned i = 0; i < total_nodes; ++i) { + unsigned node_id = (i + next_node_id) % total_nodes; + + if (!in_buffers[node_id].empty()) { + active = true; + Packet _packet = in_buffers[node_id].front(); + // ensure that the outbuffer has space and not issued before in this cycle + if (Has_Buffer_Out(_packet.output_deviceID, 1)) { + if (!issued[_packet.output_deviceID]) { + out_buffers[_packet.output_deviceID].push(_packet); + in_buffers[node_id].pop(); + issued[_packet.output_deviceID] = true; + reqs++; + } else + conflict_sub++; + } else { + out_buffer_full++; + + if (issued[_packet.output_deviceID]) conflict_sub++; + } + } + } + + next_node_id = (++next_node_id % total_nodes); + + conflicts += conflict_sub; + if (active) { + conflicts_util += conflict_sub; + cycles_util++; + reqs_util += reqs; + } + + if (verbose) { + printf("%d : cycle %d : conflicts = %d\n", m_id, cycles, conflict_sub); + printf("%d : cycle %d : passing reqs = %d\n", m_id, cycles, reqs); + } + + // collect some stats about buffer util + for (unsigned i = 0; i < total_nodes; ++i) { + in_buffer_util += in_buffers[i].size(); + out_buffer_util += out_buffers[i].size(); + } + + cycles++; +} + +// iSLIP algorithm +// McKeown, Nick. "The iSLIP scheduling algorithm for input-queued switches." +// IEEE/ACM transactions on networking 2 (1999): 188-201. +// https://www.cs.rutgers.edu/~sn624/552-F18/papers/islip.pdf +void xbar_router::iSLIP_Advance() { + vector<unsigned> node_tmp; + bool active = false; + + unsigned conflict_sub = 0; + unsigned reqs = 0; + + // calcaulte how many conflicts are there for stats + for (unsigned i = 0; i < total_nodes; ++i) { + if (!in_buffers[i].empty()) { + Packet _packet_tmp = in_buffers[i].front(); + if (!node_tmp.empty()) { + if (std::find(node_tmp.begin(), node_tmp.end(), + _packet_tmp.output_deviceID) != node_tmp.end()) { + conflict_sub++; + } else + node_tmp.push_back(_packet_tmp.output_deviceID); + } else { + node_tmp.push_back(_packet_tmp.output_deviceID); + } + active = true; + } + } + + conflicts += conflict_sub; + if (active) { + conflicts_util += conflict_sub; + cycles_util++; + } + // do iSLIP + for (unsigned i = 0; i < total_nodes; ++i) { + if (Has_Buffer_Out(i, 1)) { + for (unsigned j = 0; j < total_nodes; ++j) { + unsigned node_id = (j + next_node[i]) % total_nodes; + + if (!in_buffers[node_id].empty()) { + Packet _packet = in_buffers[node_id].front(); + if (_packet.output_deviceID == i) { + out_buffers[_packet.output_deviceID].push(_packet); + in_buffers[node_id].pop(); + if (verbose) + printf("%d : cycle %d : send req from %d to %d\n", m_id, cycles, + node_id, i - _n_shader); + if (grant_cycles_count == 1) + next_node[i] = (++node_id % total_nodes); + if (verbose) { + for (unsigned k = j + 1; k < total_nodes; ++k) { + unsigned node_id2 = (k + next_node[i]) % total_nodes; + if (!in_buffers[node_id2].empty()) { + Packet _packet2 = in_buffers[node_id2].front(); + + if (_packet2.output_deviceID == i) + printf("%d : cycle %d : cannot send req from %d to %d\n", + m_id, cycles, node_id2, i - _n_shader); + } + } + } + + reqs++; + break; + } + } + } + } else + out_buffer_full++; + } + + if (active) { + reqs_util += reqs; + } + + if (verbose) + printf("%d : cycle %d : grant_cycles = %d\n", m_id, cycles, grant_cycles); + + if (active && grant_cycles_count == 1) + grant_cycles_count = grant_cycles; + else if (active) + grant_cycles_count--; + + if (verbose) { + printf("%d : cycle %d : conflicts = %d\n", m_id, cycles, conflict_sub); + printf("%d : cycle %d : passing reqs = %d\n", m_id, cycles, reqs); + } + + // collect some stats about buffer util + for (unsigned i = 0; i < total_nodes; ++i) { + in_buffer_util += in_buffers[i].size(); + out_buffer_util += out_buffers[i].size(); + } + + cycles++; +} + +bool xbar_router::Busy() const { + for (unsigned i = 0; i < total_nodes; ++i) { + if (!in_buffers[i].empty()) return true; + + if (!out_buffers[i].empty()) return true; + } + return false; +} + +//////////////////////////////////////////////////// +/////////////LocalInterconnect///////////////////// + +// assume all the packets are one flit +#define LOCAL_INCT_FLIT_SIZE 40 + +LocalInterconnect* LocalInterconnect::New( + const struct inct_config& m_localinct_config) { + LocalInterconnect* icnt_interface = new LocalInterconnect(m_localinct_config); + + return icnt_interface; +} + +LocalInterconnect::LocalInterconnect( + const struct inct_config& m_localinct_config) + : m_inct_config(m_localinct_config) { + n_shader = 0; + n_mem = 0; + n_subnets = m_localinct_config.subnets; +} + +LocalInterconnect::~LocalInterconnect() { + for (unsigned i = 0; i < m_inct_config.subnets; ++i) { + delete net[i]; + } +} + +void LocalInterconnect::CreateInterconnect(unsigned m_n_shader, + unsigned m_n_mem) { + n_shader = m_n_shader; + n_mem = m_n_mem; + + net.resize(n_subnets); + for (unsigned i = 0; i < n_subnets; ++i) { + net[i] = new xbar_router(i, static_cast<Interconnect_type>(i), m_n_shader, + m_n_mem, m_inct_config); + } +} + +void LocalInterconnect::Init() { + // empty + // there is nothing to do +} + +void LocalInterconnect::Push(unsigned input_deviceID, unsigned output_deviceID, + void* data, unsigned int size) { + unsigned subnet; + if (n_subnets == 1) { + subnet = 0; + } else { + if (input_deviceID < n_shader) { + subnet = 0; + } else { + subnet = 1; + } + } + + // it should have free buffer + // assume all the packets have size of one + // no flits are implemented + assert(net[subnet]->Has_Buffer_In(input_deviceID, 1)); + + net[subnet]->Push(input_deviceID, output_deviceID, data, size); +} + +void* LocalInterconnect::Pop(unsigned ouput_deviceID) { + // 0-_n_shader-1 indicates reply(network 1), otherwise request(network 0) + int subnet = 0; + if (ouput_deviceID < n_shader) subnet = 1; + + return net[subnet]->Pop(ouput_deviceID); +} + +void LocalInterconnect::Advance() { + for (unsigned i = 0; i < n_subnets; ++i) { + net[i]->Advance(); + } +} + +bool LocalInterconnect::Busy() const { + for (unsigned i = 0; i < n_subnets; ++i) { + if (net[i]->Busy()) return true; + } + return false; +} + +bool LocalInterconnect::HasBuffer(unsigned deviceID, unsigned int size) const { + bool has_buffer = false; + + if ((n_subnets > 1) && deviceID >= n_shader) // deviceID is memory node + has_buffer = net[REPLY_NET]->Has_Buffer_In(deviceID, 1, true); + else + has_buffer = net[REQ_NET]->Has_Buffer_In(deviceID, 1, true); + + return has_buffer; +} + +void LocalInterconnect::DisplayStats() const { + printf("Req_Network_injected_packets_num = %lld\n", + net[REQ_NET]->packets_num); + printf("Req_Network_cycles = %lld\n", net[REQ_NET]->cycles); + printf("Req_Network_injected_packets_per_cycle = %12.4f \n", + (float)(net[REQ_NET]->packets_num) / (net[REQ_NET]->cycles)); + printf("Req_Network_conflicts_per_cycle = %12.4f\n", + (float)(net[REQ_NET]->conflicts) / (net[REQ_NET]->cycles)); + printf("Req_Network_conflicts_per_cycle_util = %12.4f\n", + (float)(net[REQ_NET]->conflicts_util) / (net[REQ_NET]->cycles_util)); + printf("Req_Bank_Level_Parallism = %12.4f\n", + (float)(net[REQ_NET]->reqs_util) / (net[REQ_NET]->cycles_util)); + printf("Req_Network_in_buffer_full_per_cycle = %12.4f\n", + (float)(net[REQ_NET]->in_buffer_full) / (net[REQ_NET]->cycles)); + printf("Req_Network_in_buffer_avg_util = %12.4f\n", + ((float)(net[REQ_NET]->in_buffer_util) / (net[REQ_NET]->cycles) / + net[REQ_NET]->active_in_buffers)); + printf("Req_Network_out_buffer_full_per_cycle = %12.4f\n", + (float)(net[REQ_NET]->out_buffer_full) / (net[REQ_NET]->cycles)); + printf("Req_Network_out_buffer_avg_util = %12.4f\n", + ((float)(net[REQ_NET]->out_buffer_util) / (net[REQ_NET]->cycles) / + net[REQ_NET]->active_out_buffers)); + + printf("\n"); + printf("Reply_Network_injected_packets_num = %lld\n", + net[REPLY_NET]->packets_num); + printf("Reply_Network_cycles = %lld\n", net[REPLY_NET]->cycles); + printf("Reply_Network_injected_packets_per_cycle = %12.4f\n", + (float)(net[REPLY_NET]->packets_num) / (net[REPLY_NET]->cycles)); + printf("Reply_Network_conflicts_per_cycle = %12.4f\n", + (float)(net[REPLY_NET]->conflicts) / (net[REPLY_NET]->cycles)); + printf( + "Reply_Network_conflicts_per_cycle_util = %12.4f\n", + (float)(net[REPLY_NET]->conflicts_util) / (net[REPLY_NET]->cycles_util)); + printf("Reply_Bank_Level_Parallism = %12.4f\n", + (float)(net[REPLY_NET]->reqs_util) / (net[REPLY_NET]->cycles_util)); + printf("Reply_Network_in_buffer_full_per_cycle = %12.4f\n", + (float)(net[REPLY_NET]->in_buffer_full) / (net[REPLY_NET]->cycles)); + printf("Reply_Network_in_buffer_avg_util = %12.4f\n", + ((float)(net[REPLY_NET]->in_buffer_util) / (net[REPLY_NET]->cycles) / + net[REPLY_NET]->active_in_buffers)); + printf("Reply_Network_out_buffer_full_per_cycle = %12.4f\n", + (float)(net[REPLY_NET]->out_buffer_full) / (net[REPLY_NET]->cycles)); + printf("Reply_Network_out_buffer_avg_util = %12.4f\n", + ((float)(net[REPLY_NET]->out_buffer_util) / (net[REPLY_NET]->cycles) / + net[REPLY_NET]->active_out_buffers)); +} + +void LocalInterconnect::DisplayOverallStats() const {} + +unsigned LocalInterconnect::GetFlitSize() const { return LOCAL_INCT_FLIT_SIZE; } + +void LocalInterconnect::DisplayState(FILE* fp) const { + fprintf(fp, "GPGPU-Sim uArch: ICNT:Display State: Under implementation\n"); +} |
