Skip to content

Commit a0cd5bb

Browse files
committed
Fix zero-like GPU launches, architecture-aware caching, and JIT API
1 parent 2de1570 commit a0cd5bb

12 files changed

Lines changed: 215 additions & 71 deletions

File tree

src/ninetoothed/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from ninetoothed.aot import aot
22
from ninetoothed.build import build
33
from ninetoothed.compiler import lower
4+
from ninetoothed.compiler.jit import jit
45
from ninetoothed.dtype import (
56
bfloat16,
67
float16,
@@ -17,7 +18,6 @@
1718
)
1819
from ninetoothed.eval import _eval as eval
1920
from ninetoothed.eval import _subs as subs
20-
from ninetoothed.jit import jit
2121
from ninetoothed.make import make
2222
from ninetoothed.symbol import Symbol, block_size
2323
from ninetoothed.tensor import Tensor

src/ninetoothed/backends/emitters/cuda.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,9 @@ def render_module(self, context: ModuleRenderContext) -> str:
288288
) {{
289289
constexpr int threads = {threads};
290290
int64_t blocks = {blocks_expr};
291+
if (blocks <= 0) {{
292+
return static_cast<int>(cudaSuccess);
293+
}}
291294
{kernel.kernel_name}_kernel<<<static_cast<unsigned int>(blocks), threads, 0, stream>>>(
292295
{args}
293296
);

src/ninetoothed/backends/materializers/cuda.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,16 +96,21 @@ def _cuda_wrapper(function, abi, tensor_specs):
9696
from ninetoothed.compiler.runtime import (
9797
KernelLaunchError,
9898
_bound_values,
99+
_empty_launch,
99100
_first_output,
100101
_public_values,
101102
)
102103

103104
spec_by_name = {spec.name: spec for spec in tensor_specs}
104105

105106
def launch(*args, **kwargs):
107+
public = _public_values(abi, args, kwargs, specs=tensor_specs)
108+
109+
if _empty_launch(abi, public):
110+
return _first_output(abi, public)
111+
106112
import torch
107113

108-
public = _public_values(abi, args, kwargs, specs=tensor_specs)
109114
values, keepalive = _bound_values(
110115
abi,
111116
public,

src/ninetoothed/compiler/__init__.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,14 @@
1111
make,
1212
resolve_target,
1313
)
14-
from ninetoothed.compiler.jit import JIT, jit
14+
from ninetoothed.compiler.jit import jit
1515
from ninetoothed.compiler.runtime import load_built_artifact
1616

1717
__all__ = [
1818
"Compiler",
1919
"Compilation",
2020
"CompileRequest",
2121
"DEFAULT_COMPILER",
22-
"JIT",
2322
"aot",
2423
"compile_kernel",
2524
"jit",

src/ninetoothed/compiler/cache.py

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ def compilation_cache_key(compilation) -> str:
4949
"backend_options": request.backend_options,
5050
"pipeline": request.pipeline,
5151
"pass_options": request.pass_options,
52-
"architecture": _architecture(),
52+
"architecture": _architecture(compilation),
5353
"versions": _compiler_versions(),
5454
}
5555
)
@@ -191,13 +191,51 @@ def _compiler_versions() -> Mapping[str, str]:
191191
return result
192192

193193

194-
def _architecture() -> Mapping[str, str | None]:
195-
return {
194+
def _architecture(compilation) -> Mapping[str, Any]:
195+
backend = compilation.artifact.backend.value
196+
backend_options = dict(compilation.request.backend_options or {})
197+
architecture = {
196198
"machine": platform.machine(),
197199
"cuda_arch": os.environ.get("TORCH_CUDA_ARCH_LIST"),
198200
"cuda_visible_devices": os.environ.get("CUDA_VISIBLE_DEVICES"),
199201
}
200202

203+
if backend == "cuda":
204+
target = str(backend_options.get("arch", "native"))
205+
architecture["cuda_target"] = target
206+
207+
if target != "native":
208+
return architecture
209+
210+
if backend in {"cuda", "tilelang", "triton"}:
211+
architecture.update(_runtime_cuda_architecture())
212+
return architecture
213+
214+
215+
def _runtime_cuda_architecture() -> Mapping[str, Any]:
216+
import torch
217+
218+
if not torch.cuda.is_available():
219+
return {
220+
"cuda_current_capability": None,
221+
"cuda_visible_capabilities": (),
222+
}
223+
224+
def capability(device: int) -> str:
225+
major, minor = torch.cuda.get_device_capability(device)
226+
227+
return f"sm_{major}{minor}"
228+
229+
current = capability(torch.cuda.current_device())
230+
visible = tuple(
231+
dict.fromkeys(capability(device) for device in range(torch.cuda.device_count()))
232+
)
233+
234+
return {
235+
"cuda_current_capability": current,
236+
"cuda_visible_capabilities": visible,
237+
}
238+
201239

202240
def _json_value(value: Any) -> Any:
203241
if value is None or isinstance(value, (bool, int, float, str)):

src/ninetoothed/compiler/jit.py

Lines changed: 13 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -24,65 +24,22 @@ def jit(
2424
)
2525

2626
def wrapper(application):
27-
return JIT(
28-
application,
29-
caller=caller,
30-
backend=backend,
31-
kernel_name=kernel_name,
32-
num_warps=num_warps,
33-
num_stages=num_stages,
34-
max_num_configs=max_num_configs,
35-
pipeline=pipeline,
36-
pass_options=pass_options,
37-
backend_options=backend_options,
38-
)()
39-
40-
return wrapper if func is None else wrapper(func)
41-
42-
43-
class JIT:
44-
"""Deferred materialization object used by :func:`jit`."""
45-
46-
def __init__(
47-
self,
48-
func,
49-
*,
50-
backend,
51-
caller,
52-
kernel_name,
53-
num_warps,
54-
num_stages,
55-
max_num_configs,
56-
pipeline,
57-
pass_options,
58-
backend_options=None,
59-
):
60-
self.func = func
61-
self._caller = caller
62-
self._backend = backend
63-
self._kernel_name = kernel_name or func.__name__
64-
self._num_warps = num_warps
65-
self._num_stages = num_stages
66-
self._max_num_configs = max_num_configs
67-
self._pipeline = pipeline
68-
self._pass_options = pass_options
69-
self._backend_options = dict(backend_options or {})
70-
71-
def __call__(self):
7227
return DEFAULT_COMPILER.materialize(
7328
CompileRequest(
74-
application=self.func,
75-
backend=self._backend,
76-
caller=self._caller,
77-
kernel_name=self._kernel_name,
78-
num_warps=self._num_warps,
79-
num_stages=self._num_stages,
80-
max_num_configs=self._max_num_configs,
81-
pipeline=self._pipeline,
82-
pass_options=self._pass_options,
83-
backend_options=self._backend_options,
29+
application=application,
30+
backend=backend,
31+
caller=caller,
32+
kernel_name=kernel_name or application.__name__,
33+
num_warps=num_warps,
34+
num_stages=num_stages,
35+
max_num_configs=max_num_configs,
36+
pipeline=pipeline,
37+
pass_options=pass_options,
38+
backend_options=backend_options,
8439
)
8540
)
8641

42+
return wrapper if func is None else wrapper(func)
43+
8744

88-
__all__ = ["JIT", "jit"]
45+
__all__ = ["jit"]

src/ninetoothed/compiler/runtime.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,10 @@ def _runtime_wrapper(
276276
):
277277
def launch(*args, **kwargs):
278278
public = _public_values(abi, args, kwargs, specs=specs)
279+
280+
if _empty_launch(abi, public):
281+
return _first_output(abi, public)
282+
279283
values, keepalive = _bound_values(abi, public, scalar_mode="value")
280284
bindings = abi.kernel_args
281285

src/ninetoothed/jit.py

Lines changed: 0 additions & 5 deletions
This file was deleted.

tests/test_backend_registry.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,7 @@ def test_optimization_metadata_contains_only_materialized_choices(self):
206206
def test_generic_cuda_launch_matches_emitted_thread_count(self):
207207
artifact = emit(_add_kernel(), "cuda")
208208
assert "constexpr int threads = 256;" in artifact.primary_source
209+
assert "if (blocks <= 0)" in artifact.primary_source
209210
assert artifact.metadata["launch_block"] == ("256",)
210211

211212
def test_artifact_can_write_all_sources(self, tmp_path):

tests/test_built_artifact_reload.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,38 @@ def test_aot_built_artifact_can_be_reloaded(backend, device, tmp_path):
6868
)
6969

7070

71+
@pytest.mark.parametrize("device", get_available_devices())
72+
@pytest.mark.parametrize("mode", ("jit", "aot"))
73+
def test_cuda_empty_tensor_is_a_no_op(mode, device, tmp_path):
74+
tensors = tuple(Tensor(1, dtype=ninetoothed.float32) for _ in range(3))
75+
compilation = DEFAULT_COMPILER.compile(
76+
CompileRequest(
77+
arrangement=_arrangement,
78+
application=_application,
79+
tensors=tensors,
80+
backend="cuda",
81+
kernel_name=f"empty_cuda_{mode}",
82+
)
83+
)
84+
handle = DEFAULT_COMPILER.materialize(
85+
compilation,
86+
output_dir=tmp_path if mode == "aot" else None,
87+
mode=mode,
88+
)
89+
input = torch.empty(0, device=device)
90+
other = torch.empty_like(input)
91+
output = torch.empty_like(input)
92+
93+
assert handle(input, other, output) is output
94+
torch.cuda.synchronize()
95+
96+
if mode == "aot":
97+
reloaded = load_built_artifact(handle._built_artifact)
98+
reloaded_output = torch.empty_like(input)
99+
assert reloaded(input, other, reloaded_output) is reloaded_output
100+
torch.cuda.synchronize()
101+
102+
71103
_RELOAD_IN_SUBPROCESS = """
72104
import pickle
73105
import sys

0 commit comments

Comments
 (0)