Coverage for / home / jenkins / .local / lib / python3.10 / site-packages / hyper_multicore / modules / mega_moe / _gmm_tiling.py: 95%
38 statements
« prev ^ index » next coverage.py v7.13.1, created at 2026-11-15 05:03 +0800
« prev ^ index » next coverage.py v7.13.1, created at 2026-11-15 05:03 +0800
1# Copyright 2026 Huawei Technologies Co., Ltd
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14# ============================================================================
15"""GMM tiling template expansion and dimension patching."""
17import ctypes
19from hyper_multicore._core_spec import DEFAULT_CORE_SPEC
22def _expand_gmm_string(csv: str, repeat: int = DEFAULT_CORE_SPEC.num_cube_cores) -> bytes:
23 """Parse comma-separated uint32 string and tile it `repeat` times."""
24 vals = [int(x) for x in csv.split(',')]
25 arr = (ctypes.c_uint32 * (len(vals) * repeat))()
26 for i in range(len(vals) * repeat):
27 arr[i] = vals[i % len(vals)]
28 return bytes(arr)
31def _find_protected_positions(vals, active_map, split_value, is_weight_grad):
32 """Return index positions that must not be replaced by dim_map substitution.
34 When split_value collides with a K/N dim in active_map, the M-tile or
35 K-tile positions (depending on GMM type) must be shielded.
36 T is located via the 393216 anchor which sits at T+19.
37 """
38 if split_value is None or split_value not in active_map:
39 return set()
40 for i in range(350, len(vals)):
41 if vals[i] == 393216: # anchor always at t_base+19
42 t_base = i - 19
43 if is_weight_grad:
44 return {t_base + 3, t_base + 4, t_base + 7}
45 return {14, t_base + 1, t_base + 5}
46 return set()
49def _patch_gmm_dims(csv: str, dim_map: dict, num_groups: int,
50 default_groups: int = 8,
51 split_value: int = None,
52 num_cube_cores: int = DEFAULT_CORE_SPEC.num_cube_cores,
53 default_num_cores: int = 24) -> str:
54 """Replace matrix dimension values in a GMM tiling CSV string.
56 dim_map: {old_int_value: new_int_value} for K/N dimension replacements.
57 Entries where old == new are no-ops and can be omitted.
58 num_groups: replacement for default_groups; only applied at index > 350.
59 split_value: M tile size (table key). When it equals a K/N value in dim_map,
60 we must protect M-tile positions from accidental replacement.
61 Two GMM types detected via vals[10]:
62 0 → activation GMM: M tile at {14, T+1, T+5}
63 2 → weight-grad GMM: split (K-tile) at {T+3, T+4, T+7}
64 T is found via the 393216 anchor fixed at T+19.
65 num_cube_cores: number of Cube cores used by the operator.
66 Replaces the hardcoded usedCoreNum/blockDim fields in the CSV:
67 vals[1] — header blockDim field
68 i > 350 — tail usedCoreNum and any derived parallelism fields
69 """
70 active_map = {k: v for k, v in dim_map.items() if k != v}
71 groups_changed = num_groups != default_groups
72 cores_changed = num_cube_cores != default_num_cores
73 if not active_map and not groups_changed and not cores_changed:
74 return csv # fast path: nothing to do
76 vals = list(map(int, csv.split(',')))
77 is_weight_grad = vals[10] == 2
79 # Patch groupNum and blockDim before dimension substitution.
80 for index, old_value, new_value in ((0, default_groups, num_groups),
81 (1, default_num_cores, num_cube_cores)):
82 if vals[index] == old_value:
83 vals[index] = new_value
85 protected = _find_protected_positions(vals, active_map, split_value, is_weight_grad)
87 for i, v in enumerate(vals):
88 if i in protected:
89 continue
90 if v in active_map:
91 vals[i] = active_map[v]
92 elif cores_changed and i > 350 and v == default_num_cores:
93 vals[i] = num_cube_cores # covers T+0 (usedCoreNum) and derived fields
94 return ','.join(map(str, vals))