我经常听到,与访问线程之间的进程内存相比,访问进程之间的共享内存段没有性能损失。换句话说,多线程应用程序不会比使用共享内存的一组进程更快(不包括锁定或其他同步问题)。
但我有疑虑:
1)shmat()将本地进程虚拟内存映射到共享段。必须为每个共享存储器地址执行此转换,并且可能代表显着的成本。在多线程应用程序中,不需要额外的转换:所有VM地址都转换为物理地址,就像在不访问共享内存的常规进程中一样。
2)内核必须以某种方式维护共享内存段。例如,当连接到shm的所有进程都被删除时,shm段仍然处于运行状态,并且最终可以被新启动的进程重新访问。在shm段上可能存在与内核操作相关的一些开销。
多进程共享内存系统是否与多线程应用程序一样快?
答案 0 :(得分:10)
1)shmat()将本地进程虚拟内存映射到共享 分割。必须为每个共享内存执行此转换 地址并且可以代表相对于数字的显着成本 shm访问。在多线程应用程序中没有额外的 需要翻译:所有VM地址都转换为物理地址 地址,如在不访问共享内存的常规进程中。
除了设置共享页面的初始成本之外,与常规内存访问相比没有开销 - 在调用shmat()
的过程中填充页表 - 在大多数Linux版本中都是1页(4或每4KB共享内存8个字节。
(对所有相关比较)无论页面是共享还是在同一进程内,都是相同的成本。
2)内核必须以某种方式维护共享内存段。 我不知道“不知何故”在表演方面意味着什么,但是 例如,当连接到shm的所有进程都被删除时 shm段仍然处于运行状态,最终可以重新访问 开始进程。必须至少有一定程度的开销 与内核在生命周期内需要检查的内容有关 shm段。
无论是否共享,每个内存页面都附有一个“结构页面”,其中包含有关该页面的一些数据。其中一项是参考计数。当页面被赋予进程[无论是通过“shmat”还是某种其他机制]时,引用计数递增。当通过某种方式释放它时,引用计数递减。如果递减的计数为零,则实际释放页面 - 否则“不再发生任何事情”。
与分配的任何其他内存相比,开销基本上为零。相同的机制用于页面的其他目的 - 例如你有一个内核也使用的页面 - 你的进程死了,内核需要知道不要释放该页面,直到它被内核发布为以及用户进程。
创建“fork”时会发生同样的事情。当一个进程被分叉时,父进程的整个页表基本上被复制到子进程中,所有页面都是只读的。无论何时发生写入,内核都会发生错误,导致该页面被复制 - 因此现在该页面有两个副本,执行写入的过程可以修改它的页面,而不会影响其他进程。一旦子(或父)进程终止,当然所有仍由BOTH进程拥有的页面[例如永远不会被编写的代码空间,可能是一堆从未被触及的常见数据等]显然不能直到两个进程都“死”为止。所以再次,引用计数页面在这里很有用,因为我们只计算每个页面上的引用计数,并且当ref-count为零时 - 也就是说,当使用该页面的所有进程都释放它时 - 页面是实际上作为“有用的页面”返回。
共享库完全相同。如果一个进程使用共享库,则该进程结束时将释放该进程。但是,如果两个,三个或100个进程使用相同的共享库,则代码显然必须保留在内存中,直到不再需要该页面为止。
所以,基本上,整个内核中的所有页面都已经被引用计数了。开销很小。
答案 1 :(得分:3)
设置共享内存需要内核的一些额外工作,因此从进程中附加/分离共享内存区域可能比常规内存分配慢(或者可能不是......我从未对此进行基准测试)。但是,一旦它附加到您的进程虚拟内存映射,共享内存与访问任何其他内存没有什么不同,除非您有多个处理器争用相同的缓存行大小的块。因此,通常情况下,共享内存应该与大多数访问的任何其他内存一样快,但是,根据您放在那里的内容,以及有多少不同的线程/进程访问它,您可以减慢特定使用模式的速度。 / p>
答案 2 :(得分:3)
如果考虑到当两个线程或进程访问同一内存时微电子级正在发生的事情,那么会产生一些有趣的结果。
关注点是CPU的体系结构如何允许多个内核(因此线程和进程)访问同一内存。这是通过L1缓存完成的,然后是L2,L3,最后是DRAM。所有这些控制器之间必须进行大量的协调。
对于具有2个CPU或更多CPU的机器,该协调通过串行总线进行。如果比较两个内核访问同一内存时发生的总线流量,并且数据复制到另一个内存时,则流量大致相同。
因此,根据机器中两个线程的运行位置,复制数据与共享数据的速度可能会很小。
复制可能是1)memcpy,2)管道写入,3)内部DMA传输(英特尔芯片最近可以这样做)。
内部DMA很有意思,因为它需要零CPU时间(天真的memcpy只是一个循环,实际上需要时间)。因此,如果可以复制数据而不是共享数据,并且使用内部DMA执行此操作,则可以像共享数据一样快。
惩罚更多的是RAM,但回报是像Actor模型编程这样的东西在起作用。这是一种消除程序中使用信号量保护共享内存的所有复杂性的方法。
答案 3 :(得分:2)
除了附加(shmat
)和分离(shmdt
)共享内存的成本之外,访问应该同样快。换句话说,它应该是快速的,因为硬件支持它。每次访问都不应有额外层的开销。
同步也应该同样快。例如,在Linux中,futex可用于进程和线程。原子变量也应该可以正常工作。
只要附加/拆卸成本不占主导地位,使用过程就没有缺点。但是,线程更简单,如果您的进程大多是短暂的,那么附加/分离开销可能是个问题。但是,由于创建流程的成本很高,无论如何,如果您担心性能,这不应该是一种可能的情况。
最后,这个讨论可能很有趣:Are shmat and shmdt expensive?。 (警告:这已经过时了。我不知道情况是否已经发生变化。)
此相关问题也可能有所帮助:What's the difference between shared memory for IPCs and threads' shared memory? (简答:不多。)
答案 4 :(得分:2)
共享内存的成本与“meta”更改的数量成正比:分配,释放,进程退出......
内存访问次数不起作用。对共享段的访问速度与其他任何位置的访问速度一样快。
CPU执行页表映射。在物理上,CPU不知道映射是共享的。
如果您遵循最佳实践(很少更改映射),您将获得与进程专用内存基本相同的性能。