Files
apache--tvm/tests/python/relax/test_transform_rewrite_cuda_graph.py
T
wehub-resource-sync 26446540fa
Lint / lint (push) Waiting to run
CI / MacOS (push) Waiting to run
CI / Windows (push) Waiting to run
chore: import upstream snapshot with attribution
2026-07-13 13:36:25 +08:00

1194 lines
58 KiB
Python

# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
# ruff: noqa: E501, F841
import pytest
import tvm_ffi
import tvm
import tvm.testing
from tvm import relax
from tvm.script import ir as I
from tvm.script import relax as R
from tvm.script import tirx as T
@pytest.fixture(autouse=True)
def enable_cuda_graph():
"""Enable cuda graph transform for all tests in this file"""
with tvm.transform.PassContext(config={"relax.backend.use_cuda_graph": True}):
yield
def test_rewrite_cuda_graph():
# fmt: off
@I.ir_module(s_tir=True)
class Before:
@T.prim_func(s_tir=True)
def exp(rxplaceholder: T.Buffer((T.int64(2), T.int64(4)), "float32"), compute: T.Buffer((T.int64(2), T.int64(4)), "float32")):
# function attr dict
T.func_attr({"tirx.noalias": True, "global_symbol": "exp"})
for i0_i1_fused_0 in T.thread_binding(T.int64(1), thread="blockIdx.x"):
for i0_i1_fused_1 in T.thread_binding(T.int64(8), thread="threadIdx.x"):
with T.sblock("compute"):
i0 = T.axis.spatial(T.int64(2), (i0_i1_fused_0 * T.int64(8) + i0_i1_fused_1) // T.int64(4))
i1 = T.axis.spatial(T.int64(4), (i0_i1_fused_0 * T.int64(8) + i0_i1_fused_1) % T.int64(4))
compute[i0, i1] = T.exp(rxplaceholder[i0, i1], dtype="float32")
@R.function
def main(x: R.Tensor((2, 4), dtype="float32")) -> R.Tensor((2,4), dtype="float32"):
# force_pure is expected because purity checking should be disabled before this pass
R.func_attr({"relax.force_pure": True})
cls = Before
storage: R.Any = R.memory.alloc_storage(R.shape([32]), 0, "global", "float32")
alloc: R.Tensor((2, 4), dtype="float32") = R.memory.alloc_tensor(storage, 0, R.shape([2, 4]), "float32")
_1: R.Tuple = cls.exp(x, alloc)
storage1: R.Any = R.memory.alloc_storage(R.shape([32]), 0, "global", "float32")
alloc1: R.Tensor((2, 4), dtype="float32") = R.memory.alloc_tensor(storage1, 0, R.shape([2, 4]), "float32")
_2: R.Tuple = cls.exp(alloc, alloc1)
_3: R.Tuple = R.memory.kill_tensor(alloc)
alloc2: R.Tensor((2, 4), dtype="float32") = R.memory.alloc_tensor(storage, 0, R.shape([2, 4]), "float32")
_4: R.Tuple = cls.exp(alloc1, alloc2)
_5: R.Tuple = R.memory.kill_tensor(alloc1)
storage2: R.Any = R.memory.alloc_storage(R.shape([32]), 0, "global", "float32")
alloc3: R.Tensor((2, 4), dtype="float32") = R.memory.alloc_tensor(storage2, 0, R.shape([2, 4]), "float32")
_6: R.Tuple = cls.exp(alloc2, alloc3)
_7: R.Tuple = R.memory.kill_tensor(alloc2)
alloc4: R.Tensor((2, 4), dtype="float32") = R.builtin.alloc_tensor(R.shape([2, 4]), "float32", 0)
_8 = cls.exp(alloc3, alloc4)
_9: R.Tuple = R.memory.kill_tensor(alloc3)
_10: R.Tuple = R.memory.kill_storage(storage)
_11: R.Tuple = R.memory.kill_storage(storage1)
_12: R.Tuple = R.memory.kill_storage(storage2)
return alloc4
@I.ir_module(s_tir=True)
class Expected:
@T.prim_func(s_tir=True)
def exp(rxplaceholder: T.Buffer((T.int64(2), T.int64(4)), "float32"), compute: T.Buffer((T.int64(2), T.int64(4)), "float32")):
# function attr dict
T.func_attr({"tirx.noalias": True, "global_symbol": "exp"})
# body
# with T.sblock("root")
for i0_i1_fused_0 in T.thread_binding(T.int64(1), thread="blockIdx.x"):
for i0_i1_fused_1 in T.thread_binding(T.int64(8), thread="threadIdx.x"):
with T.sblock("compute"):
i0 = T.axis.spatial(T.int64(2), (i0_i1_fused_0 * T.int64(8) + i0_i1_fused_1) // T.int64(4))
i1 = T.axis.spatial(T.int64(4), (i0_i1_fused_0 * T.int64(8) + i0_i1_fused_1) % T.int64(4))
T.reads(rxplaceholder[i0, i1])
T.writes(compute[i0, i1])
compute[i0, i1] = T.exp(rxplaceholder[i0, i1], dtype="float32")
@R.function(private=True)
def cuda_graph_alloc() -> R.Tuple(R.Any, R.Any, R.Any):
R.func_attr({"relax.force_pure": True})
storage: R.Any = R.memory.alloc_storage(R.shape([32]), R.prim_value(0), R.str("global"), R.dtype("float32"))
storage1: R.Any = R.memory.alloc_storage(R.shape([32]), R.prim_value(0), R.str("global"), R.dtype("float32"))
storage2: R.Any = R.memory.alloc_storage(R.shape([32]), R.prim_value(0), R.str("global"), R.dtype("float32"))
gv: R.Tuple(R.Any, R.Any, R.Any) = (storage, storage1, storage2)
return gv
@R.function(private=True)
def main_cuda_graph_capture(alloc: R.Tensor((2, 4), dtype="float32"), alloc1: R.Tensor((2, 4), dtype="float32"), storage: R.Any, storage2: R.Any) -> R.Tuple(R.Tensor((2, 4), dtype="float32")):
R.func_attr({"relax.force_pure": True})
cls = Expected
_2: R.Tuple = cls.exp(alloc, alloc1)
_3: R.Tuple = R.memory.kill_tensor(alloc)
alloc2: R.Tensor((2, 4), dtype="float32") = R.memory.alloc_tensor(storage, R.prim_value(0), R.shape([2, 4]), R.dtype("float32"))
_4: R.Tuple = cls.exp(alloc1, alloc2)
_5: R.Tuple = R.memory.kill_tensor(alloc1)
alloc3: R.Tensor((2, 4), dtype="float32") = R.memory.alloc_tensor(storage2, 0, R.shape([2, 4]), "float32")
_6: R.Tuple = cls.exp(alloc2, alloc3)
_7: R.Tuple = R.memory.kill_tensor(alloc2)
gv: R.Tuple(R.Tensor((2, 4), dtype="float32")) = (alloc3,)
return gv
@R.function
def main(x: R.Tensor((2, 4), dtype="float32")) -> R.Tensor((2,4), dtype="float32"):
# this comes after RemovePurityChecking, so we expect purity to be forced
R.func_attr({"relax.force_pure": True})
cls = Expected
gv: R.Tuple(R.Any, R.Any, R.Any) = R.call_builtin_with_ctx("vm.builtin.cuda_graph.get_cached_alloc", (cls.cuda_graph_alloc, R.prim_value(0)), ty_args=(R.Tuple(R.Any, R.Any, R.Any),))
storage: R.Any = gv[0]
alloc: R.Tensor((2, 4), dtype="float32") = R.memory.alloc_tensor(storage, R.prim_value(0), R.shape([2, 4]), R.dtype("float32"))
_1: R.Tuple = cls.exp(x, alloc)
storage1: R.Any = gv[1]
alloc1: R.Tensor((2, 4), dtype="float32") = R.memory.alloc_tensor(storage1, R.prim_value(0), R.shape([2, 4]), R.dtype("float32"))
storage2: R.Any = gv[2]
gv1: R.Tuple(R.Tensor((2, 4), dtype="float32")) = R.call_builtin_with_ctx("vm.builtin.cuda_graph.run_or_capture", (cls.main_cuda_graph_capture, (alloc, alloc1, storage, storage2), R.prim_value(0)), ty_args=(R.Tuple(R.Tensor((2, 4), dtype="float32")),))
alloc3: R.Tensor((2, 4), dtype="float32") = gv1[0]
alloc4: R.Tensor((2, 4), dtype="float32") = R.builtin.alloc_tensor(R.shape([2, 4]), R.dtype("float32"), R.prim_value(0))
_6: R.Tuple = cls.exp(alloc3, alloc4)
_7: R.Tuple = R.memory.kill_tensor(alloc3)
_8: R.Tuple = R.memory.kill_storage(storage)
_9: R.Tuple = R.memory.kill_storage(storage1)
_10: R.Tuple = R.memory.kill_storage(storage2)
return alloc4
# fmt: on
after = relax.transform.RewriteCUDAGraph()(Before)
tvm.ir.assert_structural_equal(after, Expected)
def test_tuple():
# fmt: off
@I.ir_module(s_tir=True)
class Before:
@T.prim_func(s_tir=True)
def exp(rxplaceholder: T.Buffer((T.int64(2), T.int64(4)), "float32"), compute: T.Buffer((T.int64(2), T.int64(4)), "float32")):
# function attr dict
T.func_attr({"tirx.noalias": True, "global_symbol": "exp"})
# body
# with T.sblock("root")
for i0_i1_fused_0 in T.thread_binding(T.int64(1), thread="blockIdx.x"):
for i0_i1_fused_1 in T.thread_binding(T.int64(8), thread="threadIdx.x"):
with T.sblock("compute"):
i0 = T.axis.spatial(T.int64(2), (i0_i1_fused_0 * T.int64(8) + i0_i1_fused_1) // T.int64(4))
i1 = T.axis.spatial(T.int64(4), (i0_i1_fused_0 * T.int64(8) + i0_i1_fused_1) % T.int64(4))
T.reads(rxplaceholder[i0, i1])
T.writes(compute[i0, i1])
compute[i0, i1] = T.exp(rxplaceholder[i0, i1], dtype="float32")
@R.function
def main(x: R.Tensor((2, 4), dtype="float32")) -> R.Tensor((2, 4), dtype="float32"):
R.func_attr({"relax.force_pure": True})
cls = Before
storage: R.Any = R.memory.alloc_storage(R.shape([32]), 0, "global", "float32")
alloc: R.Tensor((2, 4), dtype="float32") = R.memory.alloc_tensor(storage, 0, R.shape([2, 4]), "float32")
_: R.Tuple = cls.exp(x, alloc)
storage1: R.Any = R.memory.alloc_storage(R.shape([32]), 0, "global", "float32")
alloc1: R.Tensor((2, 4), dtype="float32") = R.memory.alloc_tensor(storage1, 0, R.shape([2, 4]), "float32")
_: R.Tuple = cls.exp(alloc, alloc1)
lv0 = (alloc1,)
lv1 = (lv0,)
lv2 = lv1[0]
lv3 = lv2[0]
alloc2: R.Tensor((2, 4), dtype="float32") = R.memory.alloc_tensor(storage, 0, R.shape([2, 4]), "float32")
_1: R.Tuple = cls.exp(lv3, alloc2)
_2: R.Tuple = R.memory.kill_tensor(alloc)
_3: R.Tuple = R.memory.kill_tensor(alloc1)
alloc3: R.Tensor((2, 4), dtype="float32") = R.builtin.alloc_tensor(R.shape([2, 4]), R.dtype("float32"), R.prim_value(0))
_4: R.Tuple = cls.exp(alloc2, alloc3)
_5: R.Tuple = R.memory.kill_tensor(alloc2)
_6: R.Tuple = R.memory.kill_storage(storage)
_7: R.Tuple = R.memory.kill_storage(storage1)
return alloc3
@I.ir_module(s_tir=True)
class Expected:
@T.prim_func(s_tir=True)
def exp(rxplaceholder: T.Buffer((T.int64(2), T.int64(4)), "float32"), compute: T.Buffer((T.int64(2), T.int64(4)), "float32")):
T.func_attr({"global_symbol": "exp", "tirx.noalias": True})
# with T.sblock("root"):
for i0_i1_fused_0 in T.thread_binding(T.int64(1), thread="blockIdx.x"):
for i0_i1_fused_1 in T.thread_binding(T.int64(8), thread="threadIdx.x"):
with T.sblock("compute"):
i0 = T.axis.spatial(T.int64(2), (i0_i1_fused_0 * T.int64(8) + i0_i1_fused_1) // T.int64(4))
i1 = T.axis.spatial(T.int64(4), (i0_i1_fused_0 * T.int64(8) + i0_i1_fused_1) % T.int64(4))
T.reads(rxplaceholder[i0, i1])
T.writes(compute[i0, i1])
compute[i0, i1] = T.exp(rxplaceholder[i0, i1])
@R.function(private=True)
def cuda_graph_alloc() -> R.Tuple(R.Any, R.Any):
R.func_attr({"relax.force_pure": True})
storage: R.Any = R.memory.alloc_storage(R.shape([32]), R.prim_value(0), R.str("global"), R.dtype("float32"))
storage1: R.Any = R.memory.alloc_storage(R.shape([32]), R.prim_value(0), R.str("global"), R.dtype("float32"))
gv: R.Tuple(R.Any, R.Any) = (storage, storage1)
return gv
@R.function(private=True)
def main_cuda_graph_capture(alloc: R.Tensor((2, 4), dtype="float32"), alloc1: R.Tensor((2, 4), dtype="float32"), storage: R.Any) -> R.Tuple(R.Tensor((2, 4), dtype="float32")):
R.func_attr({"relax.force_pure": True})
cls = Expected
_: R.Tuple = cls.exp(alloc, alloc1)
lv0: R.Tuple(R.Tensor((2, 4), dtype="float32")) = (alloc1,)
lv1: R.Tuple(R.Tuple(R.Tensor((2, 4), dtype="float32"))) = (lv0,)
lv2: R.Tuple(R.Tensor((2, 4), dtype="float32")) = lv1[0]
lv3: R.Tensor((2, 4), dtype="float32") = lv2[0]
alloc2: R.Tensor((2, 4), dtype="float32") = R.memory.alloc_tensor(storage, R.prim_value(0), R.shape([2, 4]), R.dtype("float32"))
_1: R.Tuple = cls.exp(lv3, alloc2)
_2: R.Tuple = R.memory.kill_tensor(alloc)
_3: R.Tuple = R.memory.kill_tensor(alloc1)
gv: R.Tuple(R.Tensor((2, 4), dtype="float32")) = (alloc2,)
return gv
@R.function
def main(x: R.Tensor((2, 4), dtype="float32")) -> R.Tensor((2, 4), dtype="float32"):
R.func_attr({"relax.force_pure": True})
cls = Expected
gv: R.Tuple(R.Any, R.Any) = R.call_builtin_with_ctx("vm.builtin.cuda_graph.get_cached_alloc", (cls.cuda_graph_alloc, R.prim_value(0)), ty_args=(R.Tuple(R.Any, R.Any),))
storage: R.Any = gv[0]
alloc: R.Tensor((2, 4), dtype="float32") = R.memory.alloc_tensor(storage, R.prim_value(0), R.shape([2, 4]), R.dtype("float32"))
_: R.Tuple = cls.exp(x, alloc)
storage1: R.Any = gv[1]
alloc1: R.Tensor((2, 4), dtype="float32") = R.memory.alloc_tensor(storage1, R.prim_value(0), R.shape([2, 4]), R.dtype("float32"))
gv1: R.Tuple(R.Tensor((2, 4), dtype="float32")) = R.call_builtin_with_ctx("vm.builtin.cuda_graph.run_or_capture", (cls.main_cuda_graph_capture, (alloc, alloc1, storage), R.prim_value(0)), ty_args=(R.Tuple(R.Tensor((2, 4), dtype="float32")),))
alloc2: R.Tensor((2, 4), dtype="float32") = gv1[0]
alloc3: R.Tensor((2, 4), dtype="float32") = R.builtin.alloc_tensor(R.shape([2, 4]), R.dtype("float32"), R.prim_value(0))
_4: R.Tuple = cls.exp(alloc2, alloc3)
_5: R.Tuple = R.memory.kill_tensor(alloc2)
_6: R.Tuple = R.memory.kill_storage(storage)
_7: R.Tuple = R.memory.kill_storage(storage1)
return alloc3
# fmt: on
after = relax.transform.RewriteCUDAGraph()(Before)
tvm.ir.assert_structural_equal(after, Expected)
def test_vm_builtin():
# fmt: off
@I.ir_module(s_tir=True)
class Before:
@T.prim_func(s_tir=True)
def exp(rxplaceholder: T.Buffer((T.int64(2), T.int64(4)), "float32"), compute: T.Buffer((T.int64(2), T.int64(4)), "float32")):
# function attr dict
T.func_attr({"tirx.noalias": True, "global_symbol": "exp"})
for i0_i1_fused_0 in T.thread_binding(T.int64(1), thread="blockIdx.x"):
for i0_i1_fused_1 in T.thread_binding(T.int64(8), thread="threadIdx.x"):
with T.sblock("compute"):
i0 = T.axis.spatial(T.int64(2), (i0_i1_fused_0 * T.int64(8) + i0_i1_fused_1) // T.int64(4))
i1 = T.axis.spatial(T.int64(4), (i0_i1_fused_0 * T.int64(8) + i0_i1_fused_1) % T.int64(4))
compute[i0, i1] = T.exp(rxplaceholder[i0, i1], dtype="float32")
@R.function
def main(x: R.Tensor((2, 4), dtype="float32")) -> R.Tensor((2,4), dtype="float32"):
# force_pure is expected because purity checking should be disabled before this pass
R.func_attr({"relax.force_pure": True})
cls = Before
storage: R.Any = R.memory.alloc_storage(R.shape([32]), 0, "global", "float32")
alloc: R.Tensor((2, 4), dtype="float32") = R.memory.alloc_tensor(storage, 0, R.shape([2, 4]), "float32")
_1: R.Tuple = cls.exp(x, alloc)
storage1: R.Any = R.memory.alloc_storage(R.shape([32]), 0, "global", "float32")
alloc1: R.Tensor((2, 4), dtype="float32") = R.memory.alloc_tensor(storage1, 0, R.shape([2, 4]), "float32")
_2: R.Tuple = cls.exp(alloc, alloc1)
_3: R.Tuple = R.memory.kill_tensor(alloc)
alloc2: R.Tensor((2, 4), dtype="float32") = R.memory.alloc_tensor(storage, 0, R.shape([2, 4]), "float32")
lv: R.Tensor((2, 4), dtype="float32") = alloc2
_4: R.Tuple = R.call_packed("vm.builtin.dummy", (x, lv), ty_args=R.Tuple())
_5: R.Tuple = R.memory.kill_tensor(alloc1)
alloc3: R.Tensor((2, 4), dtype="float32") = R.builtin.alloc_tensor(R.shape([2, 4]), "float32", 0)
_6 = cls.exp(alloc2, alloc3)
_7: R.Tuple = R.memory.kill_tensor(alloc2)
_8: R.Tuple = R.memory.kill_storage(storage)
return alloc3
@I.ir_module(s_tir=True)
class Expected:
@T.prim_func(s_tir=True)
def exp(rxplaceholder: T.Buffer((T.int64(2), T.int64(4)), "float32"), compute: T.Buffer((T.int64(2), T.int64(4)), "float32")):
T.func_attr({"global_symbol": "exp", "tirx.noalias": True})
# with T.sblock("root"):
for i0_i1_fused_0 in T.thread_binding(T.int64(1), thread="blockIdx.x"):
for i0_i1_fused_1 in T.thread_binding(T.int64(8), thread="threadIdx.x"):
with T.sblock("compute"):
i0 = T.axis.spatial(T.int64(2), (i0_i1_fused_0 * T.int64(8) + i0_i1_fused_1) // T.int64(4))
i1 = T.axis.spatial(T.int64(4), (i0_i1_fused_0 * T.int64(8) + i0_i1_fused_1) % T.int64(4))
T.reads(rxplaceholder[i0, i1])
T.writes(compute[i0, i1])
compute[i0, i1] = T.exp(rxplaceholder[i0, i1])
@R.function(private=True)
def cuda_graph_alloc() -> R.Tuple(R.Any, R.Any):
R.func_attr({"relax.force_pure": True})
storage: R.Any = R.memory.alloc_storage(R.shape([32]), R.prim_value(0), R.str("global"), R.dtype("float32"))
storage1: R.Any = R.memory.alloc_storage(R.shape([32]), R.prim_value(0), R.str("global"), R.dtype("float32"))
gv: R.Tuple(R.Any, R.Any) = (storage, storage1)
return gv
@R.function(private=True)
def main_cuda_graph_capture(alloc: R.Tensor((2, 4), dtype="float32"), alloc1: R.Tensor((2, 4), dtype="float32"), storage: R.Any) -> R.Tuple(R.Tensor((2, 4), dtype="float32"), R.Tensor((2, 4), dtype="float32")):
R.func_attr({"relax.force_pure": True})
cls = Expected
_2: R.Tuple = cls.exp(alloc, alloc1)
_3: R.Tuple = R.memory.kill_tensor(alloc)
alloc2: R.Tensor((2, 4), dtype="float32") = R.memory.alloc_tensor(storage, R.prim_value(0), R.shape([2, 4]), R.dtype("float32"))
lv: R.Tensor((2, 4), dtype="float32") = alloc2
gv: R.Tuple(R.Tensor((2, 4), dtype="float32"), R.Tensor((2, 4), dtype="float32")) = (lv, alloc2)
return gv
@R.function
def main(x: R.Tensor((2, 4), dtype="float32")) -> R.Tensor((2,4), dtype="float32"):
R.func_attr({"relax.force_pure": True})
cls = Expected
gv: R.Tuple(R.Any, R.Any) = R.call_builtin_with_ctx("vm.builtin.cuda_graph.get_cached_alloc", (cls.cuda_graph_alloc, R.prim_value(0)), ty_args=(R.Tuple(R.Any, R.Any),))
storage: R.Any = gv[0]
alloc: R.Tensor((2, 4), dtype="float32") = R.memory.alloc_tensor(storage, R.prim_value(0), R.shape([2, 4]), R.dtype("float32"))
_1: R.Tuple = cls.exp(x, alloc)
storage1: R.Any = gv[1]
alloc1: R.Tensor((2, 4), dtype="float32") = R.memory.alloc_tensor(storage1, R.prim_value(0), R.shape([2, 4]), R.dtype("float32"))
gv1: R.Tuple(R.Tensor((2, 4), dtype="float32"), R.Tensor((2, 4), dtype="float32")) = R.call_builtin_with_ctx("vm.builtin.cuda_graph.run_or_capture", (cls.main_cuda_graph_capture, (alloc, alloc1, storage), R.prim_value(0)), ty_args=(R.Tuple(R.Tensor((2, 4), dtype="float32"), R.Tensor((2, 4), dtype="float32")),))
alloc2: R.Tensor((2, 4), dtype="float32") = gv1[1]
lv: R.Tensor((2, 4), dtype="float32") = gv1[0]
_4: R.Tuple = R.call_packed("vm.builtin.dummy", (x, lv), ty_args=(R.Tuple,))
_5: R.Tuple = R.memory.kill_tensor(alloc1)
alloc3: R.Tensor((2, 4), dtype="float32") = R.builtin.alloc_tensor(R.shape([2, 4]), R.dtype("float32"), R.prim_value(0))
_6: R.Tuple = cls.exp(alloc2, alloc3)
_7: R.Tuple = R.memory.kill_tensor(alloc2)
_8: R.Tuple = R.memory.kill_storage(storage)
return alloc3
# fmt: on
after = relax.transform.RewriteCUDAGraph()(Before)
tvm.ir.assert_structural_equal(after, Expected)
def test_capture_fixed_inputs():
@tvm.script.ir_module
class Conv2dx3:
@R.function
def main(
data: R.Tensor((16, 32, 32, 16), "float16"),
weight1: R.Tensor((16, 3, 3, 16), "float16"),
weight2: R.Tensor((16, 3, 3, 16), "float16"),
weight3: R.Tensor((16, 3, 3, 16), "float16"),
gamma: R.Tensor((16,), "float16"),
beta: R.Tensor((16,), "float16"),
):
R.func_attr({"num_input": 1})
with R.dataflow():
conv1 = R.nn.relu(
R.nn.conv2d(
data, weight1, padding=(1, 1), data_layout="NHWC", kernel_layout="OHWI"
)
)
###############################################################################
# The second conv2d and layer norm can be captured into a graph
conv2 = R.nn.relu(
R.nn.conv2d(
conv1, weight2, padding=(1, 1), data_layout="NHWC", kernel_layout="OHWI"
)
)
ln = R.nn.layer_norm(conv2, gamma, beta, axes=[-1])
###############################################################################
conv3 = R.nn.relu(
R.nn.conv2d(
ln, weight3, padding=(1, 1), data_layout="NHWC", kernel_layout="OHWI"
)
)
R.output(conv3)
return conv3
@I.ir_module(s_tir=True)
class Expected:
@T.prim_func(s_tir=True)
def fused_conv2d_relu(
data: T.Buffer((T.int64(16), T.int64(32), T.int64(32), T.int64(16)), "float16"),
weight1: T.Buffer((T.int64(16), T.int64(3), T.int64(3), T.int64(16)), "float16"),
var_compute_intermediate: T.Buffer(
(T.int64(16), T.int64(32), T.int64(32), T.int64(16)), "float16"
),
):
T.func_attr({"tirx.noalias": True})
# with T.sblock("root"):
pad_temp = T.sblock_alloc_buffer(
(T.int64(16), T.int64(34), T.int64(34), T.int64(16)), "float16"
)
var_conv2d_nhwc_intermediate = T.sblock_alloc_buffer(
(T.int64(16), T.int64(32), T.int64(32), T.int64(16)), "float16"
)
for i0, i1, i2, i3 in T.grid(T.int64(16), T.int64(34), T.int64(34), T.int64(16)):
with T.sblock("pad_temp"):
v_i0, v_i1, v_i2, v_i3 = T.axis.remap("SSSS", [i0, i1, i2, i3])
T.reads(data[v_i0, v_i1 - T.int64(1), v_i2 - T.int64(1), v_i3])
T.writes(pad_temp[v_i0, v_i1, v_i2, v_i3])
pad_temp[v_i0, v_i1, v_i2, v_i3] = T.if_then_else(
T.int64(1) <= v_i1
and v_i1 < T.int64(33)
and T.int64(1) <= v_i2
and v_i2 < T.int64(33),
data[v_i0, v_i1 - T.int64(1), v_i2 - T.int64(1), v_i3],
T.float16(0),
)
for nn, yy, xx, ff, ry, rx, rc in T.grid(
T.int64(16),
T.int64(32),
T.int64(32),
T.int64(16),
T.int64(3),
T.int64(3),
T.int64(16),
):
with T.sblock("conv2d_nhwc"):
v_nn, v_yy, v_xx, v_ff, v_ry, v_rx, v_rc = T.axis.remap(
"SSSSRRR", [nn, yy, xx, ff, ry, rx, rc]
)
T.reads(
pad_temp[v_nn, v_yy + v_ry, v_xx + v_rx, v_rc],
weight1[v_ff, v_ry, v_rx, v_rc],
)
T.writes(var_conv2d_nhwc_intermediate[v_nn, v_yy, v_xx, v_ff])
with T.init():
var_conv2d_nhwc_intermediate[v_nn, v_yy, v_xx, v_ff] = T.float16(0)
var_conv2d_nhwc_intermediate[v_nn, v_yy, v_xx, v_ff] = (
var_conv2d_nhwc_intermediate[v_nn, v_yy, v_xx, v_ff]
+ pad_temp[v_nn, v_yy + v_ry, v_xx + v_rx, v_rc]
* weight1[v_ff, v_ry, v_rx, v_rc]
)
for i0, i1, i2, i3 in T.grid(T.int64(16), T.int64(32), T.int64(32), T.int64(16)):
with T.sblock("compute"):
v_i0, v_i1, v_i2, v_i3 = T.axis.remap("SSSS", [i0, i1, i2, i3])
T.reads(var_conv2d_nhwc_intermediate[v_i0, v_i1, v_i2, v_i3])
T.writes(var_compute_intermediate[v_i0, v_i1, v_i2, v_i3])
var_compute_intermediate[v_i0, v_i1, v_i2, v_i3] = T.max(
var_conv2d_nhwc_intermediate[v_i0, v_i1, v_i2, v_i3], T.float16(0)
)
@T.prim_func(s_tir=True)
def layer_norm(
A: T.Buffer((T.int64(16), T.int64(32), T.int64(32), T.int64(16)), "float16"),
B: T.Buffer((T.int64(16),), "float16"),
C: T.Buffer((T.int64(16),), "float16"),
T_layer_norm: T.Buffer((T.int64(16), T.int64(32), T.int64(32), T.int64(16)), "float16"),
):
T.func_attr({"op_pattern": 4, "tirx.noalias": True})
# with T.sblock("root"):
A_red_temp_v0 = T.sblock_alloc_buffer((T.int64(16), T.int64(32), T.int64(32)))
A_red_temp_v1 = T.sblock_alloc_buffer((T.int64(16), T.int64(32), T.int64(32)))
for ax0, ax1, ax2, k3 in T.grid(T.int64(16), T.int64(32), T.int64(32), T.int64(16)):
with T.sblock("A_red_temp"):
v_ax0, v_ax1, v_ax2, v_k3 = T.axis.remap("SSSR", [ax0, ax1, ax2, k3])
T.reads(A[v_ax0, v_ax1, v_ax2, v_k3])
T.writes(A_red_temp_v0[v_ax0, v_ax1, v_ax2], A_red_temp_v1[v_ax0, v_ax1, v_ax2])
with T.init():
A_red_temp_v0[v_ax0, v_ax1, v_ax2] = T.float32(0)
A_red_temp_v1[v_ax0, v_ax1, v_ax2] = T.float32(0)
v_A_red_temp_v0: T.let[T.float32] = A_red_temp_v0[v_ax0, v_ax1, v_ax2] + T.Cast(
"float32", A[v_ax0, v_ax1, v_ax2, v_k3]
)
v_A_red_temp_v1: T.let[T.float32] = A_red_temp_v1[v_ax0, v_ax1, v_ax2] + T.Cast(
"float32", A[v_ax0, v_ax1, v_ax2, v_k3]
) * T.Cast("float32", A[v_ax0, v_ax1, v_ax2, v_k3])
A_red_temp_v0[v_ax0, v_ax1, v_ax2] = v_A_red_temp_v0
A_red_temp_v1[v_ax0, v_ax1, v_ax2] = v_A_red_temp_v1
for ax0, ax1, ax2, ax3 in T.grid(T.int64(16), T.int64(32), T.int64(32), T.int64(16)):
with T.sblock("T_layer_norm"):
v_ax0, v_ax1, v_ax2, v_ax3 = T.axis.remap("SSSS", [ax0, ax1, ax2, ax3])
T.reads(
A[v_ax0, v_ax1, v_ax2, v_ax3],
A_red_temp_v0[v_ax0, v_ax1, v_ax2],
A_red_temp_v1[v_ax0, v_ax1, v_ax2],
B[v_ax3],
C[v_ax3],
)
T.writes(T_layer_norm[v_ax0, v_ax1, v_ax2, v_ax3])
T_layer_norm[v_ax0, v_ax1, v_ax2, v_ax3] = (
T.Cast(
"float16",
(
T.Cast("float32", A[v_ax0, v_ax1, v_ax2, v_ax3])
- A_red_temp_v0[v_ax0, v_ax1, v_ax2] * T.float32(0.0625)
)
* T.rsqrt(
A_red_temp_v1[v_ax0, v_ax1, v_ax2] * T.float32(0.0625)
- A_red_temp_v0[v_ax0, v_ax1, v_ax2]
* T.float32(0.0625)
* (A_red_temp_v0[v_ax0, v_ax1, v_ax2] * T.float32(0.0625))
+ T.float32(1.0000000000000001e-05)
),
)
* B[v_ax3]
+ C[v_ax3]
)
@R.function(private=True)
def cuda_graph_alloc() -> R.Tuple(R.Any, R.Any):
R.func_attr({"relax.force_pure": True})
storage: R.Any = R.memory.alloc_storage(
R.shape([524288]), R.prim_value(0), R.str("global"), R.dtype("float16")
)
storage1: R.Any = R.memory.alloc_storage(
R.shape([524288]), R.prim_value(0), R.str("global"), R.dtype("float16")
)
gv: R.Tuple(R.Any, R.Any) = storage, storage1
return gv
@R.function(private=True)
def main_cuda_graph_capture(
lv: R.Tensor((16, 32, 32, 16), dtype="float16"),
lv1: R.Tensor((16, 3, 3, 16), dtype="float16"),
alloc1: R.Tensor((16, 32, 32, 16), dtype="float16"),
alloc: R.Tensor((16, 32, 32, 16), dtype="float16"),
params: R.Tuple(
R.Tensor((16, 3, 3, 16), dtype="float16"),
R.Tensor((16, 3, 3, 16), dtype="float16"),
R.Tensor((16, 3, 3, 16), dtype="float16"),
R.Tensor((16,), dtype="float16"),
R.Tensor((16,), dtype="float16"),
),
storage: R.Any,
) -> R.Tuple(
R.Tensor((16, 32, 32, 16), dtype="float16"),
R.Tensor((16, 3, 3, 16), dtype="float16"),
R.Tensor((16, 32, 32, 16), dtype="float16"),
):
R.func_attr({"relax.force_pure": True})
cls = Expected
_1: R.Tuple = cls.fused_conv2d_relu(lv, lv1, alloc1)
_: R.Tuple = R.memory.kill_tensor(alloc)
lv1_1: R.Tensor((16, 32, 32, 16), dtype="float16") = alloc1
lv2: R.Tensor((16,), dtype="float16") = params[3]
lv3: R.Tensor((16,), dtype="float16") = params[4]
alloc2: R.Tensor((16, 32, 32, 16), dtype="float16") = R.memory.alloc_tensor(
storage, R.prim_value(0), R.shape([16, 32, 32, 16]), R.dtype("float16")
)
_2: R.Tuple = cls.layer_norm(lv1_1, lv2, lv3, alloc2)
_1_1: R.Tuple = R.memory.kill_tensor(alloc1)
ln: R.Tensor((16, 32, 32, 16), dtype="float16") = alloc2
lv4: R.Tensor((16, 3, 3, 16), dtype="float16") = params[2]
gv: R.Tuple(
R.Tensor((16, 32, 32, 16), dtype="float16"),
R.Tensor((16, 3, 3, 16), dtype="float16"),
R.Tensor((16, 32, 32, 16), dtype="float16"),
) = (ln, lv4, alloc2)
return gv
@R.function
def main_transform_params(
params: R.Tuple(
R.Tensor((16, 3, 3, 16), dtype="float16"),
R.Tensor((16, 3, 3, 16), dtype="float16"),
R.Tensor((16, 3, 3, 16), dtype="float16"),
R.Tensor((16,), dtype="float16"),
R.Tensor((16,), dtype="float16"),
),
) -> R.Tuple(
R.Tensor((16, 3, 3, 16), dtype="float16"),
R.Tensor((16, 3, 3, 16), dtype="float16"),
R.Tensor((16, 3, 3, 16), dtype="float16"),
R.Tensor((16,), dtype="float16"),
R.Tensor((16,), dtype="float16"),
):
R.func_attr({"relax.force_pure": True})
lv: R.Tensor((16, 3, 3, 16), dtype="float16") = params[0]
lv1: R.Tensor((16, 3, 3, 16), dtype="float16") = params[1]
lv2: R.Tensor((16, 3, 3, 16), dtype="float16") = params[2]
lv3: R.Tensor((16,), dtype="float16") = params[3]
lv4: R.Tensor((16,), dtype="float16") = params[4]
gv: R.Tuple(
R.Tensor((16, 3, 3, 16), dtype="float16"),
R.Tensor((16, 3, 3, 16), dtype="float16"),
R.Tensor((16, 3, 3, 16), dtype="float16"),
R.Tensor((16,), dtype="float16"),
R.Tensor((16,), dtype="float16"),
) = (lv, lv1, lv2, lv3, lv4)
return gv
@R.function
def main(
data: R.Tensor((16, 32, 32, 16), dtype="float16"),
params: R.Tuple(
R.Tensor((16, 3, 3, 16), dtype="float16"),
R.Tensor((16, 3, 3, 16), dtype="float16"),
R.Tensor((16, 3, 3, 16), dtype="float16"),
R.Tensor((16,), dtype="float16"),
R.Tensor((16,), dtype="float16"),
),
) -> R.Tensor((16, 32, 32, 16), dtype="float16"):
R.func_attr({"num_input": 1, "relax.force_pure": True})
cls = Expected
lv: R.Tensor((16, 3, 3, 16), dtype="float16") = params[0]
gv: R.Tuple(R.Any, R.Any) = R.call_builtin_with_ctx(
"vm.builtin.cuda_graph.get_cached_alloc",
(cls.cuda_graph_alloc, R.prim_value(0)),
ty_args=(R.Tuple(R.Any, R.Any),),
)
storage: R.Any = gv[0]
alloc: R.Tensor((16, 32, 32, 16), dtype="float16") = R.memory.alloc_tensor(
storage, R.prim_value(0), R.shape([16, 32, 32, 16]), R.dtype("float16")
)
_: R.Tuple = cls.fused_conv2d_relu(data, lv, alloc)
lv_1: R.Tensor((16, 32, 32, 16), dtype="float16") = alloc
lv1: R.Tensor((16, 3, 3, 16), dtype="float16") = params[1]
storage1: R.Any = gv[1]
alloc1: R.Tensor((16, 32, 32, 16), dtype="float16") = R.memory.alloc_tensor(
storage1, R.prim_value(0), R.shape([16, 32, 32, 16]), R.dtype("float16")
)
gv1: R.Tuple(
R.Tensor((16, 32, 32, 16), dtype="float16"),
R.Tensor((16, 3, 3, 16), dtype="float16"),
R.Tensor((16, 32, 32, 16), dtype="float16"),
) = R.call_builtin_with_ctx(
"vm.builtin.cuda_graph.run_or_capture",
(
cls.main_cuda_graph_capture,
(lv_1, lv1, alloc1, alloc, params, storage),
R.prim_value(0),
),
ty_args=(
R.Tuple(
R.Tensor((16, 32, 32, 16), dtype="float16"),
R.Tensor((16, 3, 3, 16), dtype="float16"),
R.Tensor((16, 32, 32, 16), dtype="float16"),
),
),
)
alloc2: R.Tensor((16, 32, 32, 16), dtype="float16") = gv1[2]
ln: R.Tensor((16, 32, 32, 16), dtype="float16") = gv1[0]
lv4: R.Tensor((16, 3, 3, 16), dtype="float16") = gv1[1]
alloc3: R.Tensor((16, 32, 32, 16), dtype="float16") = R.builtin.alloc_tensor(
R.shape([16, 32, 32, 16]), R.dtype("float16"), R.prim_value(0)
)
_3: R.Tuple = cls.fused_conv2d_relu(ln, lv4, alloc3)
_2: R.Tuple = R.memory.kill_tensor(alloc2)
gv_1: R.Tensor((16, 32, 32, 16), dtype="float16") = alloc3
_3_1: R.Tuple = R.memory.kill_storage(storage)
_4: R.Tuple = R.memory.kill_storage(storage1)
return gv_1
mod = tvm.transform.Sequential(
[
relax.pipeline.get_pipeline(),
relax.transform.LiftTransformParams(),
relax.transform.ToNonDataflow(),
relax.transform.RemovePurityChecking(),
relax.transform.CallTIRRewrite(),
relax.transform.StaticPlanBlockMemory(),
]
)(Conv2dx3)
mod["main"] = mod["main"].with_attr({"num_input": 1})
after = relax.transform.RewriteCUDAGraph()(mod)
tvm.ir.assert_structural_equal(after, after)
def test_null_value():
@I.ir_module(s_tir=True)
class Before:
@R.function
def main() -> R.Tuple(R.Any):
_io: R.Any = R.null_value()
lv: R.Tuple(R.Any) = (_io,)
gv: R.Tuple(R.Any) = lv
return gv
Expected = Before
After = relax.transform.RewriteCUDAGraph()(Before)
tvm.ir.assert_structural_equal(After, Expected)
def test_transform_is_no_op_when_disabled():
@I.ir_module(s_tir=True)
class Before:
@R.function
def main():
storage = R.memory.alloc_storage(R.shape([8]), 0, "global", "float32")
alloc3 = R.memory.alloc_tensor(storage, 0, R.shape([8]), "float32")
return R.tuple()
with tvm.transform.PassContext(config={"relax.backend.use_cuda_graph": True}):
AfterWhenEnabled = relax.transform.RewriteCUDAGraph()(Before)
with tvm.transform.PassContext(config={"relax.backend.use_cuda_graph": False}):
AfterWhenDisabled = relax.transform.RewriteCUDAGraph()(Before)
assert not tvm_ffi.structural_equal(Before, AfterWhenEnabled)
tvm.ir.assert_structural_equal(Before, AfterWhenDisabled)
def test_static_args():
@I.ir_module(s_tir=True)
class Before:
@R.function(pure=False)
def main():
storage0 = R.memory.alloc_storage(R.shape([8]), 0, "global", "float32")
alloc0 = R.memory.alloc_tensor(storage0, 0, R.shape([8]), "float32")
_ = R.call_packed("dummy_func", alloc0, R.dtype("float32"), R.str("string"))
return R.tuple()
@I.ir_module(s_tir=True)
class Expected:
@R.function(private=True)
def cuda_graph_alloc() -> R.Tuple(R.Any):
R.func_attr({"relax.force_pure": True})
storage0: R.Any = R.memory.alloc_storage(
R.shape([8]), R.prim_value(0), R.str("global"), R.dtype("float32")
)
gv: R.Tuple(R.Any) = (storage0,)
return gv
@R.function(private=True)
def main_cuda_graph_capture(alloc0: R.Tensor((8,), dtype="float32")) -> R.Tuple:
R.func_attr({"relax.force_pure": True})
_: R.Any = R.call_packed("dummy_func", alloc0, R.dtype("float32"), R.str("string"))
gv: R.Tuple = R.tuple()
return gv
@R.function(pure=False)
def main() -> R.Tuple:
cls = Expected
gv: R.Tuple(R.Any) = R.call_builtin_with_ctx(
"vm.builtin.cuda_graph.get_cached_alloc",
(cls.cuda_graph_alloc, R.prim_value(0)),
ty_args=(R.Tuple(R.Any),),
)
storage0: R.Any = gv[0]
alloc0: R.Tensor((8,), dtype="float32") = R.memory.alloc_tensor(
storage0, R.prim_value(0), R.shape([8]), R.dtype("float32")
)
gv1: R.Tuple = R.call_builtin_with_ctx(
"vm.builtin.cuda_graph.run_or_capture",
(cls.main_cuda_graph_capture, (alloc0,), R.prim_value(0)),
ty_args=(R.Tuple,),
)
return R.tuple()
mod = relax.transform.RewriteCUDAGraph()(Before)
tvm.ir.assert_structural_equal(mod, Expected)
def test_dynamic_capture():
@I.ir_module(s_tir=True)
class Before:
@T.prim_func(s_tir=True)
def add_one(x_handle: T.handle, y_handle: T.handle):
m = T.int64()
x = T.match_buffer(x_handle, (m,), "float32")
y = T.match_buffer(y_handle, (m,), "float32")
# Use T.serial with explicit int64 min so the inner sblock iter_var
# dom is all-int64 (matches what Expected emits via T.axis.spatial(m, i)).
for i in T.serial(T.int64(0), m):
with T.sblock("add"):
vi = T.axis.remap("S", [i])
y[vi] = x[vi] + T.float32(1)
@R.function
def main(x: R.Tensor(("m",), "float32")) -> R.Tensor(("m",), "float32"):
R.func_attr(
{"relax.rewrite_cuda_graph.capture_symbolic_vars": ["m"], "relax.force_pure": True}
)
m = T.int64()
storage: R.Any = R.memory.alloc_storage(
R.shape([16]), 0, "global", "float32"
) # assume m is upper-bounded
alloc1: R.Tensor((m,), "float32") = R.memory.alloc_tensor(
storage, 0, R.shape([m]), "float32"
)
_ = Before.add_one(x, alloc1)
storage1: R.Any = R.memory.alloc_storage(R.shape([16]), 0, "global", "float32")
alloc2: R.Tensor((m,), "float32") = R.memory.alloc_tensor(
storage1, 0, R.shape([m]), "float32"
)
_ = Before.add_one(alloc1, alloc2)
alloc3: R.Tensor((m,), "float32") = R.builtin.alloc_tensor(
R.shape([m]), "float32", 0, "global"
)
_ = Before.add_one(alloc2, alloc3)
return alloc3
@I.ir_module(s_tir=True)
class Expected:
@T.prim_func(s_tir=True)
def add_one(x_handle: T.handle, y_handle: T.handle):
m = T.int64()
x = T.match_buffer(x_handle, (m,))
y = T.match_buffer(y_handle, (m,))
# with T.sblock("root"):
for i in T.serial(T.int64(0), m):
with T.sblock("add"):
vi = T.axis.spatial(m, i)
T.reads(x[vi])
T.writes(y[vi])
y[vi] = x[vi] + T.float32(1)
@R.function(private=True)
def cuda_graph_alloc() -> R.Tuple(R.Any, R.Any):
R.func_attr({"relax.force_pure": True})
storage: R.Any = R.memory.alloc_storage(
R.shape([16]), R.prim_value(0), R.str("global"), R.dtype("float32")
)
storage1: R.Any = R.memory.alloc_storage(
R.shape([16]), R.prim_value(0), R.str("global"), R.dtype("float32")
)
gv: R.Tuple(R.Any, R.Any) = storage, storage1
return gv
@R.function(private=True)
def main_cuda_graph_capture(
alloc1: R.Tensor(("m",), dtype="float32"),
alloc2: R.Tensor(("m",), dtype="float32"),
shape_expr: R.Shape(["m"]),
):
m = T.int64()
R.func_attr({"relax.force_pure": True})
cls = Expected
cls.add_one(alloc1, alloc2)
gv = R.tuple()
return R.tuple()
@R.function
def main(x: R.Tensor(("m",), dtype="float32")) -> R.Tensor(("m",), dtype="float32"):
m = T.int64()
R.func_attr(
{"relax.force_pure": True, "relax.rewrite_cuda_graph.capture_symbolic_vars": ["m"]}
)
cls = Expected
gv: R.Tuple(R.Any, R.Any) = R.call_builtin_with_ctx(
"vm.builtin.cuda_graph.get_cached_alloc",
(cls.cuda_graph_alloc, R.prim_value(0)),
ty_args=(R.Tuple(R.Any, R.Any),),
)
storage: R.Any = gv[0]
alloc1: R.Tensor((m,), dtype="float32") = R.memory.alloc_tensor(
storage, R.prim_value(0), R.shape([m]), R.dtype("float32")
)
cls.add_one(x, alloc1)
storage1: R.Any = gv[1]
alloc2: R.Tensor((m,), dtype="float32") = R.memory.alloc_tensor(
storage1, R.prim_value(0), R.shape([m]), R.dtype("float32")
)
R.call_builtin_with_ctx(
"vm.builtin.cuda_graph.run_or_capture",
(
cls.main_cuda_graph_capture,
(alloc1, alloc2, R.shape([m])),
R.prim_value(0),
R.shape([m]),
),
ty_args=(R.Tuple,),
)
alloc3: R.Tensor((m,), dtype="float32") = R.builtin.alloc_tensor(
R.shape([m]), R.dtype("float32"), R.prim_value(0), R.str("global")
)
cls.add_one(alloc2, alloc3)
return alloc3
mod = relax.transform.RewriteCUDAGraph()(Before)
tvm.ir.assert_structural_equal(mod, Expected)
def test_merge_alloc_funcs():
@I.ir_module(s_tir=True)
class Before:
@R.function
def func1():
R.func_attr({"relax.force_pure": True})
storage1 = R.memory.alloc_storage(R.shape([128]), 0, "global", "float32")
storage2 = R.memory.alloc_storage(R.shape([256]), 0, "global", "float32")
storage3 = R.memory.alloc_storage(R.shape([512]), 0, "ipc_memory", "float32")
alloc1 = R.memory.alloc_tensor(storage1, 0, R.shape([128]), "float32")
alloc2 = R.memory.alloc_tensor(storage2, 0, R.shape([256]), "float32")
alloc3 = R.memory.alloc_tensor(storage3, 0, R.shape([512]), "float32")
R.call_packed("dummy", alloc1, alloc2, alloc3, ty_args=(R.Tuple,))
return R.tuple()
@R.function
def func2():
R.func_attr({"relax.force_pure": True})
storage1 = R.memory.alloc_storage(R.shape([192]), 0, "global", "float32")
storage2 = R.memory.alloc_storage(R.shape([64]), 0, "global", "float32")
storage3 = R.memory.alloc_storage(R.shape([1024]), 0, "ipc_memory", "float32")
storage4 = R.memory.alloc_storage(R.shape([512]), 0, "global", "float32")
alloc1 = R.memory.alloc_tensor(storage1, 0, R.shape([192]), "float32")
alloc2 = R.memory.alloc_tensor(storage2, 0, R.shape([64]), "float32")
alloc3 = R.memory.alloc_tensor(storage3, 0, R.shape([1024]), "float32")
alloc4 = R.memory.alloc_tensor(storage4, 0, R.shape([512]), "float32")
R.call_packed("dummy", alloc1, alloc2, alloc3, alloc4, ty_args=(R.Tuple,))
return R.tuple()
@I.ir_module(s_tir=True)
class Expected:
@R.function(private=True)
def cuda_graph_alloc() -> R.Tuple(R.Any, R.Any, R.Any, R.Any):
R.func_attr({"relax.force_pure": True})
storage4: R.Any = R.memory.alloc_storage(
R.shape([512]), R.prim_value(0), R.str("global"), R.dtype("float32")
)
storage1: R.Any = R.memory.alloc_storage(
R.shape([192]), R.prim_value(0), R.str("global"), R.dtype("float32")
)
storage2: R.Any = R.memory.alloc_storage(
R.shape([64]), R.prim_value(0), R.str("global"), R.dtype("float32")
)
storage3: R.Any = R.memory.alloc_storage(
R.shape([1024]), R.prim_value(0), R.str("ipc_memory"), R.dtype("float32")
)
gv: R.Tuple(R.Any, R.Any, R.Any, R.Any) = (
storage4,
storage1,
storage2,
storage3,
)
return gv
@R.function
def func1() -> R.Tuple:
R.func_attr({"relax.force_pure": True})
cls = Expected
gv: R.Tuple(R.Any, R.Any, R.Any, R.Any) = R.call_builtin_with_ctx(
"vm.builtin.cuda_graph.get_cached_alloc",
(cls.cuda_graph_alloc, R.prim_value(0)),
ty_args=(R.Tuple(R.Any, R.Any, R.Any, R.Any),),
)
storage1: R.Any = gv[1]
storage2: R.Any = gv[0]
storage3: R.Any = gv[3]
alloc1: R.Tensor((128,), dtype="float32") = R.memory.alloc_tensor(
storage1, R.prim_value(0), R.shape([128]), R.dtype("float32")
)
alloc2: R.Tensor((256,), dtype="float32") = R.memory.alloc_tensor(
storage2, R.prim_value(0), R.shape([256]), R.dtype("float32")
)
alloc3: R.Tensor((512,), dtype="float32") = R.memory.alloc_tensor(
storage3, R.prim_value(0), R.shape([512]), R.dtype("float32")
)
R.call_builtin_with_ctx(
"vm.builtin.cuda_graph.run_or_capture",
(cls.func1_cuda_graph_capture, (alloc1, alloc2, alloc3), R.prim_value(0)),
ty_args=(R.Tuple,),
)
return R.tuple()
@R.function(private=True)
def func1_cuda_graph_capture(
alloc1: R.Tensor((128,), dtype="float32"),
alloc2: R.Tensor((256,), dtype="float32"),
alloc3: R.Tensor((512,), dtype="float32"),
) -> R.Tuple:
R.func_attr({"relax.force_pure": True})
R.call_packed("dummy", alloc1, alloc2, alloc3, ty_args=(R.Tuple,))
R.tuple()
return R.tuple()
@R.function
def func2() -> R.Tuple:
R.func_attr({"relax.force_pure": True})
cls = Expected
gv2: R.Tuple(R.Any, R.Any, R.Any, R.Any) = R.call_builtin_with_ctx(
"vm.builtin.cuda_graph.get_cached_alloc",
(cls.cuda_graph_alloc, R.prim_value(0)),
ty_args=(R.Tuple(R.Any, R.Any, R.Any, R.Any),),
)
storage11: R.Any = gv2[1]
storage21: R.Any = gv2[2]
storage31: R.Any = gv2[3]
storage4: R.Any = gv2[0]
alloc1: R.Tensor((192,), dtype="float32") = R.memory.alloc_tensor(
storage11, R.prim_value(0), R.shape([192]), R.dtype("float32")
)
alloc2: R.Tensor((64,), dtype="float32") = R.memory.alloc_tensor(
storage21, R.prim_value(0), R.shape([64]), R.dtype("float32")
)
alloc3: R.Tensor((1024,), dtype="float32") = R.memory.alloc_tensor(
storage31, R.prim_value(0), R.shape([1024]), R.dtype("float32")
)
alloc4: R.Tensor((512,), dtype="float32") = R.memory.alloc_tensor(
storage4, R.prim_value(0), R.shape([512]), R.dtype("float32")
)
R.call_builtin_with_ctx(
"vm.builtin.cuda_graph.run_or_capture",
(cls.func2_cuda_graph_capture, (alloc1, alloc2, alloc3, alloc4), R.prim_value(1)),
ty_args=(R.Tuple,),
)
return R.tuple()
@R.function(private=True)
def func2_cuda_graph_capture(
alloc1: R.Tensor((192,), dtype="float32"),
alloc2: R.Tensor((64,), dtype="float32"),
alloc3: R.Tensor((1024,), dtype="float32"),
alloc4: R.Tensor((512,), dtype="float32"),
) -> R.Tuple:
R.func_attr({"relax.force_pure": True})
R.call_packed("dummy", alloc1, alloc2, alloc3, alloc4, ty_args=(R.Tuple,))
R.tuple()
return R.tuple()
After = relax.transform.RewriteCUDAGraph()(Before)
tvm.ir.assert_structural_equal(After, Expected)
def test_disable_capture_output():
@I.ir_module(s_tir=True)
class Before:
@R.function
def main(x: R.Tensor((8,), "float32")) -> R.Tuple(R.Tensor((8,), "float32")):
R.func_attr({"relax.force_pure": True})
storage1 = R.memory.alloc_storage(R.shape([8]), 0, "global", "float32")
alloc1 = R.memory.alloc_tensor(storage1, 0, R.shape([8]), "float32")
_ = R.call_packed("dummy", x, alloc1, ty_args=(R.Tuple,))
storage2 = R.memory.alloc_storage(R.shape([8]), 0, "global", "float32")
alloc2 = R.memory.alloc_tensor(storage2, 0, R.shape([8]), "float32")
_1 = R.call_packed("dummy", alloc1, alloc2, ty_args=(R.Tuple,))
storage3 = R.memory.alloc_storage(R.shape([8]), 0, "global", "float32")
alloc3 = R.memory.alloc_tensor(storage3, 0, R.shape([8]), "float32")
_2 = R.call_packed("dummy", alloc2, alloc3, ty_args=(R.Tuple,))
gv = (alloc3,)
return gv
@I.ir_module(s_tir=True)
class Expected:
@R.function(private=True)
def cuda_graph_alloc() -> R.Tuple(R.Any, R.Any):
R.func_attr({"relax.force_pure": True})
storage1: R.Any = R.memory.alloc_storage(
R.shape([8]), R.prim_value(0), R.str("global"), R.dtype("float32")
)
storage2: R.Any = R.memory.alloc_storage(
R.shape([8]), R.prim_value(0), R.str("global"), R.dtype("float32")
)
gv: R.Tuple(R.Any, R.Any) = storage1, storage2
return gv
@R.function(private=True)
def main_cuda_graph_capture(
alloc1: R.Tensor((8,), dtype="float32"), alloc2: R.Tensor((8,), dtype="float32")
) -> R.Tuple:
R.func_attr({"relax.force_pure": True})
R.call_packed("dummy", alloc1, alloc2, ty_args=(R.Tuple,))
R.tuple()
return R.tuple()
@R.function
def main(x: R.Tensor((8,), dtype="float32")) -> R.Tuple(R.Tensor((8,), dtype="float32")):
R.func_attr({"relax.force_pure": True})
cls = Expected
gv: R.Tuple(R.Any, R.Any) = R.call_builtin_with_ctx(
"vm.builtin.cuda_graph.get_cached_alloc",
(cls.cuda_graph_alloc, R.prim_value(0)),
ty_args=(R.Tuple(R.Any, R.Any),),
)
storage1: R.Any = gv[0]
alloc1: R.Tensor((8,), dtype="float32") = R.memory.alloc_tensor(
storage1, R.prim_value(0), R.shape([8]), R.dtype("float32")
)
R.call_packed("dummy", x, alloc1, ty_args=(R.Tuple,))
storage2: R.Any = gv[1]
alloc2: R.Tensor((8,), dtype="float32") = R.memory.alloc_tensor(
storage2, R.prim_value(0), R.shape([8]), R.dtype("float32")
)
R.call_builtin_with_ctx(
"vm.builtin.cuda_graph.run_or_capture",
(cls.main_cuda_graph_capture, (alloc1, alloc2), R.prim_value(0)),
ty_args=(R.Tuple,),
)
storage3: R.Any = R.memory.alloc_storage(
R.shape([8]), R.prim_value(0), R.str("global"), R.dtype("float32")
)
alloc3: R.Tensor((8,), dtype="float32") = R.memory.alloc_tensor(
storage3, R.prim_value(0), R.shape([8]), R.dtype("float32")
)
R.call_packed("dummy", alloc2, alloc3, ty_args=(R.Tuple,))
gv = (alloc3,)
return gv
After = relax.transform.RewriteCUDAGraph()(Before)
tvm.ir.assert_structural_equal(After, Expected)
def test_static_input_with_symbolic_shape():
@I.ir_module(s_tir=True)
class Before:
@R.function
def main(x: R.Tensor((8,), "float16"), w: R.Tensor(("m",))):
m = T.int64()
R.func_attr({"relax.force_pure": True, "num_input": 1})
storage1 = R.memory.alloc_storage(R.shape([8]), 0, "global", "float16")
alloc1 = R.memory.alloc_tensor(storage1, 0, R.shape([8]), "float16")
_ = R.call_packed("dummy", x, w, alloc1, ty_args=(R.Tuple,))
storage2 = R.memory.alloc_storage(R.shape([8]), 0, "global", "float16")
alloc2 = R.memory.alloc_tensor(storage2, 0, R.shape([8]), "float16")
_1 = R.call_packed("dummy", alloc1, w, alloc2, ty_args=(R.Tuple,))
storage3 = R.memory.alloc_storage(R.shape([8]), 0, "global", "float16")
alloc3 = R.memory.alloc_tensor(storage3, 0, R.shape([8]), "float16")
_2 = R.call_packed("dummy", alloc2, w, alloc3, ty_args=(R.Tuple,))
gv = (alloc3,)
return gv
@I.ir_module(s_tir=True)
class Expected:
@R.function(private=True)
def cuda_graph_alloc() -> R.Tuple(R.Any, R.Any):
R.func_attr({"relax.force_pure": True})
storage1: R.Any = R.memory.alloc_storage(
R.shape([8]), R.prim_value(0), R.str("global"), R.dtype("float16")
)
storage2: R.Any = R.memory.alloc_storage(
R.shape([8]), R.prim_value(0), R.str("global"), R.dtype("float16")
)
gv: R.Tuple(R.Any, R.Any) = storage1, storage2
return gv
@R.function(private=True)
def main_cuda_graph_capture(
alloc1: R.Tensor((8,), dtype="float16"),
w: R.Tensor(("m",)),
alloc2: R.Tensor((8,), dtype="float16"),
shape_expr: R.Shape(["m"]),
) -> R.Tuple:
m = T.int64()
R.func_attr({"relax.force_pure": True})
R.call_packed("dummy", alloc1, w, alloc2, ty_args=(R.Tuple,))
R.tuple()
return R.tuple()
@R.function
def main(x: R.Tensor((8,), dtype="float16"), w: R.Tensor(("m",))) -> R.Tuple(
R.Tensor((8,), dtype="float16")
):
m = T.int64()
R.func_attr({"num_input": 1, "relax.force_pure": True})
cls = Expected
gv: R.Tuple(R.Any, R.Any) = R.call_builtin_with_ctx(
"vm.builtin.cuda_graph.get_cached_alloc",
(cls.cuda_graph_alloc, R.prim_value(0)),
ty_args=(R.Tuple(R.Any, R.Any),),
)
storage1: R.Any = gv[0]
alloc1: R.Tensor((8,), dtype="float16") = R.memory.alloc_tensor(
storage1, R.prim_value(0), R.shape([8]), R.dtype("float16")
)
R.call_packed("dummy", x, w, alloc1, ty_args=(R.Tuple,))
storage2: R.Any = gv[1]
alloc2: R.Tensor((8,), dtype="float16") = R.memory.alloc_tensor(
storage2, R.prim_value(0), R.shape([8]), R.dtype("float16")
)
R.call_builtin_with_ctx(
"vm.builtin.cuda_graph.run_or_capture",
(
cls.main_cuda_graph_capture,
(alloc1, w, alloc2, R.shape([m])),
R.prim_value(0),
R.shape([m]),
),
ty_args=(R.Tuple,),
)
storage3: R.Any = R.memory.alloc_storage(
R.shape([8]), R.prim_value(0), R.str("global"), R.dtype("float16")
)
alloc3: R.Tensor((8,), dtype="float16") = R.memory.alloc_tensor(
storage3, R.prim_value(0), R.shape([8]), R.dtype("float16")
)
R.call_packed("dummy", alloc2, w, alloc3, ty_args=(R.Tuple,))
gv_1: R.Tuple(R.Tensor((8,), dtype="float16")) = (alloc3,)
return gv_1
After = relax.transform.RewriteCUDAGraph()(Before)
tvm.ir.assert_structural_equal(After, Expected)
if __name__ == "__main__":
tvm.testing.main()