首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >多进程性能优化:共享内存 vs 普通多进程

多进程性能优化:共享内存 vs 普通多进程

作者头像
Michael阿明
发布2026-03-25 13:27:21
发布2026-03-25 13:27:21
650
举报

文章目录

  • 1. 背景介绍
  • 2. 实验设计
    • 数据准备
    • 实现方式
  • 3. 实验代码
  • 4. 实验结果
    • 内存使用对比
    • 性能对比
  • 5. 分析与讨论
    • 内存使用分析
    • 性能分析
  • 6. 结论
    • 共享内存的优势
    • 适用场景
  • 总结

在多进程编程中,数据传递和内存管理是影响性能的关键因素。本文通过对比两种多进程实现方式(共享内存和普通多进程),分析它们在内存使用和性能上的差异,并提供实验数据支持。

1. 背景介绍

在多进程编程中,数据传递通常有两种方式:

  1. 普通多进程:每个子进程独立处理一份数据副本。
  2. 共享内存:所有子进程共享同一块内存区域,避免数据的多次拷贝。

我们通过一个简单的实验来对比这两种方式的性能和内存使用情况。

2. 实验设计

数据准备

  • 创建一个大小为 76 MB 的 NumPy 数组(np.ones(10000000, dtype=np.int64))。
  • 使用 5 个子进程对数组进行相同的修改操作(每个进程对数组元素加一个常数)。

实现方式

  1. 共享内存版本
    • 使用 multiprocessing.shared_memory 创建共享内存。
    • 子进程通过共享内存名称访问数据。
    • 使用锁(Lock)确保操作的互斥性。
  2. 普通多进程版本
    • 每个子进程接收一份数据副本。
    • 使用队列(Queue)收集子进程的处理结果。

3. 实验代码

以下是实验的完整代码:

代码语言:javascript
复制
import multiprocessing
import numpy as np
import time
import psutil
import os
from multiprocessing import shared_memory, Lock, Queue

def get_process_memory():
    """获取当前进程的内存使用情况"""
    process = psutil.Process(os.getpid())
    memory_info = process.memory_info()
    return memory_info.rss / 1024 / 1024  # 转换为MB

def worker(shm_name, shape, dtype, lock, add_value):
    """
    子进程通过共享内存名称连接共享内存,并在获取互斥锁后对数据进行安全修改。
    """
    # 打印子进程内存使用
    print(f"进程 {multiprocessing.current_process().name} 内存使用: {get_process_memory():.2f} MB")
    
    # 连接已有的共享内存块
    existing_shm = shared_memory.SharedMemory(name=shm_name)
    # 利用共享内存创建一个 NumPy 数组视图
    arr = np.ndarray(shape, dtype=dtype, buffer=existing_shm.buf)
    
    # 通过锁确保操作的互斥性,避免多个进程同时修改数据
    with lock:
        # 此处为示例:每个进程对数组所有元素加上一个常数 add_value
        print(f"进程 {multiprocessing.current_process().name} 正在修改数据")
        arr += add_value
    
    # 打印子进程修改后内存使用
    print(f"进程 {multiprocessing.current_process().name} 修改后内存使用: {get_process_memory():.2f} MB")
    
    existing_shm.close()

def normal_worker(data, output_queue, add_value):
    """
    普通多进程方式的工作函数,直接接收数据而不是通过输入队列
    """
    # 打印子进程内存使用
    print(f"进程 {multiprocessing.current_process().name} 内存使用: {get_process_memory():.2f} MB")
    
    try:
        # 直接处理传入的数据
        print(f"进程 {multiprocessing.current_process().name} 正在修改数据")
        result = data + add_value
        print(f"进程 {multiprocessing.current_process().name} 修改完成")
        output_queue.put(result)  # 将结果放入输出队列
        print(f"进程 {multiprocessing.current_process().name} 结果已放入队列")
    except Exception as e:
        print(f"进程 {multiprocessing.current_process().name} 出错: {e}")
    
    # 打印子进程修改后内存使用
    print(f"进程 {multiprocessing.current_process().name} 修改后内存使用: {get_process_memory():.2f} MB")
    
    print(f"进程 {multiprocessing.current_process().name} 正在退出")

def run_shared_memory_version(data, num_processes=5):
    """
    运行共享内存版本的测试
    """
    print(f"主进程内存使用 (共享内存版本开始): {get_process_memory():.2f} MB")
    start_time = time.time()
    
    # 创建共享内存块
    shm = shared_memory.SharedMemory(create=True, size=data.nbytes)
    shared_array = np.ndarray(data.shape, dtype=data.dtype, buffer=shm.buf)
    shared_array[:] = data[:]  # 初始化共享内存中的数据
    
    lock = Lock()
    processes = []
    
    for i in range(num_processes):
        p = multiprocessing.Process(
            target=worker,
            args=(shm.name, data.shape, data.dtype, lock, i+1)
        )
        processes.append(p)
        p.start()
    
    for p in processes:
        p.join()
    
    result = np.copy(shared_array)
    
    # 清理共享内存
    shm.close()
    shm.unlink()
    
    end_time = time.time()
    print(f"主进程内存使用 (共享内存版本结束): {get_process_memory():.2f} MB")
    return result, end_time - start_time

