From 13d5fae1ac28988d071b4459af169e53361921d8 Mon Sep 17 00:00:00 2001 From: Davit Grigoryan Date: Fri, 24 Apr 2026 23:59:00 +0000 Subject: fix proper conflict counter for shrd mem accesses --- src/abstract_hardware_model.cc | 103 +++++++++++++++++++++++++++++++++++++++++ src/abstract_hardware_model.h | 32 +++++++++++++ src/gpgpu-sim/shader.cc | 49 +++++++++++++------- src/gpgpusim_entrypoint.cc | 12 +++++ 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 > 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 >::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 &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 &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 &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 ®_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); } -- cgit v1.3