From 2f190971f5f952f55197d385c688667439bc6649 Mon Sep 17 00:00:00 2001 From: Davit Grigoryan Date: Mon, 27 Apr 2026 02:10:06 +0000 Subject: add debug scoreboard stats counting --- src/abstract_hardware_model.cc | 1 + src/gpgpu-sim/gpu-sim.cc | 8 ++++ src/gpgpu-sim/scoreboard.cc | 90 +++++++++++++++++++++++++++++++++++++++++- src/gpgpu-sim/scoreboard.h | 31 +++++++++++++++ src/gpgpu-sim/shader.cc | 10 +++++ src/gpgpu-sim/shader.h | 2 + 6 files changed, 140 insertions(+), 2 deletions(-) diff --git a/src/abstract_hardware_model.cc b/src/abstract_hardware_model.cc index efe0e36..f8746e6 100644 --- a/src/abstract_hardware_model.cc +++ b/src/abstract_hardware_model.cc @@ -2493,6 +2493,7 @@ const simt_mask_t &simt_reconvergence_table::get_active_mask(unsigned num) { return m_recvg_table[num].m_active_mask; } + simt_reconvergence_table_entry simt_reconvergence_table::get_recvg_entry( unsigned num) { return m_recvg_table[num]; diff --git a/src/gpgpu-sim/gpu-sim.cc b/src/gpgpu-sim/gpu-sim.cc index ca51c50..c858c50 100644 --- a/src/gpgpu-sim/gpu-sim.cc +++ b/src/gpgpu-sim/gpu-sim.cc @@ -1585,6 +1585,14 @@ void gpgpu_sim::gpu_print_stat(unsigned long long streamID) { // shader_print_l1_miss_stat( stdout ); shader_print_cache_stats(stdout); + // Scoreboard reserve/release accounting (one line per shader). Used to + // detect leaks (reserves != releases or remaining > 0) and average + // reservation duration vs. expected memory/FU latency. + fprintf(statfout, "\nScoreboard accounting:\n"); + for (unsigned i = 0; i < m_config.num_cluster(); i++) { + m_cluster[i]->print_scoreboard_accounting(statfout); + } + cache_stats core_cache_stats; core_cache_stats.clear(); for (unsigned i = 0; i < m_config.num_cluster(); i++) { diff --git a/src/gpgpu-sim/scoreboard.cc b/src/gpgpu-sim/scoreboard.cc index de71807..bcaea56 100644 --- a/src/gpgpu-sim/scoreboard.cc +++ b/src/gpgpu-sim/scoreboard.cc @@ -28,6 +28,7 @@ #include "scoreboard.h" #include "../cuda-sim/ptx_sim.h" +#include "gpu-sim.h" #include "shader.h" #include "shader_trace.h" @@ -41,6 +42,19 @@ Scoreboard::Scoreboard(unsigned sid, unsigned n_warps, class gpgpu_t* gpu) sec_reg_table.resize(n_warps); m_gpu = gpu; + + m_primary_reserve_calls = 0; + m_primary_release_calls = 0; + m_sec_reserve_calls = 0; + m_sec_release_calls = 0; + m_sec_clear_calls = 0; + m_sec_clear_regs_dropped = 0; + m_primary_total_duration_cycles = 0; + m_primary_completed_reservations = 0; + m_primary_max_duration_cycles = 0; + m_sec_total_duration_cycles = 0; + m_sec_completed_reservations = 0; + m_sec_max_duration_cycles = 0; } // Print scoreboard contents @@ -67,6 +81,9 @@ void Scoreboard::reserveRegister(unsigned wid, unsigned regnum) { SHADER_DPRINTF(SCOREBOARD, "Reserved Register - warp:%d, reg: %d\n", wid, regnum); reg_table[wid].insert(regnum); + m_primary_reserve_calls++; + m_primary_resv_cycle[std::make_pair(wid, regnum)] = + m_gpu->gpu_sim_cycle + m_gpu->gpu_tot_sim_cycle; } // Unmark register as write-pending @@ -75,6 +92,18 @@ void Scoreboard::releaseRegister(unsigned wid, unsigned regnum) { SHADER_DPRINTF(SCOREBOARD, "Release register - warp:%d, reg: %d\n", wid, regnum); reg_table[wid].erase(regnum); + m_primary_release_calls++; + auto key = std::make_pair(wid, regnum); + auto it = m_primary_resv_cycle.find(key); + if (it != m_primary_resv_cycle.end()) { + unsigned long long now = m_gpu->gpu_sim_cycle + m_gpu->gpu_tot_sim_cycle; + unsigned long long dur = now - it->second; + m_primary_total_duration_cycles += dur; + m_primary_completed_reservations++; + if (dur > m_primary_max_duration_cycles) + m_primary_max_duration_cycles = dur; + m_primary_resv_cycle.erase(it); + } } const bool Scoreboard::islongop(unsigned warp_id, unsigned regnum) { @@ -158,11 +187,28 @@ bool Scoreboard::pendingWrites(unsigned wid) const { void Scoreboard::reserveRegisterSecondary(unsigned wid, unsigned regnum) { // No abort on duplicate — secondary may share register names with primary - sec_reg_table[wid].insert(regnum); + bool inserted = sec_reg_table[wid].insert(regnum).second; + if (inserted) { + m_sec_reserve_calls++; + m_sec_resv_cycle[std::make_pair(wid, regnum)] = + m_gpu->gpu_sim_cycle + m_gpu->gpu_tot_sim_cycle; + } } void Scoreboard::releaseRegisterSecondary(unsigned wid, unsigned regnum) { - sec_reg_table[wid].erase(regnum); + size_t n = sec_reg_table[wid].erase(regnum); + if (n == 0) return; + m_sec_release_calls++; + auto key = std::make_pair(wid, regnum); + auto it = m_sec_resv_cycle.find(key); + if (it != m_sec_resv_cycle.end()) { + unsigned long long now = m_gpu->gpu_sim_cycle + m_gpu->gpu_tot_sim_cycle; + unsigned long long dur = now - it->second; + m_sec_total_duration_cycles += dur; + m_sec_completed_reservations++; + if (dur > m_sec_max_duration_cycles) m_sec_max_duration_cycles = dur; + m_sec_resv_cycle.erase(it); + } } bool Scoreboard::checkCollisionSecondary(unsigned wid, @@ -181,5 +227,45 @@ bool Scoreboard::checkCollisionSecondary(unsigned wid, } void Scoreboard::clearSecondary(unsigned wid) { + m_sec_clear_calls++; + m_sec_clear_regs_dropped += sec_reg_table[wid].size(); + // Drop in-flight duration timestamps for any regs we are dropping — + // these are abandoned reservations (e.g. warp completed, pipeline flush) + // and counting their duration would skew the histogram. + for (auto reg : sec_reg_table[wid]) + m_sec_resv_cycle.erase(std::make_pair(wid, reg)); sec_reg_table[wid].clear(); } + +// End-of-run accounting dump. +void Scoreboard::dumpAccounting(FILE* out) const { + unsigned long long primary_remaining = 0; + unsigned long long sec_remaining = 0; + for (unsigned w = 0; w < reg_table.size(); w++) + primary_remaining += reg_table[w].size(); + for (unsigned w = 0; w < sec_reg_table.size(); w++) + sec_remaining += sec_reg_table[w].size(); + double primary_avg_dur = + m_primary_completed_reservations + ? (double)m_primary_total_duration_cycles / + (double)m_primary_completed_reservations + : 0.0; + double sec_avg_dur = + m_sec_completed_reservations + ? (double)m_sec_total_duration_cycles / + (double)m_sec_completed_reservations + : 0.0; + fprintf(out, + "scoreboard_accounting sid=%u " + "primary: reserves=%llu releases=%llu remaining=%llu " + "completed=%llu avg_cycles=%.2f max_cycles=%llu " + "| secondary: reserves=%llu releases=%llu remaining=%llu " + "completed=%llu avg_cycles=%.2f max_cycles=%llu " + "clearSecondary=%llu regs_dropped_by_clear=%llu\n", + m_sid, m_primary_reserve_calls, m_primary_release_calls, + primary_remaining, m_primary_completed_reservations, primary_avg_dur, + m_primary_max_duration_cycles, m_sec_reserve_calls, + m_sec_release_calls, sec_remaining, m_sec_completed_reservations, + sec_avg_dur, m_sec_max_duration_cycles, m_sec_clear_calls, + m_sec_clear_regs_dropped); +} diff --git a/src/gpgpu-sim/scoreboard.h b/src/gpgpu-sim/scoreboard.h index 9bf51e6..1de81dd 100644 --- a/src/gpgpu-sim/scoreboard.h +++ b/src/gpgpu-sim/scoreboard.h @@ -28,7 +28,9 @@ #include #include +#include #include +#include #include #include "assert.h" @@ -56,6 +58,12 @@ class Scoreboard { bool checkCollisionSecondary(unsigned wid, const inst_t *inst) const; void clearSecondary(unsigned wid); + public: + // Accounting / leak-detection (called from gpu-sim end-of-run dump). + // Aggregates reserve/release counts, reservation-duration histogram, + // and any registers still reserved at the end of simulation. + void dumpAccounting(FILE *out) const; + private: void reserveRegister(unsigned wid, unsigned regnum); int get_sid() const { return m_sid; } @@ -72,6 +80,29 @@ class Scoreboard { std::vector > sec_reg_table; class gpgpu_t *m_gpu; + + // ---- Accounting (per-shader, scalar; cheap to update) ---- + // primary reg_table activity + unsigned long long m_primary_reserve_calls; // reserveRegister insertions + unsigned long long m_primary_release_calls; // releaseRegister erases + // secondary sec_reg_table activity + unsigned long long m_sec_reserve_calls; + unsigned long long m_sec_release_calls; + unsigned long long m_sec_clear_calls; // clearSecondary invocations + unsigned long long m_sec_clear_regs_dropped; // total regs cleared by clearSecondary + // duration tracking — sum of (release_cycle - reserve_cycle) over all completed reservations + unsigned long long m_primary_total_duration_cycles; + unsigned long long m_primary_completed_reservations; + unsigned long long m_primary_max_duration_cycles; + unsigned long long m_sec_total_duration_cycles; + unsigned long long m_sec_completed_reservations; + unsigned long long m_sec_max_duration_cycles; + // reservation timestamp lookup: (wid,reg) -> cycle_reserved + // typedef std::map, unsigned long long> resv_map_t; + std::map, unsigned long long> + m_primary_resv_cycle; + std::map, unsigned long long> + m_sec_resv_cycle; }; #endif /* SCOREBOARD_H_ */ diff --git a/src/gpgpu-sim/shader.cc b/src/gpgpu-sim/shader.cc index 8eb3f28..a113ddb 100644 --- a/src/gpgpu-sim/shader.cc +++ b/src/gpgpu-sim/shader.cc @@ -6384,6 +6384,10 @@ void shader_core_ctx::print_cache_stats(FILE *fp, unsigned &dl1_accesses, m_ldst_unit->print_cache_stats(fp, dl1_accesses, dl1_misses); } +void shader_core_ctx::print_scoreboard_accounting(FILE *fp) const { + if (m_scoreboard) m_scoreboard->dumpAccounting(fp); +} + void shader_core_ctx::get_cache_stats(cache_stats &cs) { // Adds stats from each cache to 'cs' cs += m_L1I->get_stats(); // Get L1I stats @@ -7363,6 +7367,12 @@ void simt_core_cluster::print_cache_stats(FILE *fp, unsigned &dl1_accesses, } } +void simt_core_cluster::print_scoreboard_accounting(FILE *fp) const { + for (unsigned i = 0; i < m_config->n_simt_cores_per_cluster; ++i) { + m_core[i]->print_scoreboard_accounting(fp); + } +} + void simt_core_cluster::get_icnt_stats(long &n_simt_to_mem, long &n_mem_to_simt) const { long simt_to_mem = 0; diff --git a/src/gpgpu-sim/shader.h b/src/gpgpu-sim/shader.h index 1fff5e5..ce0c8c4 100644 --- a/src/gpgpu-sim/shader.h +++ b/src/gpgpu-sim/shader.h @@ -2566,6 +2566,7 @@ class shader_core_ctx : public core_t { const shader_core_config *get_config() const { return m_config; } void print_cache_stats(FILE *fp, unsigned &dl1_accesses, unsigned &dl1_misses); + void print_scoreboard_accounting(FILE *fp) const; void get_cache_stats(cache_stats &cs); void get_L1I_sub_stats(struct cache_sub_stats &css) const; @@ -3113,6 +3114,7 @@ class simt_core_cluster { void display_pipeline(unsigned sid, FILE *fout, int print_mem, int mask); void print_cache_stats(FILE *fp, unsigned &dl1_accesses, unsigned &dl1_misses) const; + void print_scoreboard_accounting(FILE *fp) const; void get_cache_stats(cache_stats &cs) const; void get_L1I_sub_stats(struct cache_sub_stats &css) const; -- cgit v1.3