Files
FastDeploy/tests/inter_communicator/test_ipc_signal.py
T
gongweibao edd31e8849 [Feature] Add Deterministic Inference Support (#6476)
* add

* [tests] Add Paddle attention determinism tests and refactor resource manager

Add comprehensive determinism tests for Paddle attention layer and refactor
resource manager for deterministic mode support.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* add

* add

* add

* add

* add more

* add more

* fixsome

* fixsome

* fix bugs

* fix bugs

* only in gpu

* add docs

* fix comments

* fix some

* fix some

* fix comments

* add more

* fix potential problem

* remove not need

* remove not need

* remove no need

* fix bug

* fix bugs

* fix comments

* fix comments

* Update tests/ce/deterministic/test_determinism_verification.py

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update tests/inter_communicator/test_ipc_signal.py

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update tests/layers/test_paddle_attention_determinism.py

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update tests/engine/test_sampling_params_determinism.py

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update tests/layers/test_paddle_attention_determinism.py

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update tests/layers/test_paddle_attention_determinism_standalone.py

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* fix comments

* fix import error

* fix a bug

* fix bugs

* fix bugs

* fix coverage

* refine codes

* refine code

* fix comments

* fix comments

* fix comments

* rm not need

* fix allreduce large tensor bug

* mv log files

* mv log files

* add files

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-02-26 19:31:51 -08:00

346 lines
12 KiB
Python

"""
# Copyright (c) 2025 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 time
import unittest
from multiprocessing.shared_memory import SharedMemory
import numpy as np
import pytest
from fastdeploy.inter_communicator.ipc_signal import IPCSignal, shared_memory_exists
class TestSharedMemoryExists(unittest.TestCase):
"""Test cases for shared_memory_exists function."""
def test_returns_false_for_nonexistent_memory(self):
"""Test that shared_memory_exists returns False for non-existent shared memory."""
result = shared_memory_exists(f"nonexistent_shm_{time.time()}")
self.assertFalse(result)
def test_returns_true_for_existing_memory(self):
"""Test that shared_memory_exists returns True for existing shared memory."""
name = f"test_shm_{time.time()}"
shm = SharedMemory(name=name, create=True, size=1024)
try:
result = shared_memory_exists(name)
self.assertTrue(result)
finally:
try:
shm.close()
shm.unlink()
except Exception:
pass
@pytest.mark.parametrize(
"dtype,shape,initial_value",
[
(np.int32, (10,), [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]),
(np.float32, (5,), [0.0, 1.5, 2.5, 3.5, 4.5]),
(np.int64, (3, 3), [[1, 2, 3], [4, 5, 6], [7, 8, 9]]),
(np.uint8, (4,), [0, 127, 200, 255]),
],
)
def test_ipc_signal_create_with_array(dtype, shape, initial_value):
"""Test IPCSignal creation with numpy array."""
name = f"test_ipc_signal_{time.time()}"
array = np.array(initial_value, dtype=dtype)
signal = IPCSignal(name=name, array=array, dtype=dtype, create=True)
try:
# Verify value is initialized correctly
np.testing.assert_array_equal(signal.value, array)
np.testing.assert_equal(signal.value.dtype, dtype)
# Verify shared memory exists
assert shared_memory_exists(name)
finally:
try:
signal.clear()
except Exception:
pass
class TestIPCSignal(unittest.TestCase):
"""Test cases for IPCSignal class."""
def setUp(self):
"""Set up test fixtures."""
self.test_name_base = f"test_ipc_signal_{time.time()}"
self._signals_to_clean = []
def tearDown(self):
"""Clean up all tracked signals."""
for signal in self._signals_to_clean:
try:
signal.clear()
except Exception:
pass
def _track(self, signal):
"""Register a signal for automatic cleanup in tearDown."""
self._signals_to_clean.append(signal)
return signal
def test_create_with_suffix(self):
"""Test IPCSignal creation with suffix."""
name = self.test_name_base
suffix = 123
array = np.array([1, 2, 3], dtype=np.int32)
signal = self._track(IPCSignal(name=name, array=array, dtype=np.int32, suffix=suffix, create=True))
expected_name = f"{name}.{suffix}"
self.assertTrue(shared_memory_exists(expected_name))
np.testing.assert_array_equal(signal.value, array)
def test_attach_to_existing(self):
"""Test IPCSignal attaching to existing shared memory."""
name = f"{self.test_name_base}_attach"
array = np.array([10, 20, 30], dtype=np.int64)
# Create shared memory
signal1 = self._track(IPCSignal(name=name, array=array, dtype=np.int64, create=True))
signal1.value[0] = 99 # Modify value
# Attach to existing
signal2 = IPCSignal(name=name, array=array, dtype=np.int64, create=False)
# Verify value is shared
self.assertEqual(signal2.value[0], 99)
np.testing.assert_array_equal(signal2.value, signal1.value)
def test_dtype_mismatch_raises_assertion(self):
"""Test that dtype mismatch raises AssertionError."""
name = f"{self.test_name_base}_mismatch"
array = np.array([1, 2, 3], dtype=np.int32)
with self.assertRaises(AssertionError):
IPCSignal(name=name, array=array, dtype=np.float32, create=True)
def test_non_numpy_array_raises_assertion(self):
"""Test that non-numpy array raises AssertionError."""
name = f"{self.test_name_base}_non_array"
with self.assertRaises(AssertionError):
IPCSignal(name=name, array=[1, 2, 3], dtype=np.int32, create=True)
def test_create_with_shm_size(self):
"""Test IPCSignal creation with shm_size (no array)."""
name = f"{self.test_name_base}_size"
signal = self._track(IPCSignal(name=name, shm_size=1024, create=True))
# Verify signal is created but value is None (no array template)
self.assertTrue(shared_memory_exists(name))
self.assertIsNone(signal.value)
def test_attach_with_shm_size(self):
"""Test IPCSignal attach with shm_size (no array)."""
name = f"{self.test_name_base}_attach_size"
# Create
self._track(IPCSignal(name=name, shm_size=512, create=True))
# Attach
signal2 = IPCSignal(name=name, shm_size=512, create=False)
self.assertTrue(shared_memory_exists(name))
self.assertIsNone(signal2.value)
def test_shm_size_required_without_array_and_dtype(self):
"""Test that shm_size is required when array and dtype are None."""
name = f"{self.test_name_base}_no_size"
with self.assertRaises(AssertionError):
IPCSignal(name=name, create=True)
def test_clear_removes_shared_memory(self):
"""Test that clear() properly removes shared memory."""
name = f"{self.test_name_base}_clear"
array = np.array([1, 2, 3], dtype=np.int32)
signal = IPCSignal(name=name, array=array, dtype=np.int32, create=True)
self.assertTrue(shared_memory_exists(name))
signal.clear()
self.assertFalse(shared_memory_exists(name))
def test_clear_idempotent(self):
"""Test that clear() can be called multiple times safely."""
name = f"{self.test_name_base}_idempotent"
array = np.array([1, 2, 3], dtype=np.int32)
signal = IPCSignal(name=name, array=array, dtype=np.int32, create=True)
# Should not raise exception
signal.clear()
signal.clear() # Call again
def test_value_sharing_between_processes_mock(self):
"""Test that value is shared (mocked for unit test)."""
name = f"{self.test_name_base}_shared"
array = np.array([100, 200, 300], dtype=np.int64)
signal1 = self._track(IPCSignal(name=name, array=array, dtype=np.int64, create=True))
signal2 = IPCSignal(name=name, array=array, dtype=np.int64, create=False)
# Modify through signal1
signal1.value[0] = 999
signal1.value[1] = 888
signal1.value[2] = 777
# Verify signal2 sees changes
self.assertEqual(signal2.value[0], 999)
self.assertEqual(signal2.value[1], 888)
self.assertEqual(signal2.value[2], 777)
def test_multiple_array_creation_replaces_existing(self):
"""Test that creating with same name replaces existing shared memory."""
name = f"{self.test_name_base}_replace"
array1 = np.array([1, 2, 3], dtype=np.int32)
array2 = np.array([4, 5, 6], dtype=np.int32)
signal1 = IPCSignal(name=name, array=array1, dtype=np.int32, create=True)
signal1.clear()
signal2 = self._track(IPCSignal(name=name, array=array2, dtype=np.int32, create=True))
np.testing.assert_array_equal(signal2.value, array2)
def test_clear_closes_and_unlinks(self):
"""Test that clear() both closes and unlinks the shared memory."""
name = f"{self.test_name_base}_unlink"
array = np.array([1, 2, 3], dtype=np.int32)
signal = IPCSignal(name=name, array=array, dtype=np.int32, create=True)
# After clear, the shared memory should be removed
signal.clear()
self.assertFalse(shared_memory_exists(name))
# Attempting to attach should fail
try:
_ = SharedMemory(name=name, create=False)
self.fail("Should have raised FileNotFoundError")
except FileNotFoundError:
pass
def test_raw_buffer_read_write_with_shm_size(self):
"""Test raw buffer read/write in shm_size mode."""
name = f"{self.test_name_base}_raw_buf"
data = b"hello ipc signal"
signal1 = self._track(IPCSignal(name=name, shm_size=1024, create=True))
signal1.shm.buf[: len(data)] = data
signal2 = IPCSignal(name=name, shm_size=1024, create=False)
self.assertEqual(bytes(signal2.shm.buf[: len(data)]), data)
def test_create_overwrites_existing_without_clear(self):
"""Test that create=True on existing name auto-unlinks and recreates."""
name = f"{self.test_name_base}_overwrite"
array1 = np.array([1, 2, 3], dtype=np.int32)
array2 = np.array([7, 8, 9], dtype=np.int32)
# Create first signal, do NOT clear it
IPCSignal(name=name, array=array1, dtype=np.int32, create=True)
# Create again with same name — should auto-unlink old and recreate
signal2 = self._track(IPCSignal(name=name, array=array2, dtype=np.int32, create=True))
np.testing.assert_array_equal(signal2.value, array2)
def test_attach_nonexistent_raises_error(self):
"""Test that create=False on non-existent shm raises FileNotFoundError."""
name = f"nonexistent_signal_{time.time()}"
array = np.array([1, 2, 3], dtype=np.int32)
with self.assertRaises(FileNotFoundError):
IPCSignal(name=name, array=array, dtype=np.int32, create=False)
class TestIPCSignalEdgeCases(unittest.TestCase):
"""Test edge cases for IPCSignal."""
def test_empty_array_raises_error(self):
"""Test IPCSignal with empty array raises ValueError due to nbytes=0."""
name = f"test_empty_array_{time.time()}"
array = np.array([], dtype=np.int32)
with self.assertRaises(ValueError):
IPCSignal(name=name, array=array, dtype=np.int32, create=True)
def test_large_array(self):
"""Test IPCSignal with large array."""
name = f"test_large_array_{time.time()}"
size = 10000
array = np.arange(size, dtype=np.int64)
signal = IPCSignal(name=name, array=array, dtype=np.int64, create=True)
try:
np.testing.assert_array_equal(signal.value, array)
finally:
try:
signal.clear()
except Exception:
pass
def test_multidimensional_array(self):
"""Test IPCSignal with multidimensional array."""
name = f"test_multi_array_{time.time()}"
array = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]], dtype=np.int32)
signal = IPCSignal(name=name, array=array, dtype=np.int32, create=True)
try:
self.assertEqual(signal.value.shape, (3, 3))
np.testing.assert_array_equal(signal.value, array)
finally:
try:
signal.clear()
except Exception:
pass
def test_different_numeric_types(self):
"""Test IPCSignal with different numeric types."""
name_base = f"test_types_{time.time()}"
test_cases = [
(np.int8, [1, 2, 3]),
(np.int16, [1000, 2000, 3000]),
(np.int32, [100000, 200000, 300000]),
(np.int64, [1000000000, 2000000000, 3000000000]),
(np.float32, [1.5, 2.5, 3.5]),
(np.float64, [1.123456789, 2.987654321, 3.5]),
]
for i, (dtype, values) in enumerate(test_cases):
name = f"{name_base}_{i}"
array = np.array(values, dtype=dtype)
signal = IPCSignal(name=name, array=array, dtype=dtype, create=True)
try:
np.testing.assert_array_equal(signal.value, array)
finally:
try:
signal.clear()
except Exception:
pass
if __name__ == "__main__":
unittest.main()