diff options
| -rw-r--r-- | src/abstract_hardware_model.cc | 46 | ||||
| -rw-r--r-- | src/abstract_hardware_model.h | 6 | ||||
| -rw-r--r-- | src/gpgpu-sim/gpu-sim.cc | 17 | ||||
| -rw-r--r-- | src/gpgpu-sim/shader.cc | 136 | ||||
| -rw-r--r-- | src/gpgpu-sim/shader.h | 61 |
5 files changed, 223 insertions, 43 deletions
diff --git a/src/abstract_hardware_model.cc b/src/abstract_hardware_model.cc index 9262a92..86427ad 100644 --- a/src/abstract_hardware_model.cc +++ b/src/abstract_hardware_model.cc @@ -129,6 +129,52 @@ void warp_inst_t::compute_simd_sets_compacted(unsigned num_sets, } } +void warp_inst_t::compute_simd_sets_xor_static(unsigned num_sets, + unsigned set_width, + unsigned start_set) { + // start_set is unused for XOR-static: the lane permutation is fully + // determined by warp_id. Accepted for signature compatibility with + // compute_simd_sets_compacted. + (void)start_set; + + m_simd_sets.resize(num_sets); + for (unsigned s = 0; s < num_sets; s++) { + simd_set_info &info = m_simd_sets[s]; + info.set_id = s; + info.warp_id = m_warp_id; + info.set_active_mask.reset(); + info.active_mask_in_warp.reset(); + info.num_active_threads = 0; + } + + // xor_key = reverse of the low log2(warp_size) bits of m_warp_id + unsigned warp_size = m_config->warp_size; + unsigned bits = 0; + while ((1u << bits) < warp_size) bits++; + unsigned xor_key = 0; + for (unsigned i = 0; i < bits; i++) { + if (m_warp_id & (1u << i)) xor_key |= (1u << (bits - 1 - i)); + } + + // Physical lane P runs logical thread T = P ^ xor_key. + for (unsigned P = 0; P < warp_size; P++) { + unsigned T = P ^ xor_key; + unsigned s = P / set_width; + unsigned lane = P % set_width; + simd_set_info &info = m_simd_sets[s]; + info.thread_ids[lane] = T; + if (m_warp_active_mask.test(T)) { + info.set_active_mask.set(lane); + info.active_mask_in_warp.set(T); + info.num_active_threads++; + } + } + + for (unsigned s = 0; s < num_sets; s++) { + m_simd_sets[s].valid = (m_simd_sets[s].num_active_threads > 0); + } +} + unsigned warp_inst_t::num_active_simd_sets() const { unsigned count = 0; for (unsigned i = 0; i < m_simd_sets.size(); i++) { diff --git a/src/abstract_hardware_model.h b/src/abstract_hardware_model.h index 4a655b3..3f7eac3 100644 --- a/src/abstract_hardware_model.h +++ b/src/abstract_hardware_model.h @@ -1185,6 +1185,12 @@ class warp_inst_t : public inst_t { void compute_simd_sets(unsigned num_sets, unsigned set_width); void compute_simd_sets_compacted(unsigned num_sets, unsigned set_width, unsigned start_set = 0); + // Fixed per-warp lane permutation: lane P -> thread (P XOR reverse_bits(warp_id)). + // Does NOT repack — only reshuffles lane assignment. start_set is accepted + // for API symmetry with compute_simd_sets_compacted but is ignored (XOR + // placement is fixed by warp_id). + void compute_simd_sets_xor_static(unsigned num_sets, unsigned set_width, + unsigned start_set = 0); const std::vector<simd_set_info> &get_simd_sets() const { return m_simd_sets; } diff --git a/src/gpgpu-sim/gpu-sim.cc b/src/gpgpu-sim/gpu-sim.cc index 7b186aa..dd3bba7 100644 --- a/src/gpgpu-sim/gpu-sim.cc +++ b/src/gpgpu-sim/gpu-sim.cc @@ -685,6 +685,23 @@ void shader_core_config::reg_options(class OptionParser *opp) { opp, "-gpgpu_enable_compaction", OPT_BOOL, &gpgpu_enable_compaction, "Enable thread compaction into fewer SIMD sets (default = disabled)", "0"); + option_parser_register( + opp, "-gpgpu_compaction_mode", OPT_UINT32, &gpgpu_compaction_mode, + "Compaction mode: 0=none, 1=xor-static, 2=full. " + "Supersedes -gpgpu_enable_compaction (kept as deprecated alias).", + "0"); + option_parser_register( + opp, "-gpgpu_opndcoll_read_latency", OPT_UINT32, + &gpgpu_opndcoll_read_latency, + "Extra cycles from operand-read grant to operand arriving at " + "collector unit (default 0 = combinational, current behavior).", + "0"); + option_parser_register( + opp, "-gpgpu_co_issue_priority", OPT_UINT32, + &gpgpu_co_issue_priority, + "Co-issue scheduler: 0=greedy (utilization-max), 1=intra-first, " + "2=inter-first, 3=fewest-lanes, 4=same-PC (default 0).", + "0"); for (unsigned j = 0; j < SPECIALIZED_UNIT_NUM; ++j) { std::stringstream ss; diff --git a/src/gpgpu-sim/shader.cc b/src/gpgpu-sim/shader.cc index bf6c9c0..ffbb0c6 100644 --- a/src/gpgpu-sim/shader.cc +++ b/src/gpgpu-sim/shader.cc @@ -1192,12 +1192,20 @@ warp_inst_t *shader_core_ctx::issue_warp(register_set &pipe_reg_set, m_stats->shader_cycle_distro[2 + (*pipe_reg)->active_count()]++; // Compute SIMD set assignments before functional execution if (m_config->gpgpu_simd_partitioning) { - if (m_config->gpgpu_enable_compaction) - (*pipe_reg)->compute_simd_sets_compacted( - m_config->gpgpu_num_simd_sets, m_config->simd_set_width, 0); - else - (*pipe_reg)->compute_simd_sets(m_config->gpgpu_num_simd_sets, - m_config->simd_set_width); + switch (m_config->gpgpu_compaction_mode) { + case 1: // xor-static + (*pipe_reg)->compute_simd_sets_xor_static( + m_config->gpgpu_num_simd_sets, m_config->simd_set_width, 0); + break; + case 2: // full + (*pipe_reg)->compute_simd_sets_compacted( + m_config->gpgpu_num_simd_sets, m_config->simd_set_width, 0); + break; + default: // 0 = none + (*pipe_reg)->compute_simd_sets(m_config->gpgpu_num_simd_sets, + m_config->simd_set_width); + break; + } } func_exec_inst(**pipe_reg); @@ -1317,12 +1325,22 @@ void shader_core_ctx::co_issue_warp(warp_inst_t *composite, m_warp[warp_id]->get_streamID()); // Compute SIMD sets for the co-issued instruction - if (m_config->gpgpu_enable_compaction) - temp_inst.compute_simd_sets_compacted(m_config->gpgpu_num_simd_sets, - m_config->simd_set_width, start_set); - else - temp_inst.compute_simd_sets(m_config->gpgpu_num_simd_sets, - m_config->simd_set_width); + switch (m_config->gpgpu_compaction_mode) { + case 1: // xor-static + temp_inst.compute_simd_sets_xor_static(m_config->gpgpu_num_simd_sets, + m_config->simd_set_width, + start_set); + break; + case 2: // full + temp_inst.compute_simd_sets_compacted(m_config->gpgpu_num_simd_sets, + m_config->simd_set_width, + start_set); + break; + default: // 0 = none + temp_inst.compute_simd_sets(m_config->gpgpu_num_simd_sets, + m_config->simd_set_width); + break; + } // Functional execution for the co-issued warp's threads func_exec_inst(temp_inst); @@ -1951,18 +1969,27 @@ void scheduler_unit::cycle() { unsigned cand_sets_needed; bool can_co_issue; - if (m_shader->m_config->gpgpu_enable_compaction) { - // With compaction: just count sets needed, no overlap check + if (m_shader->m_config->gpgpu_compaction_mode == 2) { + // Full compaction: just count sets needed, no overlap check. + // Compacted placement uses start_set, so no two sets ever alias. unsigned cand_active = cand_mask.count(); cand_sets_needed = (cand_active + set_width - 1) / set_width; can_co_issue = (cand_sets_needed <= available_sets); } else { - // Without compaction: build temp sets and check overlap + // No compaction or XOR-static: build temp sets with the selected + // mapping and check set-level overlap vs. primary. warp_inst_t cand_temp(m_shader->m_config); cand_temp = *cand_inst; cand_temp.issue(cand_mask, cand_warp_id, 0, 0, 0, 0); - cand_temp.compute_simd_sets(m_shader->m_config->gpgpu_num_simd_sets, - m_shader->m_config->simd_set_width); + if (m_shader->m_config->gpgpu_compaction_mode == 1) { + cand_temp.compute_simd_sets_xor_static( + m_shader->m_config->gpgpu_num_simd_sets, + m_shader->m_config->simd_set_width); + } else { + cand_temp.compute_simd_sets( + m_shader->m_config->gpgpu_num_simd_sets, + m_shader->m_config->simd_set_width); + } can_co_issue = !warp_inst_t::simd_sets_overlap( co_issue_composite->get_simd_sets(), @@ -5152,6 +5179,39 @@ void opndcoll_rfu_t::allocate_cu(unsigned port_num) { } } +void opndcoll_rfu_t::deliver_operand(op_t &op) { + unsigned cu = op.get_oc_id(); + unsigned operand = op.get_operand(); + m_cu[cu]->collect_operand(operand); + if (m_shader->get_config()->gpgpu_clock_gated_reg_file) { + unsigned active_count = 0; + for (unsigned i = 0; i < m_shader->get_config()->warp_size; + i = i + m_shader->get_config()->n_regfile_gating_group) { + for (unsigned j = 0; + j < m_shader->get_config()->n_regfile_gating_group; j++) { + if (op.get_active_mask().test(i + j)) { + active_count += m_shader->get_config()->n_regfile_gating_group; + break; + } + } + } + m_shader->incregfile_reads(active_count); + } else { + m_shader->incregfile_reads( + m_shader->get_config()->warp_size); // op.get_active_count(); + } +} + +void opndcoll_rfu_t::drain_pending_operands() { + unsigned long long now = m_shader->get_gpu()->gpu_sim_cycle + + m_shader->get_gpu()->gpu_tot_sim_cycle; + while (!m_pending_operands.empty() && + m_pending_operands.front().cycle_ready <= now) { + deliver_operand(m_pending_operands.front().op); + m_pending_operands.pop_front(); + } +} + void opndcoll_rfu_t::allocate_reads() { // process read requests that do not have conflicts std::list<op_t> allocated = m_arbiter.allocate_reads(); @@ -5166,28 +5226,26 @@ void opndcoll_rfu_t::allocate_reads() { m_arbiter.allocate_for_read(bank, rr); read_ops[bank] = rr; } - std::map<unsigned, op_t>::iterator r; - for (r = read_ops.begin(); r != read_ops.end(); ++r) { - op_t &op = r->second; - unsigned cu = op.get_oc_id(); - unsigned operand = op.get_operand(); - m_cu[cu]->collect_operand(operand); - if (m_shader->get_config()->gpgpu_clock_gated_reg_file) { - unsigned active_count = 0; - for (unsigned i = 0; i < m_shader->get_config()->warp_size; - i = i + m_shader->get_config()->n_regfile_gating_group) { - for (unsigned j = 0; j < m_shader->get_config()->n_regfile_gating_group; - j++) { - if (op.get_active_mask().test(i + j)) { - active_count += m_shader->get_config()->n_regfile_gating_group; - break; - } - } - } - m_shader->incregfile_reads(active_count); - } else { - m_shader->incregfile_reads( - m_shader->get_config()->warp_size); // op.get_active_count()); + + const unsigned latency = + m_shader->get_config()->gpgpu_opndcoll_read_latency; + if (latency == 0) { + // Current behavior: deliver immediately, no queue. Bit-identical + // to the pre-Change-3 tip. + for (std::map<unsigned, op_t>::iterator r = read_ops.begin(); + r != read_ops.end(); ++r) { + deliver_operand(r->second); + } + } else { + // Enqueue; drain will deliver on `now + latency` or later. + unsigned long long now = m_shader->get_gpu()->gpu_sim_cycle + + m_shader->get_gpu()->gpu_tot_sim_cycle; + for (std::map<unsigned, op_t>::iterator r = read_ops.begin(); + r != read_ops.end(); ++r) { + pending_operand_t pend; + pend.op = r->second; + pend.cycle_ready = now + latency; + m_pending_operands.push_back(pend); } } } diff --git a/src/gpgpu-sim/shader.h b/src/gpgpu-sim/shader.h index d8103ac..3d1aea9 100644 --- a/src/gpgpu-sim/shader.h +++ b/src/gpgpu-sim/shader.h @@ -751,6 +751,7 @@ class opndcoll_rfu_t { // operand collector based register file unit void step() { dispatch_ready_cu(); + drain_pending_operands(); allocate_reads(); for (unsigned p = 0; p < m_in_ports.size(); p++) allocate_cu(p); process_banks(); @@ -771,13 +772,22 @@ class opndcoll_rfu_t { // operand collector based register file unit private: void process_banks() { m_arbiter.reset_alloction(); } + // types (forward-declared so member function signatures below can + // reference nested types defined later in this class body) + class op_t; + class collector_unit_t; + void dispatch_ready_cu(); void allocate_cu(unsigned port); void allocate_reads(); - - // types - - class collector_unit_t; + // Deliver one allocated operand to its collector unit and bump the + // regfile-read stat. Shared by allocate_reads (latency=0 fast path) + // and drain_pending_operands (latency>0). + void deliver_operand(op_t &op); + // Drain any pending-operand queue entries whose cycle_ready has + // elapsed; called from step() between dispatch_ready_cu and + // allocate_reads so delivered operands dispatch on the *next* cycle. + void drain_pending_operands(); class op_t { public: @@ -1183,6 +1193,15 @@ class opndcoll_rfu_t { // operand collector based register file unit // port_to_du_t m_dispatch_units; // std::map<warp_inst_t**,std::list<collector_unit_t*> > m_free_cu; shader_core_ctx *m_shader; + + // Delay queue for modeling operand-collector read pipelining when + // `-gpgpu_opndcoll_read_latency > 0`. allocate_reads() pushes here; + // drain_pending_operands() pops at the top of step(). + struct pending_operand_t { + op_t op; + unsigned long long cycle_ready; + }; + std::deque<pending_operand_t> m_pending_operands; }; class barrier_set_t { @@ -1716,6 +1735,37 @@ class shader_core_config : public core_config { simd_set_width = warp_size; } + // --- Compaction-mode back-compat shim (Change 1) --- + // Old flag -gpgpu_enable_compaction maps to mode=2 (full). + // New flag -gpgpu_compaction_mode=2 re-asserts the old bool so + // the three existing call sites that still read the old flag + // continue to work unchanged. Change 2 will migrate the call + // sites to read gpgpu_compaction_mode directly. + if (gpgpu_compaction_mode > 2) { + fprintf(stderr, + "GPGPU-Sim: invalid -gpgpu_compaction_mode %u; must be " + "0, 1, or 2\n", + gpgpu_compaction_mode); + abort(); + } + if (gpgpu_co_issue_priority > 4) { + fprintf(stderr, + "GPGPU-Sim: invalid -gpgpu_co_issue_priority %u; must be " + "in [0,4]\n", + gpgpu_co_issue_priority); + abort(); + } + if (gpgpu_enable_compaction && gpgpu_compaction_mode == 0) { + gpgpu_compaction_mode = 2; + } + if (gpgpu_compaction_mode == 2) { + gpgpu_enable_compaction = true; + } + // Mode 1 (xor-static) has no existing-call-site equivalent yet; + // Change 2 wires it up. Until then, selecting mode=1 produces + // identity SIMD-set layout (compute_simd_sets) because + // gpgpu_enable_compaction is left as whatever the user set. + set_pipeline_latency(); m_L1I_config.init(m_L1I_config.m_config_string, FuncCachePreferNone); @@ -1863,6 +1913,9 @@ class shader_core_config : public core_config { unsigned gpgpu_num_simd_sets; unsigned simd_set_width; // derived: warp_size / num_simd_sets bool gpgpu_enable_compaction; + unsigned gpgpu_compaction_mode; // 0=none, 1=xor-static, 2=full + unsigned gpgpu_opndcoll_read_latency; // extra cycles to collector + unsigned gpgpu_co_issue_priority; // 0=greedy..4=same-PC unsigned n_simt_cores_per_cluster; unsigned n_simt_clusters; |
