Skip to content

Commit f33a9cc

Browse files
karimelazzounimeta-codesync[bot]
authored andcommitted
Improve tests for ads_mkl/ops/oss/gdpa (2.0% → 90%)
Summary: Add comprehensive unit tests for the GDPA module covering utility functions, hardware detection, math activations, register helpers, vararg kernel AST transformations, and core GDPA helper functions. New test files (7 new, 1 existing expanded): Total: 158 tests passing across 8 test targets. Reviewed By: devashishshankar Differential Revision: D106511666 fbshipit-source-id: 4494ea0c3eef3894330161a7623609668a4a6890
1 parent 461eb63 commit f33a9cc

7 files changed

Lines changed: 1438 additions & 0 deletions

File tree

gdpa/tests/test_tma_utils.py

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
# Copyright (c) Meta Platforms, Inc. and affiliates.
2+
# All rights reserved.
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
16+
"""
17+
Unit tests for ads_mkl/ops/oss/gdpa/utils/tma_utils.py
18+
"""
19+
20+
import unittest
21+
from unittest.mock import MagicMock, patch
22+
23+
import torch
24+
from ads_mkl.ops.oss.gdpa.utils.tma_utils import is_tma_supported, TmaAutoTuneHelper
25+
26+
27+
class IsTmaSupportedTestCase(unittest.TestCase):
28+
"""Test is_tma_supported function."""
29+
30+
@patch("ads_mkl.ops.oss.gdpa.utils.tma_utils.torch.cuda.is_available")
31+
def test_returns_false_when_cuda_not_available(
32+
self, mock_available: MagicMock
33+
) -> None:
34+
"""Test returns False when CUDA is not available."""
35+
mock_available.return_value = False
36+
self.assertFalse(is_tma_supported())
37+
38+
@patch("ads_mkl.ops.oss.gdpa.utils.tma_utils.torch.version")
39+
@patch("ads_mkl.ops.oss.gdpa.utils.tma_utils.torch.cuda.get_device_capability")
40+
@patch("ads_mkl.ops.oss.gdpa.utils.tma_utils.torch.cuda.is_available")
41+
def test_returns_true_for_sm90_with_cuda_12_4(
42+
self,
43+
mock_available: MagicMock,
44+
mock_capability: MagicMock,
45+
mock_version: MagicMock,
46+
) -> None:
47+
"""Test returns True for SM90+ with CUDA >= 12.4."""
48+
mock_available.return_value = True
49+
mock_capability.return_value = (9, 0)
50+
mock_version.cuda = "12.4"
51+
self.assertTrue(is_tma_supported())
52+
53+
@patch("ads_mkl.ops.oss.gdpa.utils.tma_utils.torch.version")
54+
@patch("ads_mkl.ops.oss.gdpa.utils.tma_utils.torch.cuda.get_device_capability")
55+
@patch("ads_mkl.ops.oss.gdpa.utils.tma_utils.torch.cuda.is_available")
56+
def test_returns_false_for_sm80(
57+
self,
58+
mock_available: MagicMock,
59+
mock_capability: MagicMock,
60+
mock_version: MagicMock,
61+
) -> None:
62+
"""Test returns False for SM80 (below SM90)."""
63+
mock_available.return_value = True
64+
mock_capability.return_value = (8, 0)
65+
mock_version.cuda = "12.4"
66+
self.assertFalse(is_tma_supported())
67+
68+
@patch("ads_mkl.ops.oss.gdpa.utils.tma_utils.torch.version")
69+
@patch("ads_mkl.ops.oss.gdpa.utils.tma_utils.torch.cuda.get_device_capability")
70+
@patch("ads_mkl.ops.oss.gdpa.utils.tma_utils.torch.cuda.is_available")
71+
def test_returns_false_for_old_cuda(
72+
self,
73+
mock_available: MagicMock,
74+
mock_capability: MagicMock,
75+
mock_version: MagicMock,
76+
) -> None:
77+
"""Test returns False for CUDA < 12.4."""
78+
mock_available.return_value = True
79+
mock_capability.return_value = (9, 0)
80+
mock_version.cuda = "12.3"
81+
self.assertFalse(is_tma_supported())
82+
83+
@patch("ads_mkl.ops.oss.gdpa.utils.tma_utils.torch.cuda.is_available")
84+
def test_returns_false_on_exception(self, mock_available: MagicMock) -> None:
85+
"""Test returns False on any exception."""
86+
mock_available.side_effect = RuntimeError("No CUDA")
87+
self.assertFalse(is_tma_supported())
88+
89+
90+
class TmaAutoTuneHelperTestCase(unittest.TestCase):
91+
"""Test TmaAutoTuneHelper constants and KernelParamWrapper."""
92+
93+
def test_tma_size_constant(self) -> None:
94+
"""Test TMA_SIZE is 128."""
95+
self.assertEqual(TmaAutoTuneHelper.TMA_SIZE, 128)
96+
97+
def test_kernel_param_wrapper_returns_pointer(self) -> None:
98+
"""Test KernelParamWrapper returns correct pointer."""
99+
desc = torch.empty(128, device="cpu", dtype=torch.int8)
100+
wrapper = TmaAutoTuneHelper.KernelParamWrapper(desc)
101+
self.assertEqual(wrapper.tma_desc_cpu_ptr(), desc.data_ptr())
102+
103+
def test_kernel_param_wrapper_stores_desc(self) -> None:
104+
"""Test KernelParamWrapper stores the descriptor reference."""
105+
desc = torch.empty(128, device="cpu", dtype=torch.int8)
106+
wrapper = TmaAutoTuneHelper.KernelParamWrapper(desc)
107+
self.assertIs(wrapper.desc, desc)
108+
109+
def test_kernel_param_wrapper_alignment(self) -> None:
110+
"""Test KernelParamWrapper data_ptr is accessible."""
111+
desc = torch.empty(128, device="cpu", dtype=torch.int8)
112+
wrapper = TmaAutoTuneHelper.KernelParamWrapper(desc)
113+
ptr = wrapper.tma_desc_cpu_ptr()
114+
self.assertIsInstance(ptr, int)
115+
self.assertGreater(ptr, 0)
116+
117+
118+
if __name__ == "__main__":
119+
unittest.main()

