Class: Ignis::Collective::Algorithms::TopologyRouter
- Inherits:
-
Object
- Object
- Ignis::Collective::Algorithms::TopologyRouter
- Defined in:
- lib/nvruby/collective/algorithms/topology_router.rb
Overview
Topology-Aware Routing Optimizer
Optimizes collective operation routing based on PCIe/NVLink topology. Groups GPUs by PCIe switch to minimize cross-switch traffic.
Key optimizations:
-
Intra-switch communication first (lower latency)
-
Ring order follows physical topology
-
Tree structure respects switch boundaries
Defined Under Namespace
Classes: SwitchGroup
Instance Attribute Summary collapse
-
#gpu_ids ⇒ Array<Integer>
readonly
GPU IDs.
-
#switch_groups ⇒ Array<SwitchGroup>
readonly
GPU groups by switch.
-
#topology ⇒ Topology::Detector
readonly
Topology detector.
Instance Method Summary collapse
-
#get_path(src_rank, dst_rank) ⇒ Array<Integer>
Get routing path between two GPUs.
-
#initialize(gpu_ids:, topology: nil) ⇒ TopologyRouter
constructor
A new instance of TopologyRouter.
-
#optimal_ring_order ⇒ Array<Integer>
Get optimized ring order based on topology Groups GPUs by PCIe switch, then chains switches.
-
#optimal_tree_structure(root: 0) ⇒ Hash
Get optimized tree structure that respects switch boundaries.
-
#suggest_algorithm(message_size) ⇒ Symbol
Suggest optimal algorithm based on message size and topology.
Constructor Details
#initialize(gpu_ids:, topology: nil) ⇒ TopologyRouter
Returns a new instance of TopologyRouter.
30 31 32 33 34 35 36 |
# File 'lib/nvruby/collective/algorithms/topology_router.rb', line 30 def initialize(gpu_ids:, topology: nil) @gpu_ids = gpu_ids.dup.freeze @topology = topology || Topology::Detector.new @switch_groups = [] @routing_table = {} detect_switch_groups! end |
Instance Attribute Details
#gpu_ids ⇒ Array<Integer> (readonly)
Returns GPU IDs.
23 24 25 |
# File 'lib/nvruby/collective/algorithms/topology_router.rb', line 23 def gpu_ids @gpu_ids end |
#switch_groups ⇒ Array<SwitchGroup> (readonly)
Returns GPU groups by switch.
26 27 28 |
# File 'lib/nvruby/collective/algorithms/topology_router.rb', line 26 def switch_groups @switch_groups end |
#topology ⇒ Topology::Detector (readonly)
Returns Topology detector.
20 21 22 |
# File 'lib/nvruby/collective/algorithms/topology_router.rb', line 20 def topology @topology end |
Instance Method Details
#get_path(src_rank, dst_rank) ⇒ Array<Integer>
Get routing path between two GPUs
153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 |
# File 'lib/nvruby/collective/algorithms/topology_router.rb', line 153 def get_path(src_rank, dst_rank) return [src_rank] if src_rank == dst_rank src_gpu = @gpu_ids[src_rank] dst_gpu = @gpu_ids[dst_rank] src_group = find_group(src_gpu) dst_group = find_group(dst_gpu) if src_group == dst_group # Direct path within same switch [src_rank, dst_rank] else # Need to cross switch boundary - use group representative # Find best intermediate hop intermediate = pick_best_connector_rank(src_group, dst_group) if intermediate == src_rank || intermediate == dst_rank [src_rank, dst_rank] else [src_rank, intermediate, dst_rank] end end end |
#optimal_ring_order ⇒ Array<Integer>
Get optimized ring order based on topology Groups GPUs by PCIe switch, then chains switches
42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
# File 'lib/nvruby/collective/algorithms/topology_router.rb', line 42 def optimal_ring_order return @gpu_ids if @switch_groups.size <= 1 # Build ring by chaining switch groups # Within each group, order by P2P performance ring = [] # Sort groups by average bandwidth (best first) sorted_groups = @switch_groups.sort_by { |g| -g.bandwidth_gbps } sorted_groups.each do |group| # Order GPUs within group by P2P performance ordered = order_within_group(group.gpu_ids) ring.concat(ordered) end ring end |
#optimal_tree_structure(root: 0) ⇒ Hash
Get optimized tree structure that respects switch boundaries
65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 |
# File 'lib/nvruby/collective/algorithms/topology_router.rb', line 65 def optimal_tree_structure(root: 0) tree = {} n = @gpu_ids.size return { 0 => { parent: nil, children: [] } } if n == 1 # Build hierarchical tree: # - Level 0: Root # - Level 1: One representative from each switch group # - Level 2+: Other GPUs within each switch group # Find which group the root belongs to root_group_idx = @switch_groups.find_index { |g| g.gpu_ids.include?(@gpu_ids[root]) } root_group = @switch_groups[root_group_idx] # Root has no parent tree[root] = { parent: nil, children: [] } # Connect switch group representatives to root group_reps = [] @switch_groups.each_with_index do |group, idx| next if idx == root_group_idx # Pick highest-bandwidth GPU in group as representative rep_gpu = pick_best_connector(root_group, group) rep_rank = @gpu_ids.index(rep_gpu) tree[root][:children] << rep_rank tree[rep_rank] = { parent: root, children: [] } group_reps << { rank: rep_rank, group: group } end # Connect other GPUs in root's group directly to root root_group.gpu_ids.each do |gpu| rank = @gpu_ids.index(gpu) next if rank == root tree[root][:children] << rank tree[rank] = { parent: root, children: [] } end # Connect remaining GPUs in each group to their representative group_reps.each do |rep_info| rep_rank = rep_info[:rank] rep_gpu = @gpu_ids[rep_rank] rep_info[:group].gpu_ids.each do |gpu| next if gpu == rep_gpu rank = @gpu_ids.index(gpu) tree[rep_rank][:children] << rank tree[rank] = { parent: rep_rank, children: [] } end end tree end |
#suggest_algorithm(message_size) ⇒ Symbol
Suggest optimal algorithm based on message size and topology
127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 |
# File 'lib/nvruby/collective/algorithms/topology_router.rb', line 127 def suggest_algorithm() # Ring is bandwidth-optimal for large messages # Tree is latency-optimal for small messages # Double tree for non-power-of-2 with medium messages n = @gpu_ids.size is_power_of_2 = n > 0 && (n & (n - 1)).zero? if < 1024 # < 1KB :tree elsif > 1_048_576 # > 1MB :ring elsif !is_power_of_2 :double_tree else # Medium messages: prefer ring if good interconnect avg_bandwidth = @switch_groups.sum { |g| g.bandwidth_gbps } / @switch_groups.size.to_f avg_bandwidth > 50 ? :ring : :tree end end |