Files
2026-07-13 12:40:42 +08:00

401 lines
14 KiB
Python

# Copyright (c) 2023 PaddlePaddle Authors. All Rights Reserved.
#
# Licensed 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.
import unittest
import paddle
from paddle.base import core
def is_custom_device():
custom_dev_types = paddle.device.get_all_custom_device_type()
if custom_dev_types and paddle.device.is_compiled_with_custom_device(
custom_dev_types[0]
):
return True
return False
class TestEventStreamAPIs(unittest.TestCase):
"""Test paddle.device Event and Stream APIs across different hardware types."""
def setUp(self):
"""Set up test environment."""
if not (
core.is_compiled_with_cuda()
or core.is_compiled_with_xpu()
or is_custom_device()
):
self.skipTest("CUDA, XPU or Custom Device not available")
self.cuda_available = core.is_compiled_with_cuda()
self.xpu_available = core.is_compiled_with_xpu()
self.custom_device_available = is_custom_device()
# Get available custom device types
if self.custom_device_available:
self.custom_device_types = core.get_all_custom_device_type()
self.default_custom_device = self.custom_device_types[0]
else:
self.custom_device_types = []
self.default_custom_device = None
self._original_device = paddle.device.get_device()
self._original_stream = paddle.device.current_stream()
def tearDown(self):
"""Clean up after timing functionality test."""
paddle.device.synchronize()
paddle.device.set_device(self._original_device)
try:
paddle.device.set_stream(self._original_stream)
except Exception:
pass
def test_event_stream_apis_cuda(self):
"""Test Event and Stream APIs with CUDA."""
if not core.is_compiled_with_cuda():
self.skipTest("CUDA not available")
self._test_event_stream_apis_impl('gpu:0')
def test_event_stream_apis_customdevice(self):
"""Test Event and Stream APIs with custom device."""
if not is_custom_device():
self.skipTest("Custom device not available")
self._test_event_stream_apis_impl(f'{self.default_custom_device}:0')
def test_event_stream_apis_xpu(self):
"""Test Event and Stream APIs with XPU."""
if not core.is_compiled_with_xpu():
self.skipTest("XPU not available")
self._test_event_stream_apis_impl('xpu:0')
def _test_event_stream_apis_impl(self, device_str):
"""Test Event and Stream APIs implementation."""
# Set device
paddle.device.set_device(device_str)
# Test Event creation with different parameters
event1 = paddle.device.Event()
self.assertIsInstance(event1, paddle.device.Event)
event2 = paddle.device.Event(enable_timing=True)
self.assertIsInstance(event2, paddle.device.Event)
event3 = paddle.device.Event(enable_timing=True, blocking=True)
self.assertIsInstance(event3, paddle.device.Event)
# Test Stream creation with different parameters
stream1 = paddle.device.Stream()
self.assertIsInstance(stream1, paddle.device.Stream)
stream2 = paddle.device.Stream(device=device_str)
self.assertIsInstance(stream2, paddle.device.Stream)
stream3 = paddle.device.Stream(device=device_str, priority=1)
self.assertIsInstance(stream3, paddle.device.Stream)
# Test current_stream
current_stream = paddle.device.current_stream()
self.assertIsInstance(current_stream, paddle.device.Stream)
# Test set_stream
prev_stream = paddle.device.set_stream(stream1)
self.assertIsInstance(prev_stream, paddle.device.Stream)
prev_stream = paddle.cuda.set_stream(stream1)
self.assertIsInstance(prev_stream, paddle.cuda.Stream)
# Test Event.record() with default stream
event1.record()
# Query result may be True immediately for some devices
try:
self.assertFalse(event1.query())
except AssertionError:
pass # Some devices may complete immediately
# Test Event.record() with specific stream
self.assertTrue(event2.query())
# Test Event.synchronize()
event1.synchronize() # Wait for event to complete
self.assertTrue(event1.query()) # Should be completed now
# Test Stream.query()
if not core.is_compiled_with_xpu():
self.assertTrue(
stream1.query()
) # Should be completed (no work submitted)
# Test Stream.synchronize()
stream1.synchronize() # Should not raise exception
# Test Stream.wait_event()
stream2.wait_event(event1)
# Test Stream.wait_stream()
stream2.wait_stream(stream1)
# Test Stream.record_event()
event4 = stream1.record_event()
self.assertIsInstance(event4, paddle.device.Event)
# Test record_event with existing event
stream1.record_event(event3)
# Test Event.elapsed_time()
if hasattr(event1, 'event_base') and hasattr(event2, 'event_base'):
# Create events with timing enabled
start_event = paddle.device.Event(enable_timing=True)
end_event = paddle.device.Event(enable_timing=True)
# Record start event
start_event.record()
# Submit some work to the stream
with paddle.device.stream_guard(stream1):
# Create a tensor to ensure some work is done
tensor = paddle.randn([100, 100], dtype='float32')
result = tensor * 2
# Record end event
end_event.record()
# Synchronize to ensure events are recorded
end_event.synchronize()
# Measure elapsed time
if not core.is_compiled_with_xpu():
elapsed_time = start_event.elapsed_time(end_event)
self.assertIsInstance(elapsed_time, (int, float))
self.assertGreaterEqual(elapsed_time, 0)
# Test stream_guard context manager
with paddle.device.stream_guard(stream1):
# Inside the context, current stream should be stream1
guarded_stream = paddle.device.current_stream()
self.assertEqual(guarded_stream.device, stream1.device)
# Test operations within stream guard
tensor1 = paddle.ones([10, 10])
tensor2 = paddle.ones([10, 10])
result = tensor1 + tensor2
# After exiting context, stream should be restored
restored_stream = paddle.device.current_stream()
self.assertEqual(restored_stream.device, prev_stream.device)
# Test Stream properties and methods
self.assertTrue(hasattr(stream1, 'stream_base'))
self.assertTrue(hasattr(stream1, 'device'))
if not core.is_compiled_with_xpu():
self.assertTrue(callable(stream1.query))
self.assertTrue(callable(stream1.synchronize))
self.assertTrue(callable(stream1.wait_event))
self.assertTrue(callable(stream1.wait_stream))
self.assertTrue(callable(stream1.record_event))
# Test Event properties and methods
self.assertTrue(hasattr(event1, 'event_base'))
self.assertTrue(hasattr(event1, 'device'))
self.assertTrue(callable(event1.record))
self.assertTrue(callable(event1.query))
if not core.is_compiled_with_xpu():
self.assertTrue(callable(event1.elapsed_time))
self.assertTrue(callable(event1.synchronize))
# Test Stream equality and hash
stream_copy = paddle.device.Stream(device=device_str)
self.assertNotEqual(stream1, stream_copy) # Different stream objects
self.assertEqual(
hash(stream1), hash(stream1)
) # Same hash for same object
# Test Stream representation
stream_repr = repr(stream1)
self.assertIn('paddle.device.Stream', stream_repr)
self.assertIn(str(stream1.device), stream_repr)
# Test Event representation
event_repr = repr(event1)
self.assertIsNotNone(event_repr)
# Clean up
paddle.device.synchronize()
def test_event_stream_error_handling(self):
"""Test Event and Stream error handling."""
# Test with invalid device types
with self.assertRaises(TypeError):
paddle.device.Event(device='invalid_device:0')
with self.assertRaises(ValueError):
paddle.device.Stream(device='invalid_device:0')
# Test Event.elapsed_time with incompatible events
if core.is_compiled_with_cuda() or is_custom_device():
device_str = (
'gpu:0'
if core.is_compiled_with_cuda()
else f'{self.default_custom_device}:0'
)
paddle.device.set_device(device_str)
event1 = paddle.device.Event()
event2 = paddle.device.Event()
# Should not raise exception even if events are not recorded
try:
elapsed = event1.elapsed_time(event2)
self.assertIsInstance(elapsed, (int, float))
except Exception:
# Some implementations might raise exception, which is also acceptable
pass
class TestEventStreamTimingFunctionality(unittest.TestCase):
"""Test Event timing functionality with actual work in isolated environment."""
def setUp(self):
"""Set up test environment for timing functionality."""
if not (
core.is_compiled_with_cuda()
or core.is_compiled_with_xpu()
or is_custom_device()
):
self.skipTest("CUDA, XPU or Custom Device not available")
self.cuda_available = core.is_compiled_with_cuda()
self.custom_device_available = is_custom_device()
# Get available custom device types
if self.custom_device_available:
self.custom_device_types = core.get_all_custom_device_type()
self.default_custom_device = self.custom_device_types[0]
else:
self.custom_device_types = []
self.default_custom_device = None
self._original_device = paddle.device.get_device()
self._original_stream = paddle.device.current_stream()
def tearDown(self):
"""Clean up after timing functionality test."""
paddle.device.synchronize()
paddle.device.set_device(self._original_device)
try:
paddle.device.set_stream(self._original_stream)
except Exception:
pass
def test_event_stream_timing_functionality(self):
"""Test Event timing functionality with actual work."""
if not (self.cuda_available or self.custom_device_available):
self.skipTest(
"Timing functionality test requires CUDA or custom device"
)
device_str = (
'gpu:0'
if self.cuda_available
else f'{self.default_custom_device}:0'
)
paddle.device.set_device(device_str)
# Create events with timing enabled
start_event = paddle.device.Event(enable_timing=True)
end_event = paddle.device.Event(enable_timing=True)
# Create a stream for work execution
stream = paddle.device.Stream(device=device_str)
# Record start event
start_event.record(stream)
# Perform some work on the stream
with paddle.device.stream_guard(stream):
# Create and perform operations on tensors
x = paddle.randn([1000, 1000], dtype='float32')
y = paddle.randn([1000, 1000], dtype='float32')
# Matrix multiplication - computationally intensive
z = paddle.matmul(x, y)
# Ensure the operation is executed
z_mean = z.mean()
# Record end event
end_event.record(stream)
# Wait for the end event to complete
end_event.synchronize()
if not core.is_compiled_with_xpu():
# Calculate elapsed time
elapsed_time = start_event.elapsed_time(end_event)
# Verify the timing result
self.assertIsInstance(elapsed_time, (int, float))
self.assertGreater(elapsed_time, 0) # Should take some time
class TestEventAPIs(unittest.TestCase):
"""Unified test for paddle.Event, paddle.device.Event, and paddle.cuda.Event."""
def setUp(self):
if not paddle.device.is_compiled_with_cuda():
self.skipTest("This test requires CUDA.")
self.device = "gpu:0"
paddle.device.set_device(self.device)
self.event_classes = [
("paddle.Event", paddle.Event),
("paddle.cuda.Event", paddle.cuda.Event),
]
def test_event_timing_consistency(self):
"""Check timing consistency across different Event APIs."""
for name, EventCls in self.event_classes:
with self.subTest(api=name):
start = EventCls(enable_timing=True)
end = EventCls(enable_timing=True)
start.record()
x = paddle.randn([2048, 2048], dtype="float32")
y = paddle.randn([2048, 2048], dtype="float32")
z = paddle.matmul(x, y)
_ = z.mean()
end.record()
end.synchronize()
elapsed = start.elapsed_time(end)
self.assertIsInstance(elapsed, (int, float))
self.assertGreater(
elapsed,
0.0,
f"{name} should measure positive elapsed time.",
)
def test_event_methods_available(self):
"""Ensure all Event variants expose expected methods."""
for name, EventCls in self.event_classes:
with self.subTest(api=name):
e = EventCls(enable_timing=True)
self.assertTrue(hasattr(e, "record"))
self.assertTrue(hasattr(e, "synchronize"))
self.assertTrue(hasattr(e, "elapsed_time"))
if __name__ == '__main__':
unittest.main()