from types import SimpleNamespace
from unittest import mock
from torch_npu.profiler._flops_formulas import (
_calculate_common_layout_flops,
matmul_flops,
npu_all_gather_base_mm_flops,
npu_alltoallv_gmm_flops,
npu_block_sparse_attention_flops,
npu_fusion_attention_flops,
npu_gmm_alltoallv_flops,
npu_grouped_matmul_flops,
npu_grouped_matmul_swiglu_quant_v2_flops,
npu_quant_matmul_gelu_flops,
npu_transpose_batchmatmul_flops,
)
from torch_npu.profiler._flops_hook import FlopsHookManager
from torch_npu.testing.testcase import run_tests, TestCase
class _Tensor:
def __init__(self, shape):
self.shape = shape
class _TensorWithList:
def __init__(self, value):
self._value = value
def tolist(self):
return self._value
class TestFlopsHook(TestCase):
def tearDown(self):
FlopsHookManager.uninstall()
def test_fusion_attention_formula_accepts_real_positional_arguments(self):
query = _Tensor((2, 4, 8, 16))
key = _Tensor((2, 4, 8, 16))
value = _Tensor((2, 4, 8, 16))
self.assertEqual(
2 * 2 * 4 * 8 * 8 * (16 + 16),
npu_fusion_attention_flops(query, key, value, 4, "BNSD"),
)
def test_fusion_attention_formula_uses_value_dim(self):
query = _Tensor((1, 2, 3, 4))
key = _Tensor((1, 2, 5, 4))
value = _Tensor((1, 2, 5, 7))
self.assertEqual(
2 * 1 * 2 * 3 * 5 * (4 + 7),
npu_fusion_attention_flops(query, key, value, 2, "BNSD"),
)
def test_sparse_formula_uses_sequence_lengths_for_non_square_attention(self):
self.assertEqual(
2 * 1 * 2 * (8 * 4 - 4 * 4 / 2) * (16 + 16),
_calculate_common_layout_flops(
(1, 2, 8, 16),
(1, 2, 4, 16),
(1, 2, 4, 16),
"BNSD",
2,
2,
2,
),
)
def test_all_gather_base_mm_formula_counts_gathered_gemm_only(self):
self.assertEqual(
2 * (3 * 2) * 4 * 5,
npu_all_gather_base_mm_flops(
_Tensor((3, 4)), _Tensor((4, 5)), "hcom", 2
),
)
def test_transpose_batchmatmul_formula_counts_batch_gemm_only(self):
self.assertEqual(
2 * 2 * 3 * 4 * 5,
npu_transpose_batchmatmul_flops(
_Tensor((3, 2, 4)),
_Tensor((2, 4, 5)),
perm_x1=(1, 0, 2),
),
)
def test_grouped_matmul_formula_sums_group_gemms(self):
self.assertEqual(
2 * 2 * 3 * 7 + 2 * 4 * 5 * 11,
npu_grouped_matmul_flops(
[_Tensor((2, 3)), _Tensor((4, 5))],
[_Tensor((3, 7)), _Tensor((5, 11))],
),
)
def test_grouped_matmul_formula_uses_group_list_for_split_groups(self):
self.assertEqual(
2 * 2 * 3 * 7 + 2 * 4 * 3 * 11,
npu_grouped_matmul_flops(
_Tensor((6, 3)),
[_Tensor((3, 7)), _Tensor((3, 11))],
group_list=[2, 6],
),
)
def test_grouped_matmul_formula_rejects_scalar_group_list(self):
with self.assertRaisesRegex(ValueError, "returning a sequence"):
npu_grouped_matmul_flops(
_Tensor((6, 3)),
[_Tensor((3, 7)), _Tensor((3, 11))],
group_list=_TensorWithList(6),
)
def test_quant_matmul_gelu_formula_counts_matmul_only(self):
self.assertEqual(
2 * 6 * 4 * 5,
npu_quant_matmul_gelu_flops(
_Tensor((2, 3, 4)), _Tensor((4, 5)), _Tensor((6,)), _Tensor((5,))
),
)
def test_grouped_matmul_swiglu_quant_formula_counts_grouped_gemm_only(self):
self.assertEqual(
2 * 8 * 4 * 16,
npu_grouped_matmul_swiglu_quant_v2_flops(
_Tensor((8, 4)),
_Tensor((2, 4, 16)),
_Tensor((2, 16)),
_Tensor((8,)),
_Tensor((2,)),
),
)
def test_alltoallv_gmm_formula_adds_optional_shared_mm(self):
self.assertEqual(
2 * 7 * 4 * 6 + 2 * 5 * 2 * 9,
npu_alltoallv_gmm_flops(
_Tensor((7, 4)),
_Tensor((3, 4, 6)),
"hcom",
2,
[3, 4],
[4, 3],
mm_x=_Tensor((5, 2)),
mm_weight=_Tensor((2, 9)),
),
)
def test_gmm_alltoallv_formula_counts_route_gmm_only_when_no_shared_mm(self):
self.assertEqual(
2 * 7 * 4 * 6,
npu_gmm_alltoallv_flops(
_Tensor((7, 4)),
_Tensor((3, 4, 6)),
"hcom",
2,
[3, 4],
[4, 3],
),
)
def test_block_sparse_attention_formula_counts_valid_blocks(self):
mask = [
[
[[1, 0], [0, 1], [1, 1]],
[[0, 1], [1, 0], [0, 0]],
]
]
self.assertEqual(
2 * 30 * (4 + 8),
npu_block_sparse_attention_flops(
_Tensor((1, 2, 5, 4)),
_Tensor((1, 2, 6, 4)),
_Tensor((1, 2, 6, 8)),
mask,
(2, 3),
q_input_layout="BNSD",
kv_input_layout="BNSD",
),
)
def test_block_sparse_attention_formula_keeps_bnsd_lengths_per_batch(self):
mask = [
[[[1, 0], [0, 1], [1, 1]]],
[[[1, 1], [0, 0], [1, 0]]],
]
self.assertEqual(
2 * 21 * (4 + 4),
npu_block_sparse_attention_flops(
_Tensor((2, 1, 5, 4)),
_Tensor((2, 1, 5, 4)),
_Tensor((2, 1, 5, 4)),
mask,
(2, 3),
q_input_layout="BNSD",
kv_input_layout="BNSD",
actual_seq_lengths=[3, 5],
actual_seq_lengths_kv=[5, 5],
),
)
def test_matmul_formula_supports_broadcast_batch_dimensions(self):
self.assertEqual(
2 * 5 * 2 * 3 * 4 * 6,
matmul_flops(_Tensor((5, 1, 3, 4)), _Tensor((2, 4, 6))),
)
def test_hook_records_flops_and_op_name_in_range_label(self):
target = SimpleNamespace()
def original(value):
return value + 1
target.op = original
with (
mock.patch(
"torch_npu.profiler._flops_hook.get_flop_func",
return_value=lambda value: value * 2,
),
mock.patch(
"torch_npu.profiler._flops_hook.mstx.range_start", return_value=7
) as mock_start,
mock.patch("torch_npu.profiler._flops_hook.mstx.range_end") as mock_end,
):
FlopsHookManager.install({"demo_op": (target, "op")})
self.assertEqual(4, target.op(3))
mock_start.assert_called_once_with("6-demo_op", domain="mfu_flops")
mock_end.assert_called_once_with(7, domain="mfu_flops")
if __name__ == "__main__":
run_tests()