Introduction
While queues and pipes copy data between processes (incurring serialization overhead), shared_memory provides direct memory sharing without copying. This is essential for high-performance workloads like sharing large NumPy arrays between processes. This lesson covers creating shared memory blocks, sharing NumPy arrays, using ShareableList, and the critical importance of proper cleanup.
Key Concepts
- SharedMemory: An OS-level shared memory block that can be accessed by multiple processes without copying data.
- Buffer protocol: SharedMemory exposes a
bufattribute that supports Python's buffer protocol, allowing zero-copy access from NumPy and other libraries. - ShareableList: A fixed-size list stored in shared memory that supports basic types (int, float, str, bytes, None).
- unlink(): Releases the shared memory block from the OS. Forgetting to call it leaks memory that persists until reboot.
Real World Context
A multi-process image pipeline loads a 2GB dataset of images into a NumPy array. Without shared memory, each of the 8 worker processes would need its own copy, requiring 16GB of RAM. With SharedMemory, all processes access the same memory block, keeping total usage at 2GB. The workers modify their assigned regions in place with no serialization overhead.
Deep Dive
Creating Shared Memory
pythonfrom multiprocessing import shared_memory import numpy as np # Create shared memory block shm = shared_memory.SharedMemory(create=True, size=1000) # Access as buffer buffer = shm.buf buffer[0] = 255 # Clean up (important!) shm.close() shm.unlink() # Remove from system
Sharing NumPy Arrays
pythonfrom multiprocessing import shared_memory import numpy as np # Create array in shared memory arr = np.array([1, 2, 3, 4, 5]) shm = shared_memory.SharedMemory(create=True, size=arr.nbytes) shared_arr = np.ndarray(arr.shape, dtype=arr.dtype, buffer=shm.buf) shared_arr[:] = arr # Copy data to shared memory # In another process, attach to existing shared memory shm2 = shared_memory.SharedMemory(name=shm.name) shared_arr2 = np.ndarray(arr.shape, dtype=arr.dtype, buffer=shm2.buf) # shared_arr2 now sees the same data!
ShareableList
pythonfrom multiprocessing.shared_memory import ShareableList # Create shareable list sl = ShareableList([1, 2, 3, 4, 5]) # Access by name from another process sl2 = ShareableList(name=sl.shm.name) sl2[0] = 99 # Visible to all processes # Clean up sl.shm.close() sl.shm.unlink()
Memory Management
pythonimport atexit # Ensure cleanup on exit shm = shared_memory.SharedMemory(create=True, size=1000) atexit.register(shm.unlink) atexit.register(shm.close)
Common Pitfalls
- Forgetting to call
unlink()— Shared memory blocks persist at the OS level. If you only callclose(), the block remains allocated until the system reboots. Always callunlink()from exactly one process (the creator). - Calling
unlink()from every process — Only the process that created the shared memory shouldunlink()it. Other processes should only callclose(). Unlinking from a process that is still using it causes segfaults. - Not registering cleanup with
atexit— If your program crashes before cleanup, the shared memory leaks. Registershm.close()andshm.unlink()withatexitas a safety net.
Best Practices
- Use
atexit.register()for cleanup safety — Registercloseandunlinkimmediately after creating shared memory so it is cleaned up even if the program exits unexpectedly. - Pass the
shm.namestring to child processes — Instead of passing the SharedMemory object, pass its name string. Child processes attach to the existing block by name, which works across all start methods.
Summary
shared_memory.SharedMemoryprovides zero-copy data sharing between processes, ideal for large arrays.- NumPy arrays can be mapped directly onto shared memory buffers for high-performance parallel processing.
ShareableListstores fixed-size lists of basic types in shared memory.- Always call
close()in every process andunlink()in exactly one process (the creator). - Use
atexit.register()to prevent shared memory leaks from crashes.
Code Examples
python
from multiprocessing import Process, shared_memory
import numpy as np
def worker(shm_name, shape, dtype):
# Attach to existing shared memory
shm = shared_memory.SharedMemory(name=shm_name)
arr = np.ndarray(shape, dtype=dtype, buffer=shm.buf)
# Modify in place
arr *= 2
shm.close()
if __name__ == '__main__':
# Create shared array
original = np.array([1, 2, 3, 4, 5], dtype=np.int64)
shm = shared_memory.SharedMemory(create=True, size=original.nbytes)
shared = np.ndarray(original.shape, dtype=original.dtype, buffer=shm.buf)
shared[:] = original
# Modify in subprocess
p = Process(target=worker, args=(shm.name, shared.shape, shared.dtype))
p.start()
p.join()
print(shared) # [2, 4, 6, 8, 10]
shm.close()
shm.unlink()