gdpa/tests/test_utils.py

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
# Copyright (c) Meta Platforms, Inc. and affiliates.
2+
# All rights reserved.
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
16+
"""
17+
Unit tests for ads_mkl/ops/oss/gdpa/utils/utils.py
18+
"""
19+
20+
import logging
21+
import os
22+
import unittest
23+
from unittest.mock import MagicMock, patch
24+
25+
import torch
26+
from ads_mkl.ops.oss.gdpa.utils.utils import (
27+
dump_kernel_info,
28+
DUMP_KERNEL_INFO_ENV_VAR,
29+
get_num_sms,
30+
get_num_warps,
31+
should_use_i64_idx,
32+
)
33+
34+
35+
class GetNumWarpsTestCase(unittest.TestCase):
36+
"""Test get_num_warps function."""
37+
38+
def test_get_num_warps_returns_int(self) -> None:
39+
"""Test that get_num_warps returns an integer."""
40+
result = get_num_warps()
41+
self.assertIsInstance(result, int)
42+
self.assertIn(result, [16, 32])
43+
44+
45+
class ShouldUseI64IdxTestCase(unittest.TestCase):
46+
"""Test should_use_i64_idx function."""
47+
48+
def test_small_tensors_return_false(self) -> None:
49+
"""Test small tensors don't need 64-bit indexing."""
50+
t1 = torch.randn(10, 10)
51+
t2 = torch.randn(100, 100)
52+
self.assertFalse(should_use_i64_idx(t1, t2))
53+
54+
def test_large_tensor_returns_true(self) -> None:
55+
"""Test tensor with >= 2^31 elements needs 64-bit indexing."""
56+
# Create a mock tensor that pretends to have >= 2^31 elements
57+
large_tensor = MagicMock(spec=torch.Tensor)
58+
large_tensor.numel.return_value = 2**31
59+
self.assertTrue(should_use_i64_idx(large_tensor))
60+
61+
def test_empty_args_return_false(self) -> None:
62+
"""Test no arguments returns False."""
63+
self.assertFalse(should_use_i64_idx())
64+
65+
def test_single_small_tensor(self) -> None:
66+
"""Test single small tensor returns False."""
67+
t = torch.randn(10)
68+
self.assertFalse(should_use_i64_idx(t))
69+
70+
def test_one_large_among_many_returns_true(self) -> None:
71+
"""Test that if any tensor is large, returns True."""
72+
t1 = torch.randn(10)
73+
large_tensor = MagicMock(spec=torch.Tensor)
74+
large_tensor.numel.return_value = 2**31
75+
t3 = torch.randn(10)
76+
self.assertTrue(should_use_i64_idx(t1, large_tensor, t3))
77+
78+
def test_just_below_threshold_returns_false(self) -> None:
79+
"""Test tensor just below 2^31 elements returns False."""
80+
tensor = MagicMock(spec=torch.Tensor)
81+
tensor.numel.return_value = 2**31 - 1
82+
self.assertFalse(should_use_i64_idx(tensor))
83+
84+
85+
class DumpKernelInfoTestCase(unittest.TestCase):
86+
"""Test dump_kernel_info function."""
87+
88+
def test_dump_disabled_by_default(self) -> None:
89+
"""Test that dump is disabled when env var is not set."""
90+
kernel_info = MagicMock()
91+
with patch.dict(os.environ, {}, clear=True):
92+
# Should not raise and not write any files
93+
dump_kernel_info(kernel_info)
94+
95+
def test_dump_disabled_when_env_var_is_zero(self) -> None:
96+
"""Test that dump is disabled when env var is '0'."""
97+
kernel_info = MagicMock()
98+
with patch.dict(os.environ, {DUMP_KERNEL_INFO_ENV_VAR: "0"}):
99+
dump_kernel_info(kernel_info)
100+
101+
@patch("builtins.open", create=True)
102+
def test_dump_enabled_when_env_var_is_one(self, mock_open: MagicMock) -> None:
103+
"""Test that dump writes files when env var is '1'."""
104+
kernel_info = MagicMock()
105+
kernel_info.metadata.name = "test_kernel"
106+
kernel_info.n_spills = 0
107+
kernel_info.n_regs = 32
108+
kernel_info.asm = {
109+
"ttir": "ttir_content",
110+
"ttgir": "ttgir_content",
111+
"llir": "llir_content",
112+
"ptx": "ptx_content",
113+
}
114+
with patch.dict(os.environ, {DUMP_KERNEL_INFO_ENV_VAR: "1"}):
115+
with self.assertLogs(level=logging.INFO):
116+
dump_kernel_info(kernel_info)
117+
118+
119+
class GetNumSmsTestCase(unittest.TestCase):
120+
"""Test get_num_sms function."""
121+
122+
@patch("ads_mkl.ops.oss.gdpa.utils.utils.torch.cuda.is_available")
123+
def test_returns_none_when_cuda_not_available(
124+
self, mock_cuda_available: MagicMock
125+
) -> None:
126+
"""Test returns None when CUDA is not available."""
127+
mock_cuda_available.return_value = False
128+
# Clear lru_cache
129+
get_num_sms.cache_clear()
130+
result = get_num_sms()
131+
self.assertIsNone(result)
132+
133+
@patch("ads_mkl.ops.oss.gdpa.utils.utils.torch.cuda.get_device_properties")
134+
@patch("ads_mkl.ops.oss.gdpa.utils.utils.torch.cuda.is_available")
135+
def test_returns_sm_count_when_cuda_available(
136+
self, mock_cuda_available: MagicMock, mock_get_props: MagicMock
137+
) -> None:
138+
"""Test returns SM count when CUDA is available."""
139+
mock_cuda_available.return_value = True
140+
mock_props = MagicMock()
141+
mock_props.multi_processor_count = 108
142+
mock_get_props.return_value = mock_props
143+
# Clear lru_cache
144+
get_num_sms.cache_clear()
145+
result = get_num_sms()
146+
self.assertEqual(result, 108)
147+
148+
149+
if __name__ == "__main__":
150+
unittest.main()

0 commit comments

Comments
 (0)