| # 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. |
| |
| from __future__ import annotations |
| |
| import ctypes |
| |
| import pytest |
| import tvm_ffi |
| import tvm_ffi.cpp |
| from tvm_ffi.testing import run_with_gpu_lock |
| |
| try: |
| import torch |
| except ImportError: |
| torch = None # ty: ignore[invalid-assignment] |
| |
| |
| try: |
| from cuda.bindings import driver as cuda_driver |
| except ImportError: |
| cuda_driver = None |
| |
| |
| @pytest.mark.skipif(cuda_driver is None, reason="Requires cuda-python") |
| def test_cuda_driver_stream() -> None: |
| assert cuda_driver is not None |
| echo = tvm_ffi.get_global_func("testing.echo") |
| stream = cuda_driver.CUstream(0) |
| y = echo(stream) |
| assert y is not None |
| z = echo(cuda_driver.CUstream(1)) |
| assert isinstance(z, ctypes.c_void_p) |
| assert z.value == 1 |
| |
| |
| def gen_check_stream_mod() -> tvm_ffi.Module: |
| return tvm_ffi.cpp.load_inline( |
| name="check_stream", |
| cpp_sources=""" |
| void check_stream(int device_type, int device_id, uint64_t stream) { |
| uint64_t cur_stream = reinterpret_cast<uint64_t>(TVMFFIEnvGetStream(device_type, device_id)); |
| TVM_FFI_ICHECK_EQ(cur_stream, stream); |
| } |
| """, |
| functions=["check_stream"], |
| ) |
| |
| |
| def test_raw_stream() -> None: |
| mod = gen_check_stream_mod() |
| device = tvm_ffi.device("cuda:0") |
| stream_1 = 123456789 |
| stream_2 = 987654321 |
| with tvm_ffi.use_raw_stream(device, stream_1): |
| mod.check_stream(device.dlpack_device_type(), device.index, stream_1) |
| assert tvm_ffi.get_raw_stream(device) == stream_1 |
| |
| with tvm_ffi.use_raw_stream(device, stream_2): |
| mod.check_stream(device.dlpack_device_type(), device.index, stream_2) |
| assert tvm_ffi.get_raw_stream(device) == stream_2 |
| |
| mod.check_stream(device.dlpack_device_type(), device.index, stream_1) |
| assert tvm_ffi.get_raw_stream(device) == stream_1 |
| |
| |
| @pytest.mark.skipif( |
| torch is None or not torch.cuda.is_available(), reason="Requires torch and CUDA" |
| ) |
| def test_torch_stream() -> None: |
| assert torch is not None |
| mod = gen_check_stream_mod() |
| |
| def run_and_check() -> None: |
| assert torch is not None |
| device_id = torch.cuda.current_device() |
| device = tvm_ffi.device("cuda", device_id) |
| device_type = device.dlpack_device_type() |
| stream_1 = torch.cuda.Stream(device_id) |
| stream_2 = torch.cuda.Stream(device_id) |
| with tvm_ffi.use_torch_stream(torch.cuda.stream(stream_1)): |
| assert torch.cuda.current_stream() == stream_1 |
| mod.check_stream(device_type, device_id, stream_1.cuda_stream) |
| |
| with tvm_ffi.use_torch_stream(torch.cuda.stream(stream_2)): |
| assert torch.cuda.current_stream() == stream_2 |
| mod.check_stream(device_type, device_id, stream_2.cuda_stream) |
| |
| assert torch.cuda.current_stream() == stream_1 |
| mod.check_stream(device_type, device_id, stream_1.cuda_stream) |
| |
| run_with_gpu_lock(run_and_check) |
| |
| |
| @pytest.mark.skipif( |
| torch is None or not torch.cuda.is_available(), reason="Requires torch and CUDA" |
| ) |
| def test_torch_current_stream() -> None: |
| assert torch is not None |
| mod = gen_check_stream_mod() |
| |
| def run_and_check() -> None: |
| assert torch is not None |
| device_id = torch.cuda.current_device() |
| device = tvm_ffi.device("cuda", device_id) |
| device_type = device.dlpack_device_type() |
| stream_1 = torch.cuda.Stream(device_id) |
| stream_2 = torch.cuda.Stream(device_id) |
| with torch.cuda.stream(stream_1): |
| assert torch.cuda.current_stream() == stream_1 |
| with tvm_ffi.use_torch_stream(): |
| mod.check_stream(device_type, device_id, stream_1.cuda_stream) |
| |
| with torch.cuda.stream(stream_2): |
| assert torch.cuda.current_stream() == stream_2 |
| with tvm_ffi.use_torch_stream(): |
| mod.check_stream(device_type, device_id, stream_2.cuda_stream) |
| |
| assert torch.cuda.current_stream() == stream_1 |
| with tvm_ffi.use_torch_stream(): |
| mod.check_stream(device_type, device_id, stream_1.cuda_stream) |
| |
| run_with_gpu_lock(run_and_check) |
| |
| |
| @pytest.mark.skipif( |
| torch is None or not torch.cuda.is_available(), reason="Requires torch and CUDA" |
| ) |
| def test_torch_graph() -> None: |
| assert torch is not None |
| mod = gen_check_stream_mod() |
| |
| def run_and_check() -> None: |
| assert torch is not None |
| device_id = torch.cuda.current_device() |
| device = tvm_ffi.device("cuda", device_id) |
| device_type = device.dlpack_device_type() |
| graph = torch.cuda.CUDAGraph() |
| stream = torch.cuda.Stream(device_id) |
| x = torch.zeros(1, device="cuda") |
| with tvm_ffi.use_torch_stream(torch.cuda.graph(graph, stream=stream)): |
| assert torch.cuda.current_stream() == stream |
| mod.check_stream(device_type, device_id, stream.cuda_stream) |
| # avoid cuda graph no capture warning |
| x = x + 1 |
| |
| run_with_gpu_lock(run_and_check) |