def run_normal_version(data, num_processes=5):
    """
    运行普通多进程版本的测试
    """
    print(f"主进程内存使用 (普通版本开始): {get_process_memory():.2f} MB")
    start_time = time.time()
    output_queue = Queue()
    processes = []
    
    # 启动所有进程
    for i in range(num_processes):
        p = multiprocessing.Process(
            target=normal_worker,
            args=(data, output_queue, i+1)
        )
        processes.append(p)
        p.start()
        print(f"启动进程 {i+1}")
    
    # 立即开始收集结果,不等待进程完成
    results = []
    for i in range(num_processes):
        print(f"正在等待第 {i+1} 个结果...")
        result = output_queue.get()  # 这会阻塞直到有数据可用
        print(f"收到第 {i+1} 个结果")
        results.append(result)
    
    # 收集完结果后,等待所有进程完成
    for p in processes:
        p.join()
        
    print("所有进程都已完成")
    final_result = sum(results) - data * (num_processes-1)  # 计算最终结果
    
    end_time = time.time()
    print(f"主进程内存使用 (普通版本结束): {get_process_memory():.2f} MB")
    return final_result, end_time - start_time

if __name__ == '__main__':
    # 首先安装必要的包
    # 可以在终端运行: pip install psutil
    
    print(f"初始主进程内存使用: {get_process_memory():.2f} MB")
    
    # 创建测试数据
    data = np.ones(10000000, dtype=np.int64)  # 使用更大的数组来测试性能差异
    print("初始数据大小:", data.nbytes / 1024 / 1024, "MB")
    
    # 运行共享内存版本
    print("\n运行共享内存版本...")
    shared_result, shared_time = run_shared_memory_version(data)
    print("共享内存版本耗时:", shared_time, "秒")
    print("共享内存版本结果:", shared_result[:10], "...")  # 只显示前10个元素
    
    # 运行普通多进程版本
    print("\n运行普通多进程版本...")
    normal_result, normal_time = run_normal_version(data)
    print("普通多进程版本耗时:", normal_time, "秒")
    print("普通多进程版本结果:", normal_result[:10], "...")  # 只显示前10个元素

    assert np.array_equal(shared_result, normal_result), "结果不一致!"
    print("结果一致性检查通过!")
    
    print("\n性能对比:")
    print(f"共享内存版本比普通多进程版本快 {normal_time/shared_time:.2f} 倍")
    print(f"内存使用总结: 共享内存版本可以节省内存,因为所有进程共享同一块内存")

4. 实验结果

内存使用对比

版本

主进程内存 (结束) (MB)

共享内存版本

182.00

普通多进程版本

565.17

性能对比

版本

耗时 (秒)

性能提升倍数

共享内存版本

0.29

-

普通多进程版本

3.30

慢 11.4 倍

5. 分析与讨论

内存使用分析

  1. 共享内存版本
    • 子进程共享同一块内存,避免了数据的多次拷贝。
    • 主进程内存使用从初始的 30.56 MB 增加到 182.00 MB,主要由于共享内存的创建和锁的管理。
  2. 普通多进程版本
    • 每个子进程需要独立处理一份数据副本,导致内存使用显著增加。
    • 主进程内存从初始的 30.56 MB 增加到 565.17 MB,是共享内存版本的 3 倍

性能分析

  1. 共享内存版本
    • 避免了数据的多次拷贝,减少了 I/O 操作。
    • 耗时 0.29 秒,性能表现优异。
  2. 普通多进程版本
    • 每个子进程需要独立处理数据副本,增加了数据传输和拷贝的开销。
    • 耗时 3.30 秒,是共享内存版本的 11.4 倍

6. 结论

共享内存的优势

  1. 内存使用效率高:所有子进程共享同一块内存,避免了数据的多次拷贝。
  2. 性能提升显著:减少了数据传输和拷贝的开销,大幅提升了处理速度。
  3. 适合大规模数据处理:在处理大规模数据时,共享内存可以显著节省内存并提高性能。

适用场景

  • 大规模数据处理:如图像处理、科学计算等需要处理大量数据的场景。
  • 高并发场景:需要多个进程同时访问和修改同一数据集的场景。

总结

通过对比共享内存和普通多进程的实现方式,我们发现共享内存版本在内存使用和性能上具有显著优势。 对于需要处理大规模数据或多进程协作的场景,推荐使用共享内存来优化性能和内存使用。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2025-04-16,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Michael阿明 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. 背景介绍
  • 2. 实验设计
    • 数据准备
    • 实现方式
  • 3. 实验代码
  • 4. 实验结果
    • 内存使用对比
    • 性能对比
  • 5. 分析与讨论
    • 内存使用分析
    • 性能分析
  • 6. 结论
    • 共享内存的优势
    • 适用场景
  • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档