summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavit Grigoryan <[email protected]>2026-04-24 23:59:00 +0000
committerDavit Grigoryan <[email protected]>2026-04-24 23:59:00 +0000
commit13d5fae1ac28988d071b4459af169e53361921d8 (patch)
tree9a4e8de9d139021c6d91cba720c32976f489d0b4
parent0d71376678778ea7f62556d997c71baa43fb2fbe (diff)
fix proper conflict counter for shrd mem accesses
-rw-r--r--src/abstract_hardware_model.cc103
-rw-r--r--src/abstract_hardware_model.h32
-rw-r--r--src/gpgpu-sim/shader.cc49
-rw-r--r--src/gpgpusim_entrypoint.cc12
4 files changed, 180 insertions, 16 deletions
diff --git a/src/abstract_hardware_model.cc b/src/abstract_hardware_model.cc
index 68efd4d..efe0e36 100644
--- a/src/abstract_hardware_model.cc
+++ b/src/abstract_hardware_model.cc
@@ -43,6 +43,109 @@
#include "gpgpusim_entrypoint.h"
#include "option_parser.h"
+// Forward declarations needed by compute_unified_shared_cycles below.
+new_addr_type line_size_based_tag_func(new_addr_type address,
+ new_addr_type line_size);
+
+// MEMCO v3 Model B diagnostic globals: count how often the unified shared
+// bank-conflict computation fired and the cumulative delta vs the per-set
+// max model. Printed at gpgpu_sim termination.
+unsigned long long g_memcov3_shared_unified_invocations = 0;
+unsigned long long g_memcov3_shared_unified_cycles_unified = 0;
+unsigned long long g_memcov3_shared_unified_cycles_perset_max = 0;
+
+void warp_inst_t::compute_unified_shared_cycles() {
+ static const bool enabled = (getenv("MEMCOV3_SHARED_UNIFIED") != NULL);
+ if (!enabled) return;
+ if (empty()) return;
+ if (op != LOAD_OP && op != STORE_OP) return;
+ if (!m_per_scalar_thread_valid) return;
+
+ // Determine whether any shared work is present in this composite.
+ bool primary_shared = (space.get_type() == shared_space ||
+ space.get_type() == sstarr_space);
+ bool any_shared_set = false;
+ for (unsigned s = 0; s < m_simd_sets.size(); s++) {
+ if (!m_simd_sets[s].valid || m_simd_sets[s].source_inst == NULL) continue;
+ enum _memory_space_t sp = m_simd_sets[s].source_inst->space.get_type();
+ if (sp == shared_space || sp == sstarr_space) {
+ any_shared_set = true;
+ break;
+ }
+ }
+ if (!primary_shared && !any_shared_set) return;
+
+ // Build a unified bank → word → count map across all participating
+ // active lanes (primary + shared-source coissuer sets). No subwarp
+ // subdivision: Model B assumes a single bank arbiter sees all lanes
+ // concurrently. Bank conflicts are counted across the union.
+ std::map<unsigned, std::map<new_addr_type, unsigned> > bank_accs;
+
+ if (primary_shared) {
+ for (unsigned t = 0; t < m_config->warp_size; t++) {
+ if (!active(t)) continue;
+ new_addr_type addr = m_per_scalar_thread[t].memreqaddr[0];
+ unsigned bank = m_config->shmem_bank_func(addr);
+ new_addr_type word = line_size_based_tag_func(addr, m_config->WORD_SIZE);
+ bank_accs[bank][word]++;
+ }
+ }
+ for (unsigned s = 0; s < m_simd_sets.size(); s++) {
+ const simd_set_info &si = m_simd_sets[s];
+ if (!si.valid || si.source_inst == NULL) continue;
+ enum _memory_space_t sp = si.source_inst->space.get_type();
+ if (sp != shared_space && sp != sstarr_space) continue;
+ if (si.per_thread_addrs.empty()) continue;
+ for (unsigned lane = 0; lane < si.per_thread_addrs.size(); lane++) {
+ if (!si.set_active_mask.test(lane)) continue;
+ new_addr_type addr = si.per_thread_addrs[lane].memreqaddr[0];
+ unsigned bank = m_config->shmem_bank_func(addr);
+ new_addr_type word = line_size_based_tag_func(addr, m_config->WORD_SIZE);
+ bank_accs[bank][word]++;
+ }
+ }
+
+ // Find the bank with the most distinct words — that's the unified
+ // bank-conflict cycle count for this composite.
+ unsigned unified_cycles = 0;
+ for (std::map<unsigned, std::map<new_addr_type, unsigned> >::iterator b =
+ bank_accs.begin();
+ b != bank_accs.end(); ++b) {
+ if (b->second.size() > unified_cycles) unified_cycles = b->second.size();
+ }
+ if (unified_cycles == 0) return; // no active lanes after all
+
+ // Compute the per-set-max model's value for diagnostic comparison.
+ unsigned perset_max = 0;
+ if (primary_shared && cycles > perset_max) perset_max = cycles;
+ for (unsigned s = 0; s < m_simd_sets.size(); s++) {
+ const simd_set_info &si = m_simd_sets[s];
+ if (!si.valid || si.source_inst == NULL) continue;
+ enum _memory_space_t sp = si.source_inst->space.get_type();
+ if (sp != shared_space && sp != sstarr_space) continue;
+ if (si.cycles > perset_max) perset_max = si.cycles;
+ }
+
+ g_memcov3_shared_unified_invocations++;
+ g_memcov3_shared_unified_cycles_unified += unified_cycles;
+ g_memcov3_shared_unified_cycles_perset_max += perset_max;
+
+ set_unified_shared_cycles(unified_cycles);
+
+ // Zero out the per-set + primary cycles so other paths (e.g. Stage 6
+ // early-release in ldst_unit::cycle, which checks has_dispatch_delay
+ // per set) see "drained" — we'll consume the unified counter in
+ // shared_cycle instead. This avoids per-set cycles staying stuck
+ // nonzero (since shared_cycle no longer decrements them).
+ if (primary_shared) cycles = 0;
+ for (unsigned s = 0; s < m_simd_sets.size(); s++) {
+ simd_set_info &si = m_simd_sets[s];
+ if (!si.valid || si.source_inst == NULL) continue;
+ enum _memory_space_t sp = si.source_inst->space.get_type();
+ if (sp == shared_space || sp == sstarr_space) si.cycles = 0;
+ }
+}
+
void mem_access_t::init(gpgpu_context *ctx) {
gpgpu_ctx = ctx;
m_uid = ++(gpgpu_ctx->sm_next_access_uid);
diff --git a/src/abstract_hardware_model.h b/src/abstract_hardware_model.h
index 713c51a..369459b 100644
--- a/src/abstract_hardware_model.h
+++ b/src/abstract_hardware_model.h
@@ -1315,6 +1315,17 @@ class warp_inst_t : public inst_t {
}
}
}
+ // MEMCO v3 Model B (unified shared bank-conflict computation).
+ // Walks the primary's per-lane addresses (if primary is shared) plus each
+ // valid simd_set whose source is shared/sstarr (using captured
+ // per_thread_addrs + set_active_mask). Builds a single bank→word→count
+ // map across the union, computes the max bank-access count, and stores
+ // it via set_unified_shared_cycles. No subwarp subdivision (assumes a
+ // unified bank arbiter sees all participating lanes concurrently).
+ // No-op if env var MEMCOV3_SHARED_UNIFIED is unset, or composite has no
+ // shared work, or m_per_scalar_thread_valid is false. Records pre/post
+ // delta in two globals (g_memcov3_shared_unified_*) for diagnostics.
+ void compute_unified_shared_cycles();
void merge_simd_sets(const std::vector<simd_set_info> &other_sets);
void set_source_inst_on_sets(const inst_t *src);
void set_split_id_on_sets(unsigned split_id);
@@ -1470,6 +1481,27 @@ class warp_inst_t : public inst_t {
bool has_dispatch_delay() { return cycles > 0; }
+ // MEMCO v3 shared unified bank-conflict (Model B). When active, replaces
+ // per-set max with a single composite-level cycle counter computed across
+ // all participating lanes' (bank, word) pairs — matching real-HW bank
+ // arbiter that sees all co-issued lanes concurrently. Activated by
+ // compute_unified_shared_cycles() at ldst_unit::issue when the env var
+ // MEMCOV3_SHARED_UNIFIED=1 is set and the composite has shared work.
+ bool m_unified_shared_active = false;
+ unsigned m_unified_shared_cycles = 0;
+ bool dispatch_delay_unified() {
+ if (m_unified_shared_cycles > 0) m_unified_shared_cycles--;
+ return m_unified_shared_cycles > 0;
+ }
+ bool has_dispatch_delay_unified() const {
+ return m_unified_shared_cycles > 0;
+ }
+ bool unified_shared_active() const { return m_unified_shared_active; }
+ void set_unified_shared_cycles(unsigned c) {
+ m_unified_shared_cycles = c;
+ m_unified_shared_active = true;
+ }
+
void print(FILE *fout) const;
unsigned get_uid() const { return m_uid; }
unsigned long long get_streamID() const { return m_streamID; }
diff --git a/src/gpgpu-sim/shader.cc b/src/gpgpu-sim/shader.cc
index 3b2b17c..8eb3f28 100644
--- a/src/gpgpu-sim/shader.cc
+++ b/src/gpgpu-sim/shader.cc
@@ -3313,26 +3313,37 @@ bool ldst_unit::shared_cycle(warp_inst_t &inst, mem_stage_stall_type &rc_fail,
bool any_stall = false;
- // Primary's own cycles (only counts if primary is shared).
- if (primary_is_shared) {
- if (inst.has_dispatch_delay()) {
+ // MEMCO v3 Model B: when unified bank-conflict cycles are active for
+ // this composite, replace the per-set max with a single unified counter
+ // (computed at ldst_unit::issue across the union of all participating
+ // lanes). Skip the primary + per-set walks below.
+ if (inst.unified_shared_active()) {
+ if (inst.has_dispatch_delay_unified()) {
m_stats->gpgpu_n_shmem_bank_access[m_sid]++;
}
- if (inst.dispatch_delay()) any_stall = true;
- }
-
- // Per-set cycles for shared coissuer sets. Each set's cycles counts
- // down independently; we stall as long as ANY still has cycles > 0.
- if (inst.has_simd_sets()) {
- std::vector<simd_set_info> &sets = inst.get_simd_sets_mutable();
- for (unsigned s = 0; s < sets.size(); s++) {
- if (!sets[s].valid || sets[s].source_inst == NULL) continue;
- enum _memory_space_t src_sp = sets[s].source_inst->space.get_type();
- if (src_sp != shared_space && src_sp != sstarr_space) continue;
- if (sets[s].has_dispatch_delay()) {
+ if (inst.dispatch_delay_unified()) any_stall = true;
+ } else {
+ // Primary's own cycles (only counts if primary is shared).
+ if (primary_is_shared) {
+ if (inst.has_dispatch_delay()) {
m_stats->gpgpu_n_shmem_bank_access[m_sid]++;
}
- if (sets[s].dispatch_delay()) any_stall = true;
+ if (inst.dispatch_delay()) any_stall = true;
+ }
+
+ // Per-set cycles for shared coissuer sets. Each set's cycles counts
+ // down independently; we stall as long as ANY still has cycles > 0.
+ if (inst.has_simd_sets()) {
+ std::vector<simd_set_info> &sets = inst.get_simd_sets_mutable();
+ for (unsigned s = 0; s < sets.size(); s++) {
+ if (!sets[s].valid || sets[s].source_inst == NULL) continue;
+ enum _memory_space_t src_sp = sets[s].source_inst->space.get_type();
+ if (src_sp != shared_space && src_sp != sstarr_space) continue;
+ if (sets[s].has_dispatch_delay()) {
+ m_stats->gpgpu_n_shmem_bank_access[m_sid]++;
+ }
+ if (sets[s].dispatch_delay()) any_stall = true;
+ }
}
}
@@ -4308,6 +4319,12 @@ void ldst_unit::issue(register_set &reg_set) {
coalesce_accessq_across_sets(*inst);
}
+ // MEMCO v3 Model B: compute unified shared bank-conflict cycles across
+ // all participating sets when MEMCOV3_SHARED_UNIFIED is set.
+ if (is_coissue_composite) {
+ inst->compute_unified_shared_cycles();
+ }
+
if (is_coissue_composite) {
// MEM co-issue: accesses in the composite's queue may originate from
// different source warps (inter-warp) or different splits of the same
diff --git a/src/gpgpusim_entrypoint.cc b/src/gpgpusim_entrypoint.cc
index 5ae7682..35b3e89 100644
--- a/src/gpgpusim_entrypoint.cc
+++ b/src/gpgpusim_entrypoint.cc
@@ -88,11 +88,23 @@ void *gpgpu_sim_thread_sequential(void *ctx_ptr) {
extern unsigned long long g_memcov3_n_merges_global;
extern unsigned long long g_memcov3_n_merged_sources_global;
+extern unsigned long long g_memcov3_shared_unified_invocations;
+extern unsigned long long g_memcov3_shared_unified_cycles_unified;
+extern unsigned long long g_memcov3_shared_unified_cycles_perset_max;
static void termination_callback() {
printf(
"MEMCOV3: interset_merges = %llu, merged_sources_absorbed = %llu\n",
g_memcov3_n_merges_global, g_memcov3_n_merged_sources_global);
+ printf(
+ "MEMCOV3 shared_unified: invocations = %llu, "
+ "cycles_unified_total = %llu, cycles_perset_max_total = %llu, "
+ "delta = %lld\n",
+ g_memcov3_shared_unified_invocations,
+ g_memcov3_shared_unified_cycles_unified,
+ g_memcov3_shared_unified_cycles_perset_max,
+ (long long)g_memcov3_shared_unified_cycles_unified -
+ (long long)g_memcov3_shared_unified_cycles_perset_max);
printf("GPGPU-Sim: *** exit detected ***\n");
fflush(stdout);
}