1- import copy
2- from accelforge .frontend import arch
3- from accelforge .frontend .arch import Network as NetworkSpec
41from accelforge .frontend .mapping import (
5- TensorHolder ,
6- TensorName
2+ Spatial
73)
84from accelforge .frontend ._workload_isl ._symbolic import (
95 compute_dense_tile_occupancy ,
2016
2117class NetworkAnalyzer :
2218 def __init__ (self , network_stats ):
23- self .overall_max_hops = 0
19+ self .overall_max_hops : dict = {}
2420 self .network_stats = network_stats
2521
2622 def accumulate_child_result (
@@ -30,8 +26,9 @@ def accumulate_child_result(
3026 shape_repeats ,
3127 einsum_name ,
3228 child_shape ,
33- node ,
29+ node : Spatial ,
3430 ):
31+ """This function is called for every repeated shape."""
3532 flattened_arch = info .job .flattened_arch
3633
3734 for network , child_network_stats in child_result .network_stats .items ():
@@ -40,44 +37,37 @@ def accumulate_child_result(
4037 self .network_stats [network ] = NetworkStats ()
4138 accumulated_network_stats = self .network_stats [network ]
4239
43- accumulated_network_stats .total_hops += (
44- child_network_stats .total_hops * shape_repeats
45- )
46- accumulated_network_stats .max_hops = MaxGeqZero (
47- accumulated_network_stats .max_hops ,
48- child_network_stats .max_hops ,
49- )
50- projection = info .einsum_tensor_to_projection [(einsum_name , network .tensor )]
51- component_object = flattened_arch [network .component ]
52- workload_bpv = info .job .einsum .tensor_accesses [
53- network .tensor
54- ].bits_per_value
55- bits_per_value = component_object .bits_per_value .get (
56- network .tensor , workload_bpv
57- )
58- bits_per_action = component_object .bits_per_action
59- if bits_per_action is not None :
60- actions_per_value = bits_per_value / bits_per_action
61- else :
62- actions_per_value = bits_per_value
63- volume = (
64- compute_dense_tile_occupancy (projection , child_shape )
65- * actions_per_value
66- )
67-
40+ # We only need to update the summary if the spatial loop is for
41+ # a component higher than the network of interest
6842 if flattened_arch .is_above (node .component , network .component ):
43+ accumulated_network_stats .total_hops += (
44+ child_network_stats .total_hops * shape_repeats
45+ )
46+ accumulated_network_stats .max_hops = MaxGeqZero (
47+ accumulated_network_stats .max_hops ,
48+ child_network_stats .max_hops ,
49+ )
50+ for k , v in child_network_stats .max_traffic .items ():
51+ accumulated_network_stats .max_traffic [k ] = MaxGeqZero (
52+ accumulated_network_stats .max_traffic .get (k , 0 ),
53+ v
54+ )
6955 continue
7056
57+ volume = self ._get_data_volume (network , einsum_name , info , child_shape )
58+
7159 relevancy = info .tensor_to_relevancy [network .tensor ][node .rank_variable ]
7260
7361 # The fanout in this dimension in mapping nodes below, i.e., the stride
7462 last_fanout = child_result .fanout .get ((node .component , einsum_name ), {})
7563 last_fanout = last_fanout .get (node .name , 1 )
7664 if isinstance (relevancy , Irrelevant ):
65+ # The volume travels through link by link in one axis of the mesh
7766 # Distributed or not, the amount of total cost is the same.
7867 # However, the accesses now come from different physical memories
7968 total_cost = multicast_cost (shape_repeats , last_fanout )* volume
8069 max_hops = shape_repeats * last_fanout
70+ max_traffic = volume
8171 elif isinstance (relevancy , Relevant ):
8272 # If distributed, then we bind data as locally as possible in the
8373 # physical buffers
@@ -99,26 +89,56 @@ def accumulate_child_result(
9989 *
10090 volume
10191 )
102- max_hops = MinGeqZero (shape_repeats * last_fanout , physical_stride )
92+ max_hops = MinGeqZero ((n_dsts_per_physical - 1 )* last_fanout , physical_stride )
93+ max_traffic = (n_dsts_per_physical - 1 )* volume
10394 else :
10495 total_cost = unicast_cost (shape_repeats , last_fanout )* volume
10596 max_hops = shape_repeats * last_fanout
97+ max_traffic = (shape_repeats - 1 )* volume
10698 elif isinstance (relevancy , PartiallyRelevant ):
10799 raise NotImplementedError ()
108100 else :
109101 raise RuntimeError (f"unhandled relevancy type { relevancy } " )
110102
111- # TODO: this is sketchy
112- self .overall_max_hops += max_hops
103+ # Each subsequent call to this function (i.e., over different iterations of a spatial loop)
104+ # adds more to the max hops
105+ self .overall_max_hops [network ] = self .overall_max_hops .get (network , 0 ) + max_hops
113106
114- accumulated_network_stats .total_hops += total_cost
107+ accumulated_network_stats .total_hops += (
108+ total_cost + child_network_stats .total_hops * shape_repeats
109+ )
115110 accumulated_network_stats .max_hops = MaxGeqZero (
116111 accumulated_network_stats .max_hops ,
117- self .overall_max_hops + child_network_stats .max_hops ,
112+ self .overall_max_hops [network ] + child_network_stats .max_hops ,
113+ )
114+ accumulated_network_stats .max_traffic [node .name ] = MaxGeqZero (
115+ accumulated_network_stats .max_traffic .get (node .name , 0 ),
116+ max_traffic + child_network_stats .max_traffic .get (node .name , 0 )
118117 )
119118
120119 return self .overall_max_hops
121120
121+ def _get_data_volume (self , network , einsum_name , info , child_shape ):
122+ flattened_arch = info .job .flattened_arch
123+ projection = info .einsum_tensor_to_projection [(einsum_name , network .tensor )]
124+ component_object = flattened_arch [network .component ]
125+ workload_bpv = info .job .einsum .tensor_accesses [
126+ network .tensor
127+ ].bits_per_value
128+ bits_per_value = component_object .bits_per_value .get (
129+ network .tensor , workload_bpv
130+ )
131+ bits_per_action = component_object .bits_per_action
132+ if bits_per_action is not None :
133+ actions_per_value = bits_per_value / bits_per_action
134+ else :
135+ actions_per_value = bits_per_value
136+ volume = (
137+ compute_dense_tile_occupancy (projection , child_shape )
138+ * actions_per_value
139+ )
140+ return volume
141+
122142
123143def multicast_cost (n_dsts , stride ):
124144 """Returns total hops of multicast along a dimension."""
0 commit comments