23

我经常听说访问进程之间的共享内存段与访问线程之间的进程内存相比没有性能损失。换句话说,多线程应用程序不会比使用共享内存的一组进程更快(不包括锁定或其他同步问题)。

但我有我的疑问:

1) shmat() 将本地进程虚拟内存映射到共享段。必须为每个共享内存地址执行此转换,并且可能会带来很大的成本。在多线程应用程序中,不需要额外的转换:所有 VM 地址都被转换为物理地址,就像在不访问共享内存的常规进程中一样。

2)共享内存段必须由内核以某种方式维护。例如,当所有附加到 shm 的进程都被关闭时,shm 段仍然处于启动状态,并且最终可以被新启动的进程重新访问。可能存在与 shm 段上的内核操作相关的一些开销。

多进程共享内存系统是否与多线程应用程序一样快?

4

5 回答 5

14

1) shmat() 将本地进程虚拟内存映射到共享段。必须为每个共享内存地址执行此转换,并且相对于 shm 访问的数量而言,这可能会带来很大的成本。在多线程应用程序中,不需要额外的转换:所有 VM 地址都被转换为物理地址,就像在不访问共享内存的常规进程中一样。

除了设置共享页面的初始成本(在调用的进程中填充页表)之外,与常规内存访问相比没有任何开销shmat()- 在大多数 Linux 风格中,每 4KB 共享内存为 1 页(4 或 8 字节) .

无论页面是共享分配还是在同一进程中分配,(对于所有相关比较)都是相同的成本。

2)共享内存段必须由内核以某种方式维护。我不知道“以某种方式”在性能方面意味着什么,但是例如,当所有附加到 shm 的进程都被删除时,shm 段仍然处于启动状态,并且最终可以被新启动的进程重新访问。必须至少有一定程度的开销与内核在 shm 段的生命周期内需要检查的事情有关。

无论是否共享,每一页内存都附有一个“结构页”,其中包含有关该页的一些数据。其中一项是引用计数。当一个页面被分配给一个进程时[无论是通过“shmat”还是其他机制],引用计数都会增加。当它通过某种方式被释放时,引用计数会减少。如果递减计数为零,则页面实际上被释放 - 否则“不会再发生任何事情”。

与分配的任何其他内存相比,开销基本上为零。无论如何,相同的机制用于页面的其他目的-例如,您有一个也被内核使用的页面-并且您的进程死了,内核需要知道在内核释放该页面之前不要释放该页面以及用户进程。

创建“分叉”时也会发生同样的事情。当一个进程被分叉时,父进程的整个页表本质上被复制到子进程中,并且所有页面都是只读的。每当发生写入时,内核都会发生错误,从而导致该页面被复制 - 因此该页面现在有两个副本,并且执行写入的进程可以修改它的页面,而不会影响其他进程。一旦子(或父)进程死亡,当然所有页面仍然由两个进程拥有[例如从未被写入的代码空间,可能还有一堆从未被触及的公共数据等]显然不能释放,直到两个进程都“死”。再一次,引用计数的页面在这里很有用,因为我们只计算每个页面上的引用计数,

共享库也会发生完全相同的事情。如果一个进程使用共享库,它将在该进程结束时被释放。但是,如果两个、三个或 100 个进程使用同一个共享库,那么代码显然必须保留在内存中,直到不再需要该页面为止。

所以,基本上,整个内核中的所有页面都已经被引用计数了。开销很少。

于 2013-01-24T23:10:10.510 回答
5

如果考虑当两个线程或进程访问同一内存时在微电子级别发生的事情,就会产生一些有趣的结果。

有趣的一点是 CPU 的架构如何允许多个内核(因此线程和进程)访问相同的内存。这是通过 L1 缓存完成的,然后是 L2、L3,最后是 DRAM。所有这一切的控制器之间必须进行大量的协调。

对于具有 2 个或更多 CPU 的机器,这种协调通过串行总线进行。如果比较两个内核访问同一内存时发生的总线流量,以及将数据复制到另一块内存时发生的总线流量,则流量大致相同。

因此,根据两个线程在机器中运行的位置,复制数据与共享数据的速度损失可能很小。

复制可能是 1) memcpy,2) 管道写入,3) 内部 DMA 传输(英特尔芯片现在可以做到这一点)。

内部 DMA 很有趣,因为它需要零 CPU 时间(天真的 memcpy 只是一个循环,实际上需要时间)。因此,如果一个人可以复制数据而不是共享数据,并且使用内部 DMA 来做到这一点,那么您就可以像共享数据一样快。

代价是更多的 RAM,但回报是 Actor 模型编程之类的东西正在发挥作用。这是一种消除程序中使用信号量保护共享内存的所有复杂性的方法。

于 2017-08-19T06:04:12.747 回答
4

设置共享内存需要内核进行一些额外的工作,因此从进程中附加/分离共享内存区域可能比常规内存分配慢(或者它可能不是......我从未对此进行基准测试)。但是,一旦它附加到您的进程虚拟内存映射,共享内存就与任何其他用于访问的内存没有什么不同,除非您有多个处理器竞争相同的高速缓存行大小的块。因此,一般而言,对于大多数访问,共享内存应该与任何其他内存一样快,但是,根据您放在那里的内容以及访问它的不同线程/进程的数量,您可能会因特定的使用模式而减慢速度。

于 2013-01-24T21:34:55.730 回答
2

除了附加(shmat)和分离(shmdt)共享内存的成本外,访问速度应该同样快。换句话说,它应该很快,因为硬件支持它。每次访问都不应该有额外层形式的开销。

同步也应该同样快。例如,在 Linux 中,futex可用于进程和线程。原子变量也应该可以正常工作。

只要连接/分离成本不占主导地位,使用工艺就应该没有缺点。但是,线程更简单,并且如果您的进程大多是短暂的,则附加/分离开销可能是一个问题。但是,由于创建流程的成本会很高,无论如何,如果您担心性能,这不应该是一种可能的情况。

最后,这个讨论可能很有趣:shmat 和 shmdt 贵吗?. (警告:它已经过时了。我不知道从那以后情况是否发生了变化。)

这个相关问题也可能会有所帮助:IPC 的共享内存和线程的共享内存有什么区别? (简短的回答:不多。)

于 2013-01-24T21:49:00.623 回答
2

共享内存的成本与其“元”更改的数量成正比:分配、释放、进程退出……

内存访问次数没有影响。访问共享段的速度与访问其他任何地方一样快。

CPU 执行页表映射。在物理上,CPU 不知道映射是共享的。

如果您遵循最佳实践(即很少更改映射),您将获得与进程专用内存基本相同的性能。

于 2013-01-24T22:29:54.973 回答