py-sharedmemory 1.0.1__tar.gz → 2.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {py_sharedmemory-1.0.1 → py_sharedmemory-2.0.0}/LICENSE +1 -1
- {py_sharedmemory-1.0.1/py_sharedmemory.egg-info → py_sharedmemory-2.0.0}/PKG-INFO +7 -5
- {py_sharedmemory-1.0.1 → py_sharedmemory-2.0.0}/README.md +5 -3
- py_sharedmemory-2.0.0/memory/__init__.py +3 -0
- py_sharedmemory-2.0.0/memory/convert.py +53 -0
- py_sharedmemory-2.0.0/memory/factory.py +13 -0
- py_sharedmemory-2.0.0/memory/receiver.py +23 -0
- py_sharedmemory-2.0.0/memory/sender.py +111 -0
- {py_sharedmemory-1.0.1 → py_sharedmemory-2.0.0/py_sharedmemory.egg-info}/PKG-INFO +7 -5
- {py_sharedmemory-1.0.1 → py_sharedmemory-2.0.0}/py_sharedmemory.egg-info/SOURCES.txt +2 -3
- {py_sharedmemory-1.0.1 → py_sharedmemory-2.0.0}/pyproject.toml +2 -2
- py_sharedmemory-1.0.1/memory/__init__.py +0 -1
- py_sharedmemory-1.0.1/memory/converter.py +0 -106
- py_sharedmemory-1.0.1/memory/factory.py +0 -12
- py_sharedmemory-1.0.1/memory/receiver.py +0 -75
- py_sharedmemory-1.0.1/memory/sender.py +0 -149
- py_sharedmemory-1.0.1/tests/test_shared_memory.py +0 -339
- {py_sharedmemory-1.0.1 → py_sharedmemory-2.0.0}/py_sharedmemory.egg-info/dependency_links.txt +0 -0
- {py_sharedmemory-1.0.1 → py_sharedmemory-2.0.0}/py_sharedmemory.egg-info/requires.txt +0 -0
- {py_sharedmemory-1.0.1 → py_sharedmemory-2.0.0}/py_sharedmemory.egg-info/top_level.txt +0 -0
- {py_sharedmemory-1.0.1 → py_sharedmemory-2.0.0}/setup.cfg +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: py-sharedmemory
|
|
3
|
-
Version:
|
|
4
|
-
Summary:
|
|
3
|
+
Version: 2.0.0
|
|
4
|
+
Summary: Optimized Python IPC: Uses shared memory to bypass multiprocessing queue I/O bottlenecks, ideal for large data (1MB+) in scientific computing, RL, etc. Reduces system load and improves latency consistency
|
|
5
5
|
Author: Tobias Würth
|
|
6
6
|
License-Expression: MIT
|
|
7
7
|
Project-URL: Repository, https://github.com/tobiaswuerth/python_shared_memory_queue
|
|
@@ -17,15 +17,17 @@ Dynamic: license-file
|
|
|
17
17
|
|
|
18
18
|
# py-sharedmemory: High-Performance Multiprocessing Queue Alternative for Large Data
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
`py-sharedmemory` offers a high-performance replacement. It bypasses pickling by converting data into bytes and moving them into shared memory. Only minimal metadata is sent via the standard queue mechanism. This significantly reduces serialization overhead and inter-process I/O, drastically improving data transfer throughput for data-intensive Python multiprocessing tasks. If your application struggles with slow `multiprocessing.Queue` performance due to large object transfers, `py-sharedmemory` provides a faster alternative.
|
|
20
|
+
[](https://badge.fury.io/py/py-sharedmemory)
|
|
23
21
|
|
|
24
22
|
Install using:
|
|
25
23
|
```
|
|
26
24
|
pip install py-sharedmemory
|
|
27
25
|
```
|
|
28
26
|
|
|
27
|
+
Python's standard `multiprocessing.Queue` pickles data for inter-process communication. This often creates performance bottlenecks in demanding applications like Deep Reinforcement Learning (DeepRL) training, scientific computing, or distributed systems that handle large data objects (e.g., NumPy arrays, tensors >1MB) or involve high I/O volumes between many processes (like actors, replay buffers, and trainers).
|
|
28
|
+
|
|
29
|
+
`py-sharedmemory` offers a high-performance replacement. It bypasses pickling by converting data into bytes and moving them into shared memory. Only minimal metadata is sent via the standard queue mechanism. This significantly reduces serialization overhead and inter-process I/O, drastically improving data transfer throughput for data-intensive Python multiprocessing tasks. If your application struggles with slow `multiprocessing.Queue` performance due to large object transfers, `py-sharedmemory` provides a faster alternative.
|
|
30
|
+
|
|
29
31
|
---
|
|
30
32
|
|
|
31
33
|
# Usage example
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
# py-sharedmemory: High-Performance Multiprocessing Queue Alternative for Large Data
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
`py-sharedmemory` offers a high-performance replacement. It bypasses pickling by converting data into bytes and moving them into shared memory. Only minimal metadata is sent via the standard queue mechanism. This significantly reduces serialization overhead and inter-process I/O, drastically improving data transfer throughput for data-intensive Python multiprocessing tasks. If your application struggles with slow `multiprocessing.Queue` performance due to large object transfers, `py-sharedmemory` provides a faster alternative.
|
|
3
|
+
[](https://badge.fury.io/py/py-sharedmemory)
|
|
6
4
|
|
|
7
5
|
Install using:
|
|
8
6
|
```
|
|
9
7
|
pip install py-sharedmemory
|
|
10
8
|
```
|
|
11
9
|
|
|
10
|
+
Python's standard `multiprocessing.Queue` pickles data for inter-process communication. This often creates performance bottlenecks in demanding applications like Deep Reinforcement Learning (DeepRL) training, scientific computing, or distributed systems that handle large data objects (e.g., NumPy arrays, tensors >1MB) or involve high I/O volumes between many processes (like actors, replay buffers, and trainers).
|
|
11
|
+
|
|
12
|
+
`py-sharedmemory` offers a high-performance replacement. It bypasses pickling by converting data into bytes and moving them into shared memory. Only minimal metadata is sent via the standard queue mechanism. This significantly reduces serialization overhead and inter-process I/O, drastically improving data transfer throughput for data-intensive Python multiprocessing tasks. If your application struggles with slow `multiprocessing.Queue` performance due to large object transfers, `py-sharedmemory` provides a faster alternative.
|
|
13
|
+
|
|
12
14
|
---
|
|
13
15
|
|
|
14
16
|
# Usage example
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
from typing import NamedTuple
|
|
2
|
+
from multiprocessing.shared_memory import SharedMemory
|
|
3
|
+
import pickle
|
|
4
|
+
from pickle import PickleBuffer
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class SMInfo(NamedTuple):
|
|
8
|
+
smh_name: str
|
|
9
|
+
total_bytes: int
|
|
10
|
+
buffer_lengths: list[int]
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def data_to_smh(data: any) -> tuple[SharedMemory, SMInfo]:
|
|
14
|
+
buffers: list[PickleBuffer] = []
|
|
15
|
+
buffer_lengths: list[int] = []
|
|
16
|
+
|
|
17
|
+
def _append_buffer(buffer: PickleBuffer):
|
|
18
|
+
memview = buffer.raw()
|
|
19
|
+
buffers.append(memview)
|
|
20
|
+
buffer_lengths.append(len(memview))
|
|
21
|
+
|
|
22
|
+
main = pickle.dumps(data, pickle.HIGHEST_PROTOCOL, buffer_callback=_append_buffer)
|
|
23
|
+
|
|
24
|
+
len_main = len(main)
|
|
25
|
+
total_bytes = len_main + sum(buffer_lengths)
|
|
26
|
+
buffer_lengths.insert(0, len_main)
|
|
27
|
+
buffers.insert(0, main)
|
|
28
|
+
|
|
29
|
+
shm: SharedMemory = SharedMemory(create=True, size=total_bytes)
|
|
30
|
+
offset = 0
|
|
31
|
+
for length, buffer in zip(buffer_lengths, buffers):
|
|
32
|
+
shm.buf[offset : offset + length] = buffer
|
|
33
|
+
offset += length
|
|
34
|
+
|
|
35
|
+
info: SMInfo = SMInfo(shm.name, total_bytes, buffer_lengths)
|
|
36
|
+
return shm, info
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def data_from_smh(info: SMInfo) -> any:
|
|
40
|
+
shm: SharedMemory = SharedMemory(name=info.smh_name, size=info.total_bytes)
|
|
41
|
+
local_buffers: bytes = bytes(shm.buf[: info.total_bytes])
|
|
42
|
+
shm.close()
|
|
43
|
+
|
|
44
|
+
# unpack data
|
|
45
|
+
buffers = []
|
|
46
|
+
offset = 0
|
|
47
|
+
for length in info.buffer_lengths:
|
|
48
|
+
buffer = local_buffers[offset : offset + length]
|
|
49
|
+
buffers.append(buffer)
|
|
50
|
+
offset += length
|
|
51
|
+
|
|
52
|
+
main = buffers.pop(0)
|
|
53
|
+
return pickle.loads(main, buffers=buffers)
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import multiprocessing as mp
|
|
2
|
+
|
|
3
|
+
from .receiver import SharedMemoryReceiver
|
|
4
|
+
from .sender import SharedMemorySender
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def create_shared_memory_pair(capacity):
|
|
8
|
+
data_queue = mp.Queue()
|
|
9
|
+
ack_queue = mp.Queue()
|
|
10
|
+
|
|
11
|
+
sender = SharedMemorySender(capacity, data_queue, ack_queue)
|
|
12
|
+
receiver = SharedMemoryReceiver(data_queue, ack_queue)
|
|
13
|
+
return sender, receiver
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
from multiprocessing.queues import Queue
|
|
2
|
+
|
|
3
|
+
from .convert import SMInfo, data_from_smh
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class SharedMemoryReceiver:
|
|
7
|
+
def __init__(self, q_data_in: Queue, q_ack_out: Queue):
|
|
8
|
+
self.q_data_in: Queue = q_data_in
|
|
9
|
+
self.q_ack_out: Queue = q_ack_out
|
|
10
|
+
|
|
11
|
+
def _process_info(self, info: SMInfo):
|
|
12
|
+
assert info, "No info received"
|
|
13
|
+
data = data_from_smh(info)
|
|
14
|
+
self.q_ack_out.put(info.smh_name)
|
|
15
|
+
del info
|
|
16
|
+
return data
|
|
17
|
+
|
|
18
|
+
def get_nowait(self):
|
|
19
|
+
return self.get(block=False)
|
|
20
|
+
|
|
21
|
+
def get(self, block: bool = True, timeout: float = None):
|
|
22
|
+
info: SMInfo = self.q_data_in.get(block, timeout)
|
|
23
|
+
return self._process_info(info)
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import multiprocessing as mp
|
|
2
|
+
import threading as th
|
|
3
|
+
import atexit
|
|
4
|
+
|
|
5
|
+
from .convert import data_to_smh
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class SharedMemorySender:
|
|
9
|
+
def __init__(self, capacity: int, q_data_out: mp.Queue, q_ack_in: mp.Queue):
|
|
10
|
+
self.q_data_out: mp.Queue = q_data_out
|
|
11
|
+
self.q_ack_in: mp.Queue = q_ack_in
|
|
12
|
+
self.capacity: int = capacity
|
|
13
|
+
|
|
14
|
+
self.is_closed = False
|
|
15
|
+
self.is_initialized = False
|
|
16
|
+
|
|
17
|
+
self.open_handles = {}
|
|
18
|
+
self.has_capacity = None
|
|
19
|
+
self.is_empty = None
|
|
20
|
+
self.thread_ack_running = True
|
|
21
|
+
self.thread_ack = None
|
|
22
|
+
|
|
23
|
+
def _initialize(self):
|
|
24
|
+
if self.is_initialized:
|
|
25
|
+
return
|
|
26
|
+
|
|
27
|
+
self.is_initialized = True
|
|
28
|
+
atexit.register(self._cleanup)
|
|
29
|
+
|
|
30
|
+
self.has_capacity = th.Semaphore(self.capacity or 0)
|
|
31
|
+
self.is_empty = th.Event()
|
|
32
|
+
self.is_empty.set()
|
|
33
|
+
|
|
34
|
+
self.thread_ack_running = True
|
|
35
|
+
self.thread_ack = th.Thread(target=self._handle_acks, daemon=True)
|
|
36
|
+
self.thread_ack.start()
|
|
37
|
+
|
|
38
|
+
def _cleanup(self):
|
|
39
|
+
if self.is_closed is not None:
|
|
40
|
+
self.is_closed = True
|
|
41
|
+
|
|
42
|
+
if self.thread_ack_running is not None:
|
|
43
|
+
self.thread_ack_running = False
|
|
44
|
+
if self.thread_ack is not None and self.thread_ack.is_alive():
|
|
45
|
+
self.thread_ack.join(timeout=1.0)
|
|
46
|
+
|
|
47
|
+
if self.open_handles is not None:
|
|
48
|
+
for shm_name in list(self.open_handles.keys()):
|
|
49
|
+
try:
|
|
50
|
+
self._close_handle(shm_name)
|
|
51
|
+
except:
|
|
52
|
+
pass
|
|
53
|
+
self.open_handles = None
|
|
54
|
+
|
|
55
|
+
def __del__(self):
|
|
56
|
+
self._cleanup()
|
|
57
|
+
|
|
58
|
+
def _close_handle(self, shm_name: str):
|
|
59
|
+
smh = self.open_handles.pop(shm_name, None)
|
|
60
|
+
if smh is None:
|
|
61
|
+
return
|
|
62
|
+
if len(self.open_handles) == 0:
|
|
63
|
+
self.is_empty.set()
|
|
64
|
+
|
|
65
|
+
smh.close()
|
|
66
|
+
smh.unlink()
|
|
67
|
+
if self.capacity:
|
|
68
|
+
self.has_capacity.release()
|
|
69
|
+
|
|
70
|
+
def _handle_acks(self):
|
|
71
|
+
while self.thread_ack_running:
|
|
72
|
+
try:
|
|
73
|
+
ack_smh_name = self.q_ack_in.get(timeout=0.1)
|
|
74
|
+
self._close_handle(ack_smh_name)
|
|
75
|
+
except mp.queues.Empty:
|
|
76
|
+
continue
|
|
77
|
+
except Exception as e:
|
|
78
|
+
if not self.is_closed:
|
|
79
|
+
print(f"SharedMemorySender._handle_acks error: {e}")
|
|
80
|
+
self._cleanup()
|
|
81
|
+
raise e
|
|
82
|
+
|
|
83
|
+
def put_nowait(self, data):
|
|
84
|
+
return self.put(data, block=False)
|
|
85
|
+
|
|
86
|
+
def put(self, data, block: bool = True, timeout: float = None):
|
|
87
|
+
if self.is_closed:
|
|
88
|
+
raise BrokenPipeError("Sender is closed.")
|
|
89
|
+
self._initialize()
|
|
90
|
+
|
|
91
|
+
if self.capacity:
|
|
92
|
+
if not self.has_capacity.acquire(blocking=block, timeout=timeout):
|
|
93
|
+
raise mp.queues.Full
|
|
94
|
+
|
|
95
|
+
try:
|
|
96
|
+
smh, info = data_to_smh(data)
|
|
97
|
+
self.is_empty.clear()
|
|
98
|
+
self.q_data_out.put_nowait(info)
|
|
99
|
+
self.open_handles[smh.name] = smh
|
|
100
|
+
except Exception as e:
|
|
101
|
+
if not self.is_closed:
|
|
102
|
+
print(f"SharedMemorySender.put error: {e}")
|
|
103
|
+
self._cleanup()
|
|
104
|
+
raise e
|
|
105
|
+
|
|
106
|
+
def wait_for_all_ack(self):
|
|
107
|
+
if self.is_closed:
|
|
108
|
+
raise BrokenPipeError("Sender is closed.")
|
|
109
|
+
if not self.is_initialized:
|
|
110
|
+
return
|
|
111
|
+
self.is_empty.wait()
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: py-sharedmemory
|
|
3
|
-
Version:
|
|
4
|
-
Summary:
|
|
3
|
+
Version: 2.0.0
|
|
4
|
+
Summary: Optimized Python IPC: Uses shared memory to bypass multiprocessing queue I/O bottlenecks, ideal for large data (1MB+) in scientific computing, RL, etc. Reduces system load and improves latency consistency
|
|
5
5
|
Author: Tobias Würth
|
|
6
6
|
License-Expression: MIT
|
|
7
7
|
Project-URL: Repository, https://github.com/tobiaswuerth/python_shared_memory_queue
|
|
@@ -17,15 +17,17 @@ Dynamic: license-file
|
|
|
17
17
|
|
|
18
18
|
# py-sharedmemory: High-Performance Multiprocessing Queue Alternative for Large Data
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
`py-sharedmemory` offers a high-performance replacement. It bypasses pickling by converting data into bytes and moving them into shared memory. Only minimal metadata is sent via the standard queue mechanism. This significantly reduces serialization overhead and inter-process I/O, drastically improving data transfer throughput for data-intensive Python multiprocessing tasks. If your application struggles with slow `multiprocessing.Queue` performance due to large object transfers, `py-sharedmemory` provides a faster alternative.
|
|
20
|
+
[](https://badge.fury.io/py/py-sharedmemory)
|
|
23
21
|
|
|
24
22
|
Install using:
|
|
25
23
|
```
|
|
26
24
|
pip install py-sharedmemory
|
|
27
25
|
```
|
|
28
26
|
|
|
27
|
+
Python's standard `multiprocessing.Queue` pickles data for inter-process communication. This often creates performance bottlenecks in demanding applications like Deep Reinforcement Learning (DeepRL) training, scientific computing, or distributed systems that handle large data objects (e.g., NumPy arrays, tensors >1MB) or involve high I/O volumes between many processes (like actors, replay buffers, and trainers).
|
|
28
|
+
|
|
29
|
+
`py-sharedmemory` offers a high-performance replacement. It bypasses pickling by converting data into bytes and moving them into shared memory. Only minimal metadata is sent via the standard queue mechanism. This significantly reduces serialization overhead and inter-process I/O, drastically improving data transfer throughput for data-intensive Python multiprocessing tasks. If your application struggles with slow `multiprocessing.Queue` performance due to large object transfers, `py-sharedmemory` provides a faster alternative.
|
|
30
|
+
|
|
29
31
|
---
|
|
30
32
|
|
|
31
33
|
# Usage example
|
|
@@ -2,7 +2,7 @@ LICENSE
|
|
|
2
2
|
README.md
|
|
3
3
|
pyproject.toml
|
|
4
4
|
memory/__init__.py
|
|
5
|
-
memory/
|
|
5
|
+
memory/convert.py
|
|
6
6
|
memory/factory.py
|
|
7
7
|
memory/receiver.py
|
|
8
8
|
memory/sender.py
|
|
@@ -10,5 +10,4 @@ py_sharedmemory.egg-info/PKG-INFO
|
|
|
10
10
|
py_sharedmemory.egg-info/SOURCES.txt
|
|
11
11
|
py_sharedmemory.egg-info/dependency_links.txt
|
|
12
12
|
py_sharedmemory.egg-info/requires.txt
|
|
13
|
-
py_sharedmemory.egg-info/top_level.txt
|
|
14
|
-
tests/test_shared_memory.py
|
|
13
|
+
py_sharedmemory.egg-info/top_level.txt
|
|
@@ -8,13 +8,13 @@ packages = ['memory']
|
|
|
8
8
|
[project]
|
|
9
9
|
name = "py-sharedmemory"
|
|
10
10
|
authors = [{name = "Tobias Würth"}]
|
|
11
|
-
version = "
|
|
11
|
+
version = "2.0.0"
|
|
12
12
|
requires-python = ">=3.9"
|
|
13
13
|
dependencies = [
|
|
14
14
|
"numpy>=1.26"
|
|
15
15
|
]
|
|
16
16
|
|
|
17
|
-
description = "
|
|
17
|
+
description = "Optimized Python IPC: Uses shared memory to bypass multiprocessing queue I/O bottlenecks, ideal for large data (1MB+) in scientific computing, RL, etc. Reduces system load and improves latency consistency"
|
|
18
18
|
keywords = ["python", "performance", "queue", "multiprocessing", "numpy", "zero-copy", "data-transfer", "low-latency", "inter-process-communication", "shared-memory", "high-throughput", "parallel-processing", "large-data", "memoryview"]
|
|
19
19
|
readme = {file = "README.md", content-type = "text/markdown"}
|
|
20
20
|
license = "MIT"
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
from .factory import create_shared_memory_pair, SharedMemorySender, SharedMemoryReceiver
|
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
import numpy as np
|
|
2
|
-
|
|
3
|
-
def _encode_int(item:int):
|
|
4
|
-
size = item.bit_length() // 8 + 1
|
|
5
|
-
signed = item < 0
|
|
6
|
-
bytes_data = item.to_bytes(size, 'little', signed=signed)
|
|
7
|
-
return ('i', size, signed), memoryview(bytes_data)
|
|
8
|
-
|
|
9
|
-
def _decode_int(byte_data:memoryview, info:tuple):
|
|
10
|
-
return int.from_bytes(byte_data, 'little', signed=info[2])
|
|
11
|
-
|
|
12
|
-
def _encode_float(item:float):
|
|
13
|
-
byte_data = np.float64(item).tobytes()
|
|
14
|
-
return ('f', 8), memoryview(byte_data)
|
|
15
|
-
|
|
16
|
-
def _decode_float(byte_data:memoryview, info:tuple):
|
|
17
|
-
return np.frombuffer(byte_data, dtype=np.float64)[0]
|
|
18
|
-
|
|
19
|
-
def _encode_bool(item:bool):
|
|
20
|
-
byte_data = item.to_bytes(1, 'little')
|
|
21
|
-
return ('b', 1), memoryview(byte_data)
|
|
22
|
-
|
|
23
|
-
def _decode_bool(byte_data:memoryview, info:tuple):
|
|
24
|
-
return bool.from_bytes(byte_data, 'little')
|
|
25
|
-
|
|
26
|
-
def _encode_bytes(item:bytes):
|
|
27
|
-
return ('r', len(item)), memoryview(item)
|
|
28
|
-
|
|
29
|
-
def _decode_bytes(byte_data:memoryview, info:tuple):
|
|
30
|
-
if byte_data is None:
|
|
31
|
-
return b''
|
|
32
|
-
return bytes(byte_data)
|
|
33
|
-
|
|
34
|
-
def _encode_str(item:str):
|
|
35
|
-
bytes_data = str.encode(item)
|
|
36
|
-
return ('s', len(bytes_data)), memoryview(bytes_data)
|
|
37
|
-
|
|
38
|
-
def _decode_str(byte_data:memoryview, info:tuple):
|
|
39
|
-
if byte_data is None:
|
|
40
|
-
return ''
|
|
41
|
-
return bytes(byte_data).decode()
|
|
42
|
-
|
|
43
|
-
def _encode_ndarray(item:np.ndarray):
|
|
44
|
-
byte_data = item.tobytes()
|
|
45
|
-
return ('n', len(byte_data), item.dtype, item.shape), memoryview(byte_data)
|
|
46
|
-
|
|
47
|
-
def _decode_ndarray(byte_data:memoryview, info:tuple):
|
|
48
|
-
return np.frombuffer(byte_data, dtype=info[2]).copy().reshape(info[3])
|
|
49
|
-
|
|
50
|
-
def _encode_ndtype(item:np.dtype):
|
|
51
|
-
byte_data = str.encode(str(item))
|
|
52
|
-
return ('t', len(byte_data)), memoryview(byte_data)
|
|
53
|
-
|
|
54
|
-
def _decode_ndtype(byte_data:memoryview, info:tuple):
|
|
55
|
-
return np.dtype(bytes(byte_data).decode())
|
|
56
|
-
|
|
57
|
-
iterables = {
|
|
58
|
-
tuple: 'p',
|
|
59
|
-
list: 'l',
|
|
60
|
-
dict: 'd',
|
|
61
|
-
set: 'u',
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
encoder = {
|
|
65
|
-
bytes: _encode_bytes,
|
|
66
|
-
int: _encode_int,
|
|
67
|
-
float: _encode_float,
|
|
68
|
-
bool: _encode_bool,
|
|
69
|
-
str: _encode_str,
|
|
70
|
-
np.ndarray: _encode_ndarray,
|
|
71
|
-
}
|
|
72
|
-
decoder = {
|
|
73
|
-
'r': _decode_bytes,
|
|
74
|
-
'i': _decode_int,
|
|
75
|
-
'f': _decode_float,
|
|
76
|
-
'b': _decode_bool,
|
|
77
|
-
's': _decode_str,
|
|
78
|
-
'n': _decode_ndarray,
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
def encode(item):
|
|
82
|
-
if item is None:
|
|
83
|
-
return (None, 0), b''
|
|
84
|
-
|
|
85
|
-
datatype = type(item)
|
|
86
|
-
enc = encoder.get(datatype, None)
|
|
87
|
-
if enc is not None:
|
|
88
|
-
return enc(item)
|
|
89
|
-
|
|
90
|
-
if isinstance(item, np.dtype):
|
|
91
|
-
return _encode_ndtype(item)
|
|
92
|
-
|
|
93
|
-
raise ValueError(f"Unsupported data type: {type(item)}")
|
|
94
|
-
|
|
95
|
-
def decode(bytes_data:memoryview, info:tuple):
|
|
96
|
-
if info[0] is None:
|
|
97
|
-
return None
|
|
98
|
-
|
|
99
|
-
dec = decoder.get(info[0], None)
|
|
100
|
-
if dec is not None:
|
|
101
|
-
return dec(bytes_data, info)
|
|
102
|
-
|
|
103
|
-
if info[0] == 't':
|
|
104
|
-
return _decode_ndtype(bytes_data, info)
|
|
105
|
-
|
|
106
|
-
raise ValueError(f"Unsupported data type: {info[0]}")
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import multiprocessing as mp
|
|
2
|
-
|
|
3
|
-
from .receiver import SharedMemoryReceiver
|
|
4
|
-
from .sender import SharedMemorySender
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
def create_shared_memory_pair(capacity):
|
|
8
|
-
q_data = mp.Queue(capacity)
|
|
9
|
-
q_ack = mp.Queue()
|
|
10
|
-
sender = SharedMemorySender(capacity, q_data, q_ack)
|
|
11
|
-
receiver = SharedMemoryReceiver(q_data, q_ack)
|
|
12
|
-
return sender, receiver
|
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
import multiprocessing as mp
|
|
2
|
-
import queue
|
|
3
|
-
from multiprocessing.shared_memory import SharedMemory
|
|
4
|
-
|
|
5
|
-
from .converter import decode, iterables
|
|
6
|
-
|
|
7
|
-
iterable_keys = set(iterables.values())
|
|
8
|
-
non_dict_iterables = set(iterables.keys()) - {dict}
|
|
9
|
-
|
|
10
|
-
class SharedMemoryReceiver:
|
|
11
|
-
def __init__(self, queue_data_in:mp.Queue, queue_ack_out:mp.Queue):
|
|
12
|
-
self.queue_data_in = queue_data_in
|
|
13
|
-
self.queue_ack_out = queue_ack_out
|
|
14
|
-
|
|
15
|
-
def _recover_data_recursive(self, shm:SharedMemory, info:tuple, byte_idx=0):
|
|
16
|
-
if info[0] not in iterable_keys:
|
|
17
|
-
if info[1] == 0: # size
|
|
18
|
-
return decode(None, info)
|
|
19
|
-
return decode(shm.buf[byte_idx:byte_idx+info[1]], info)
|
|
20
|
-
|
|
21
|
-
if info[0] == 'd': # dict
|
|
22
|
-
data = {}
|
|
23
|
-
for info_child, key in zip(info[3], info[4]):
|
|
24
|
-
item = self._recover_data_recursive(shm, info_child, byte_idx)
|
|
25
|
-
byte_idx += info_child[1]
|
|
26
|
-
data[key] = item
|
|
27
|
-
return data
|
|
28
|
-
|
|
29
|
-
# tuple, list, set
|
|
30
|
-
data = []
|
|
31
|
-
for info_child in info[3]:
|
|
32
|
-
item = self._recover_data_recursive(shm, info_child, byte_idx)
|
|
33
|
-
byte_idx += info_child[1]
|
|
34
|
-
data.append(item)
|
|
35
|
-
|
|
36
|
-
if info[2] in non_dict_iterables:
|
|
37
|
-
return info[2](data)
|
|
38
|
-
|
|
39
|
-
# NamedTuple needs to be handled separately
|
|
40
|
-
return info[2](*data)
|
|
41
|
-
|
|
42
|
-
def _recover_data(self, info:tuple):
|
|
43
|
-
if info[2] == 0:
|
|
44
|
-
return self._recover_data_recursive(None, info[1:])
|
|
45
|
-
|
|
46
|
-
name = info[0]
|
|
47
|
-
info = info[1:]
|
|
48
|
-
shm = SharedMemory(name=name, size=info[1])
|
|
49
|
-
data = self._recover_data_recursive(shm, info)
|
|
50
|
-
shm.close()
|
|
51
|
-
return data
|
|
52
|
-
|
|
53
|
-
def get_nowait(self):
|
|
54
|
-
if self.queue_data_in.empty():
|
|
55
|
-
return None
|
|
56
|
-
|
|
57
|
-
try:
|
|
58
|
-
info = self.queue_data_in.get_nowait()
|
|
59
|
-
except queue.Empty:
|
|
60
|
-
return None
|
|
61
|
-
|
|
62
|
-
if info is None:
|
|
63
|
-
return None
|
|
64
|
-
|
|
65
|
-
item = self._recover_data(info)
|
|
66
|
-
self.queue_ack_out.put_nowait(info[0])
|
|
67
|
-
del info
|
|
68
|
-
return item
|
|
69
|
-
|
|
70
|
-
def get(self, timeout=None):
|
|
71
|
-
info = self.queue_data_in.get(timeout=timeout)
|
|
72
|
-
item = self._recover_data(info)
|
|
73
|
-
self.queue_ack_out.put_nowait(info[0])
|
|
74
|
-
del info
|
|
75
|
-
return item
|
|
@@ -1,149 +0,0 @@
|
|
|
1
|
-
import multiprocessing as mp
|
|
2
|
-
import queue
|
|
3
|
-
from multiprocessing.shared_memory import SharedMemory
|
|
4
|
-
|
|
5
|
-
from .converter import encode, iterables
|
|
6
|
-
|
|
7
|
-
class _Separator:
|
|
8
|
-
def __init__(self, obj):
|
|
9
|
-
self.obj = obj
|
|
10
|
-
|
|
11
|
-
def _iterable_handler_list(stack, parents, entry):
|
|
12
|
-
stack.append(_Separator((type(entry), entry.__class__,)))
|
|
13
|
-
parents.append(([], []))
|
|
14
|
-
for field in reversed(entry): # reverse to maintain order in final binary
|
|
15
|
-
stack.append(field)
|
|
16
|
-
|
|
17
|
-
def _iterable_handler_set(stack, parents, entry):
|
|
18
|
-
stack.append(_Separator((set,entry.__class__,)))
|
|
19
|
-
parents.append(([], []))
|
|
20
|
-
for field in entry:
|
|
21
|
-
stack.append(field)
|
|
22
|
-
|
|
23
|
-
def _iterable_handler_dict(stack, parents, entry):
|
|
24
|
-
keys = list(entry.keys())
|
|
25
|
-
stack.append(_Separator((dict,entry.__class__,keys)))
|
|
26
|
-
parents.append(([], []))
|
|
27
|
-
for key in reversed(keys): # reverse to maintain order in final binary
|
|
28
|
-
stack.append(entry[key])
|
|
29
|
-
|
|
30
|
-
iterable_handlers = {
|
|
31
|
-
list: _iterable_handler_list,
|
|
32
|
-
tuple: _iterable_handler_list,
|
|
33
|
-
set: _iterable_handler_set,
|
|
34
|
-
dict: _iterable_handler_dict,
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
tuple_key = iterables[tuple]
|
|
38
|
-
|
|
39
|
-
class SharedMemorySender:
|
|
40
|
-
def __init__(self, capacity, queue_data_out:mp.Queue, queue_ack_in:mp.Queue):
|
|
41
|
-
self.open_handles = {}
|
|
42
|
-
self.queue_data_out = queue_data_out
|
|
43
|
-
self.queue_ack_in = queue_ack_in
|
|
44
|
-
self.capacity = capacity
|
|
45
|
-
|
|
46
|
-
def _prepare_binary(self, data):
|
|
47
|
-
parents:list[tuple[list[memoryview], list[tuple]]] = []
|
|
48
|
-
stack = [data]
|
|
49
|
-
while stack:
|
|
50
|
-
entry = stack.pop()
|
|
51
|
-
|
|
52
|
-
if isinstance(entry, _Separator):
|
|
53
|
-
# children processed, gather the results
|
|
54
|
-
bytes_data, child_infos = parents.pop()
|
|
55
|
-
entry_type = entry.obj[0]
|
|
56
|
-
key = iterables.get(entry_type, tuple_key)
|
|
57
|
-
size = sum(info[1] for info in child_infos)
|
|
58
|
-
clazz = entry.obj[1]
|
|
59
|
-
if entry_type == dict:
|
|
60
|
-
info = (key, size, clazz, child_infos, entry.obj[2])
|
|
61
|
-
else:
|
|
62
|
-
info = (key, size, clazz, child_infos)
|
|
63
|
-
|
|
64
|
-
if not parents:
|
|
65
|
-
# root entry was tuple, return the result
|
|
66
|
-
del parents, stack
|
|
67
|
-
return info, bytes_data
|
|
68
|
-
|
|
69
|
-
# append to parent binary
|
|
70
|
-
parents[-1][0].extend(bytes_data)
|
|
71
|
-
parents[-1][1].append(info)
|
|
72
|
-
continue
|
|
73
|
-
|
|
74
|
-
entry_type = type(entry)
|
|
75
|
-
if entry_type in iterables:
|
|
76
|
-
iterable_handlers[entry_type](stack, parents, entry)
|
|
77
|
-
continue
|
|
78
|
-
if isinstance(entry, tuple):
|
|
79
|
-
# NamedTuple
|
|
80
|
-
_iterable_handler_list(stack, parents, entry)
|
|
81
|
-
continue
|
|
82
|
-
|
|
83
|
-
info, bytes_data = encode(entry)
|
|
84
|
-
if not parents:
|
|
85
|
-
# root entry was single data, return the result
|
|
86
|
-
del parents, stack
|
|
87
|
-
return info, [bytes_data]
|
|
88
|
-
|
|
89
|
-
parents[-1][0].append(bytes_data)
|
|
90
|
-
parents[-1][1].append(info)
|
|
91
|
-
|
|
92
|
-
def put(self, data):
|
|
93
|
-
self.process_ack()
|
|
94
|
-
while self.capacity and len(self.open_handles) >= self.capacity:
|
|
95
|
-
self.wait_for_ack()
|
|
96
|
-
|
|
97
|
-
info, bytes_datas = self._prepare_binary(data)
|
|
98
|
-
size = info[1]
|
|
99
|
-
if size == 0:
|
|
100
|
-
info = (None, *info)
|
|
101
|
-
self.queue_data_out.put(info)
|
|
102
|
-
return
|
|
103
|
-
|
|
104
|
-
shm = SharedMemory(create=True, size=size)
|
|
105
|
-
info = (shm.name, *info)
|
|
106
|
-
|
|
107
|
-
offset = 0
|
|
108
|
-
for item in bytes_datas:
|
|
109
|
-
size = len(item)
|
|
110
|
-
shm.buf[offset:offset + size] = item
|
|
111
|
-
offset += size
|
|
112
|
-
|
|
113
|
-
self.queue_data_out.put(info)
|
|
114
|
-
self.open_handles[shm.name] = shm
|
|
115
|
-
|
|
116
|
-
def close_handle(self, shm_name:str):
|
|
117
|
-
shm = self.open_handles[shm_name]
|
|
118
|
-
shm.close()
|
|
119
|
-
shm.unlink()
|
|
120
|
-
del self.open_handles[shm_name]
|
|
121
|
-
|
|
122
|
-
def has_space(self):
|
|
123
|
-
self.process_ack()
|
|
124
|
-
return not self.capacity or len(self.open_handles) < self.capacity
|
|
125
|
-
|
|
126
|
-
def process_ack(self):
|
|
127
|
-
try:
|
|
128
|
-
while True:
|
|
129
|
-
retour = self.queue_ack_in.get_nowait()
|
|
130
|
-
if retour is None:
|
|
131
|
-
return
|
|
132
|
-
self.close_handle(retour)
|
|
133
|
-
except queue.Empty:
|
|
134
|
-
pass
|
|
135
|
-
|
|
136
|
-
def close_all(self):
|
|
137
|
-
for shm_name in list(self.open_handles.keys()):
|
|
138
|
-
self.close_handle(shm_name)
|
|
139
|
-
|
|
140
|
-
def wait_for_ack(self):
|
|
141
|
-
shm_name = self.queue_ack_in.get()
|
|
142
|
-
self.close_handle(shm_name)
|
|
143
|
-
|
|
144
|
-
def wait_for_all_ack(self):
|
|
145
|
-
while len(self.open_handles) > 0:
|
|
146
|
-
self.wait_for_ack()
|
|
147
|
-
|
|
148
|
-
def __del__(self):
|
|
149
|
-
self.close_all()
|
|
@@ -1,339 +0,0 @@
|
|
|
1
|
-
import unittest
|
|
2
|
-
import numpy as np
|
|
3
|
-
|
|
4
|
-
import multiprocessing as mp
|
|
5
|
-
mp.log_to_stderr()
|
|
6
|
-
|
|
7
|
-
from typing import NamedTuple
|
|
8
|
-
|
|
9
|
-
from memory import create_shared_memory_pair
|
|
10
|
-
|
|
11
|
-
class MyTuple(NamedTuple):
|
|
12
|
-
a:int
|
|
13
|
-
b:float
|
|
14
|
-
c:str
|
|
15
|
-
|
|
16
|
-
class TestSharedMemory(unittest.TestCase):
|
|
17
|
-
def test_None(self):
|
|
18
|
-
sender, receiver = create_shared_memory_pair(capacity=1)
|
|
19
|
-
data = None
|
|
20
|
-
sender.put(data)
|
|
21
|
-
item = receiver.get(timeout=2)
|
|
22
|
-
self.assertEqual(item, data, f"Expected {data}, got {item}")
|
|
23
|
-
def test_None_tuple(self):
|
|
24
|
-
sender, receiver = create_shared_memory_pair(capacity=1)
|
|
25
|
-
data = (None,None)
|
|
26
|
-
sender.put(data)
|
|
27
|
-
item = receiver.get(timeout=2)
|
|
28
|
-
self.assertEqual(item, data, f"Expected {data}, got {item}")
|
|
29
|
-
def test_None_list(self):
|
|
30
|
-
sender, receiver = create_shared_memory_pair(capacity=1)
|
|
31
|
-
data = [None,None]
|
|
32
|
-
sender.put(data)
|
|
33
|
-
item = receiver.get(timeout=2)
|
|
34
|
-
self.assertEqual(item, data, f"Expected {data}, got {item}")
|
|
35
|
-
def test_None_dict(self):
|
|
36
|
-
sender, receiver = create_shared_memory_pair(capacity=1)
|
|
37
|
-
data = {None:None}
|
|
38
|
-
sender.put(data)
|
|
39
|
-
item = receiver.get(timeout=2)
|
|
40
|
-
self.assertEqual(item, data, f"Expected {data}, got {item}")
|
|
41
|
-
def test_None_set(self):
|
|
42
|
-
sender, receiver = create_shared_memory_pair(capacity=1)
|
|
43
|
-
data = {None}
|
|
44
|
-
sender.put(data)
|
|
45
|
-
item = receiver.get(timeout=2)
|
|
46
|
-
self.assertEqual(item, data, f"Expected {data}, got {item}")
|
|
47
|
-
|
|
48
|
-
def test_int_positive_small(self):
|
|
49
|
-
sender, receiver = create_shared_memory_pair(capacity=1)
|
|
50
|
-
data = 37
|
|
51
|
-
sender.put(data)
|
|
52
|
-
item = receiver.get(timeout=2)
|
|
53
|
-
self.assertEqual(item, data, f"Expected {data}, got {item}")
|
|
54
|
-
def test_int_negative_small(self):
|
|
55
|
-
sender, receiver = create_shared_memory_pair(capacity=1)
|
|
56
|
-
data = -37
|
|
57
|
-
sender.put(data)
|
|
58
|
-
item = receiver.get(timeout=2)
|
|
59
|
-
self.assertEqual(item, data, f"Expected {data}, got {item}")
|
|
60
|
-
def test_int_positive_big(self):
|
|
61
|
-
sender, receiver = create_shared_memory_pair(capacity=1)
|
|
62
|
-
data = 2147483640
|
|
63
|
-
sender.put(data)
|
|
64
|
-
item = receiver.get(timeout=2)
|
|
65
|
-
self.assertEqual(item, data, f"Expected {data}, got {item}")
|
|
66
|
-
def test_int_negative_big(self):
|
|
67
|
-
sender, receiver = create_shared_memory_pair(capacity=1)
|
|
68
|
-
data = -2147483640
|
|
69
|
-
sender.put(data)
|
|
70
|
-
item = receiver.get(timeout=2)
|
|
71
|
-
self.assertEqual(item, data, f"Expected {data}, got {item}")
|
|
72
|
-
|
|
73
|
-
def test_float1(self):
|
|
74
|
-
sender, receiver = create_shared_memory_pair(capacity=1)
|
|
75
|
-
data = 21474.83640
|
|
76
|
-
sender.put(data)
|
|
77
|
-
item = receiver.get(timeout=2)
|
|
78
|
-
self.assertEqual(item, data, f"Expected {data}, got {item}")
|
|
79
|
-
def test_float2(self):
|
|
80
|
-
sender, receiver = create_shared_memory_pair(capacity=1)
|
|
81
|
-
data = -21474.83640
|
|
82
|
-
sender.put(data)
|
|
83
|
-
item = receiver.get(timeout=2)
|
|
84
|
-
self.assertEqual(item, data, f"Expected {data}, got {item}")
|
|
85
|
-
|
|
86
|
-
def test_bool1(self):
|
|
87
|
-
sender, receiver = create_shared_memory_pair(capacity=1)
|
|
88
|
-
data = True
|
|
89
|
-
sender.put(data)
|
|
90
|
-
item = receiver.get(timeout=2)
|
|
91
|
-
self.assertEqual(item, data, f"Expected {data}, got {item}")
|
|
92
|
-
def test_bool2(self):
|
|
93
|
-
sender, receiver = create_shared_memory_pair(capacity=1)
|
|
94
|
-
data = True
|
|
95
|
-
sender.put(data)
|
|
96
|
-
item = receiver.get(timeout=2)
|
|
97
|
-
self.assertEqual(item, data, f"Expected {data}, got {item}")
|
|
98
|
-
|
|
99
|
-
def test_bytes(self):
|
|
100
|
-
sender, receiver = create_shared_memory_pair(capacity=1)
|
|
101
|
-
data = b"Hello World!"
|
|
102
|
-
sender.put(data)
|
|
103
|
-
item = receiver.get(timeout=2)
|
|
104
|
-
self.assertEqual(item, data, f"Expected {data}, got {item}")
|
|
105
|
-
def test_bytes_empty(self):
|
|
106
|
-
sender, receiver = create_shared_memory_pair(capacity=1)
|
|
107
|
-
data = b''
|
|
108
|
-
sender.put(data)
|
|
109
|
-
item = receiver.get(timeout=2)
|
|
110
|
-
self.assertEqual(item, data, f"Expected {data}, got {item}")
|
|
111
|
-
|
|
112
|
-
def test_str(self):
|
|
113
|
-
sender, receiver = create_shared_memory_pair(capacity=1)
|
|
114
|
-
data = "Hello World!"
|
|
115
|
-
sender.put(data)
|
|
116
|
-
item = receiver.get(timeout=2)
|
|
117
|
-
self.assertEqual(item, data, f"Expected {data}, got {item}")
|
|
118
|
-
def test_str_empty(self):
|
|
119
|
-
sender, receiver = create_shared_memory_pair(capacity=1)
|
|
120
|
-
data = ""
|
|
121
|
-
sender.put(data)
|
|
122
|
-
item = receiver.get(timeout=2)
|
|
123
|
-
self.assertEqual(item, data, f"Expected {data}, got {item}")
|
|
124
|
-
def test_str_long(self):
|
|
125
|
-
sender, receiver = create_shared_memory_pair(capacity=1)
|
|
126
|
-
data = """Zarathustra went down the mountain alone, no one meeting him. When he entered the forest, however, there suddenly stood before him an old man, who had left his holy cot to seek roots. And thus spake the old man to Zarathustra:
|
|
127
|
-
“No stranger to me is this wanderer: many years ago passed he by. Zarathustra he was called; but he hath altered.
|
|
128
|
-
Then thou carriedst thine ashes into the mountains: wilt thou now carry thy fire into the valleys? Fearest thou not the incendiary’s doom?
|
|
129
|
-
Yea, I recognise Zarathustra. Pure is his eye, and no loathing lurketh about his mouth. Goeth he not along like a dancer?
|
|
130
|
-
Altered is Zarathustra; a child hath Zarathustra become; an awakened one is Zarathustra: what wilt thou do in the land of the sleepers?
|
|
131
|
-
As in the sea hast thou lived in solitude, and it hath borne thee up. Alas, wilt thou now go ashore? Alas, wilt thou again drag thy body thyself?”
|
|
132
|
-
Zarathustra answered: “I love mankind.”
|
|
133
|
-
“Why,” said the saint, “did I go into the forest and the desert? Was it not because I loved men far too well?
|
|
134
|
-
Now I love God: men, I do not love. Man is a thing too imperfect for me. Love to man would be fatal to me.”
|
|
135
|
-
Zarathustra answered: “What spake I of love! I am bringing gifts unto men.”
|
|
136
|
-
“Give them nothing,” said the saint. “Take rather part of their load, and carry it along with them—that will be most agreeable unto them: if only it be agreeable unto thee!
|
|
137
|
-
If, however, thou wilt give unto them, give them no more than an alms, and let them also beg for it!”
|
|
138
|
-
“No,” replied Zarathustra, “I give no alms. I am not poor enough for that.”
|
|
139
|
-
The saint laughed at Zarathustra, and spake thus: “Then see to it that they accept thy treasures! They are distrustful of anchorites, and do not believe that we come with gifts.
|
|
140
|
-
The fall of our footsteps ringeth too hollow through their streets. And just as at night, when they are in bed and hear a man abroad long before sunrise, so they ask themselves concerning us: Where goeth the thief?
|
|
141
|
-
Go not to men, but stay in the forest! Go rather to the animals! Why not be like me—a bear amongst bears, a bird amongst birds?”
|
|
142
|
-
“And what doeth the saint in the forest?” asked Zarathustra.
|
|
143
|
-
The saint answered: “I make hymns and sing them; and in making hymns I laugh and weep and mumble: thus do I praise God.
|
|
144
|
-
With singing, weeping, laughing, and mumbling do I praise the God who is my God. But what dost thou bring us as a gift?”
|
|
145
|
-
When Zarathustra had heard these words, he bowed to the saint and said: “What should I have to give thee! Let me rather hurry hence lest I take aught away from thee!”—And thus they parted from one another, the old man and Zarathustra, laughing like schoolboys.
|
|
146
|
-
When Zarathustra was alone, however, he said to his heart: “Could it be possible! This old saint in the forest hath not yet heard of it, that God is dead!”"""
|
|
147
|
-
sender.put(data)
|
|
148
|
-
item = receiver.get(timeout=2)
|
|
149
|
-
self.assertEqual(item, data, f"Expected {data}, got {item}")
|
|
150
|
-
|
|
151
|
-
def test_ndarray1(self):
|
|
152
|
-
sender, receiver = create_shared_memory_pair(capacity=1)
|
|
153
|
-
data = np.array([1,2,3,4,5], dtype=np.uint32)
|
|
154
|
-
sender.put(data)
|
|
155
|
-
item = receiver.get(timeout=2)
|
|
156
|
-
np.testing.assert_array_equal(item, data, f"Expected {data}, got {item}")
|
|
157
|
-
def test_ndarray2(self):
|
|
158
|
-
sender, receiver = create_shared_memory_pair(capacity=1)
|
|
159
|
-
data = np.array([[1.2,2.3,3.4],[-53.3,-3535,1]], dtype=np.float32)
|
|
160
|
-
sender.put(data)
|
|
161
|
-
item = receiver.get(timeout=2)
|
|
162
|
-
np.testing.assert_array_equal(item, data, f"Expected {data}, got {item}")
|
|
163
|
-
def test_ndarray_big(self):
|
|
164
|
-
sender, receiver = create_shared_memory_pair(capacity=1)
|
|
165
|
-
data = np.random.rand(64, 30, 6, 84, 84).astype(np.float32)
|
|
166
|
-
sender.put(data)
|
|
167
|
-
item = receiver.get(timeout=2)
|
|
168
|
-
np.testing.assert_array_equal(item, data, f"Expected {data}, got {item}")
|
|
169
|
-
|
|
170
|
-
def test_ndtype1(self):
|
|
171
|
-
sender, receiver = create_shared_memory_pair(capacity=1)
|
|
172
|
-
data = np.dtype(np.float32)
|
|
173
|
-
sender.put(data)
|
|
174
|
-
item = receiver.get(timeout=2)
|
|
175
|
-
self.assertEqual(item, data, f"Expected {data}, got {item}")
|
|
176
|
-
def test_ndtype2(self):
|
|
177
|
-
sender, receiver = create_shared_memory_pair(capacity=1)
|
|
178
|
-
data = np.dtype(np.bool_)
|
|
179
|
-
sender.put(data)
|
|
180
|
-
item = receiver.get(timeout=2)
|
|
181
|
-
self.assertEqual(item, data, f"Expected {data}, got {item}")
|
|
182
|
-
|
|
183
|
-
def test_list(self):
|
|
184
|
-
sender, receiver = create_shared_memory_pair(capacity=1)
|
|
185
|
-
data = [1,2,3,4,5]
|
|
186
|
-
sender.put(data)
|
|
187
|
-
item = receiver.get(timeout=2)
|
|
188
|
-
self.assertEqual(item, data, f"Expected {data}, got {item}")
|
|
189
|
-
def test_list_nested(self):
|
|
190
|
-
sender, receiver = create_shared_memory_pair(capacity=1)
|
|
191
|
-
data = [[1,2,3],[4,5,6]]
|
|
192
|
-
sender.put(data)
|
|
193
|
-
item = receiver.get(timeout=2)
|
|
194
|
-
self.assertEqual(item, data, f"Expected {data}, got {item}")
|
|
195
|
-
def test_list_mixed_nested(self):
|
|
196
|
-
sender, receiver = create_shared_memory_pair(capacity=1)
|
|
197
|
-
data = [1,2,[3,4],5]
|
|
198
|
-
sender.put(data)
|
|
199
|
-
item = receiver.get(timeout=2)
|
|
200
|
-
self.assertEqual(item, data, f"Expected {data}, got {item}")
|
|
201
|
-
def test_list_nested_tuple(self):
|
|
202
|
-
sender, receiver = create_shared_memory_pair(capacity=1)
|
|
203
|
-
data = [1,2,(3,4),5]
|
|
204
|
-
sender.put(data)
|
|
205
|
-
item = receiver.get(timeout=2)
|
|
206
|
-
self.assertEqual(item, data, f"Expected {data}, got {item}")
|
|
207
|
-
def test_list_mixed(self):
|
|
208
|
-
sender, receiver = create_shared_memory_pair(capacity=1)
|
|
209
|
-
data = [1,'a',3.4,5, None, b'Hello World!']
|
|
210
|
-
sender.put(data)
|
|
211
|
-
item = receiver.get(timeout=2)
|
|
212
|
-
self.assertEqual(item, data, f"Expected {data}, got {item}")
|
|
213
|
-
def test_list_empty(self):
|
|
214
|
-
sender, receiver = create_shared_memory_pair(capacity=1)
|
|
215
|
-
data = []
|
|
216
|
-
sender.put(data)
|
|
217
|
-
item = receiver.get(timeout=2)
|
|
218
|
-
self.assertEqual(item, data, f"Expected {data}, got {item}")
|
|
219
|
-
def test_list_empty_nested(self):
|
|
220
|
-
sender, receiver = create_shared_memory_pair(capacity=1)
|
|
221
|
-
data = [[],[]]
|
|
222
|
-
sender.put(data)
|
|
223
|
-
item = receiver.get(timeout=2)
|
|
224
|
-
self.assertEqual(item, data, f"Expected {data}, got {item}")
|
|
225
|
-
def test_list_empty_nested_tuple(self):
|
|
226
|
-
sender, receiver = create_shared_memory_pair(capacity=1)
|
|
227
|
-
data = [(),()]
|
|
228
|
-
sender.put(data)
|
|
229
|
-
item = receiver.get(timeout=2)
|
|
230
|
-
self.assertEqual(item, data, f"Expected {data}, got {item}")
|
|
231
|
-
|
|
232
|
-
def test_tuple(self):
|
|
233
|
-
sender, receiver = create_shared_memory_pair(capacity=1)
|
|
234
|
-
data = (1,2,3,4,5)
|
|
235
|
-
sender.put(data)
|
|
236
|
-
item = receiver.get(timeout=2)
|
|
237
|
-
self.assertEqual(item, data, f"Expected {data}, got {item}")
|
|
238
|
-
def test_tuple_nested(self):
|
|
239
|
-
sender, receiver = create_shared_memory_pair(capacity=1)
|
|
240
|
-
data = ((1,2,3),(4,5,6))
|
|
241
|
-
sender.put(data)
|
|
242
|
-
item = receiver.get(timeout=2)
|
|
243
|
-
self.assertEqual(item, data, f"Expected {data}, got {item}")
|
|
244
|
-
def test_tuple_mixed_nested(self):
|
|
245
|
-
sender, receiver = create_shared_memory_pair(capacity=1)
|
|
246
|
-
data = (1,2,[3,4],5)
|
|
247
|
-
sender.put(data)
|
|
248
|
-
item = receiver.get(timeout=2)
|
|
249
|
-
self.assertEqual(item, data, f"Expected {data}, got {item}")
|
|
250
|
-
def test_tuple_nested_list(self):
|
|
251
|
-
sender, receiver = create_shared_memory_pair(capacity=1)
|
|
252
|
-
data = (1,2,[3,4],5)
|
|
253
|
-
sender.put(data)
|
|
254
|
-
item = receiver.get(timeout=2)
|
|
255
|
-
self.assertEqual(item, data, f"Expected {data}, got {item}")
|
|
256
|
-
def test_tuple_mixed(self):
|
|
257
|
-
sender, receiver = create_shared_memory_pair(capacity=1)
|
|
258
|
-
data = (1,'a',3.4,5, None, b'Hello World!')
|
|
259
|
-
sender.put(data)
|
|
260
|
-
item = receiver.get(timeout=2)
|
|
261
|
-
self.assertEqual(item, data, f"Expected {data}, got {item}")
|
|
262
|
-
def test_tuple_empty(self):
|
|
263
|
-
sender, receiver = create_shared_memory_pair(capacity=1)
|
|
264
|
-
data = ()
|
|
265
|
-
sender.put(data)
|
|
266
|
-
item = receiver.get(timeout=2)
|
|
267
|
-
self.assertEqual(item, data, f"Expected {data}, got {item}")
|
|
268
|
-
def test_tuple_empty_nested(self):
|
|
269
|
-
sender, receiver = create_shared_memory_pair(capacity=1)
|
|
270
|
-
data = ((),())
|
|
271
|
-
sender.put(data)
|
|
272
|
-
item = receiver.get(timeout=2)
|
|
273
|
-
self.assertEqual(item, data, f"Expected {data}, got {item}")
|
|
274
|
-
|
|
275
|
-
def test_namedtuple(self):
|
|
276
|
-
sender, receiver = create_shared_memory_pair(capacity=1)
|
|
277
|
-
data = MyTuple(1, 2.3, 'Hello World!')
|
|
278
|
-
sender.put(data)
|
|
279
|
-
item = receiver.get(timeout=2)
|
|
280
|
-
self.assertEqual(item, data, f"Expected {data}, got {item}")
|
|
281
|
-
|
|
282
|
-
def test_dict(self):
|
|
283
|
-
sender, receiver = create_shared_memory_pair(capacity=1)
|
|
284
|
-
data = {1: 'a', 'b': 2, 3: 4}
|
|
285
|
-
sender.put(data)
|
|
286
|
-
item = receiver.get(timeout=2)
|
|
287
|
-
self.assertEqual(item, data, f"Expected {data}, got {item}")
|
|
288
|
-
def test_dict_nested(self):
|
|
289
|
-
sender, receiver = create_shared_memory_pair(capacity=1)
|
|
290
|
-
data = {1: {2: 3, 4: 5}, 'a': {'b': 'c', 'd': 'e'}}
|
|
291
|
-
sender.put(data)
|
|
292
|
-
item = receiver.get(timeout=2)
|
|
293
|
-
self.assertEqual(item, data, f"Expected {data}, got {item}")
|
|
294
|
-
def test_dict_mixed_nested(self):
|
|
295
|
-
sender, receiver = create_shared_memory_pair(capacity=1)
|
|
296
|
-
data = {1: {2: 3, 4: 5}, 'a': ['b', 'c', 'd']}
|
|
297
|
-
sender.put(data)
|
|
298
|
-
item = receiver.get(timeout=2)
|
|
299
|
-
self.assertEqual(item, data, f"Expected {data}, got {item}")
|
|
300
|
-
def test_dict_mixed(self):
|
|
301
|
-
sender, receiver = create_shared_memory_pair(capacity=1)
|
|
302
|
-
data = {1: 'a', 'b': 2, 3: 4, 'c': None, 'd': b'Hello World!'}
|
|
303
|
-
sender.put(data)
|
|
304
|
-
item = receiver.get(timeout=2)
|
|
305
|
-
self.assertEqual(item, data, f"Expected {data}, got {item}")
|
|
306
|
-
def test_dict_empty(self):
|
|
307
|
-
sender, receiver = create_shared_memory_pair(capacity=1)
|
|
308
|
-
data = {}
|
|
309
|
-
sender.put(data)
|
|
310
|
-
item = receiver.get(timeout=2)
|
|
311
|
-
self.assertEqual(item, data, f"Expected {data}, got {item}")
|
|
312
|
-
def test_dict_empty_nested(self):
|
|
313
|
-
sender, receiver = create_shared_memory_pair(capacity=1)
|
|
314
|
-
data = {1: {}, 'a': {}}
|
|
315
|
-
sender.put(data)
|
|
316
|
-
item = receiver.get(timeout=2)
|
|
317
|
-
self.assertEqual(item, data, f"Expected {data}, got {item}")
|
|
318
|
-
|
|
319
|
-
def test_set(self):
|
|
320
|
-
sender, receiver = create_shared_memory_pair(capacity=1)
|
|
321
|
-
data = {1,2,3,4,5}
|
|
322
|
-
sender.put(data)
|
|
323
|
-
item = receiver.get(timeout=2)
|
|
324
|
-
self.assertEqual(item, data, f"Expected {data}, got {item}")
|
|
325
|
-
def test_set_mixed(self):
|
|
326
|
-
sender, receiver = create_shared_memory_pair(capacity=1)
|
|
327
|
-
data = {1,2,'a',None,4}
|
|
328
|
-
sender.put(data)
|
|
329
|
-
item = receiver.get(timeout=2)
|
|
330
|
-
self.assertEqual(item, data, f"Expected {data}, got {item}")
|
|
331
|
-
def test_set_empty(self):
|
|
332
|
-
sender, receiver = create_shared_memory_pair(capacity=1)
|
|
333
|
-
data = set()
|
|
334
|
-
sender.put(data)
|
|
335
|
-
item = receiver.get(timeout=2)
|
|
336
|
-
self.assertEqual(item, data, f"Expected {data}, got {item}")
|
|
337
|
-
|
|
338
|
-
if __name__ == '__main__':
|
|
339
|
-
unittest.main()
|
{py_sharedmemory-1.0.1 → py_sharedmemory-2.0.0}/py_sharedmemory.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|