多线程和CPU缓存

我使用多线程在C中实现图像过滤操作,并尽可能优化。 我有一个问题,但:如果一个内存被线程0访问,并且如果相同的内存被线程1访问,它会从缓存中获取它吗? 这个问题源于这两个线程可能运行到CPU的两个不同内核的可能性。 所以另一种说法是:所有内核都共享相同的缓存吗?

假设我有如下的内存布局

int output [100];

假设有2个CPU核心,因此我产生了两个线程同时工作。 一种方案可能是将内存分成两个块,即0-49和50-99,并让每个线程在每个块上工作。 另一种方法可以让线程0在偶数索引上工作,如0 2 4等等,而另一个线程在奇数索引上工作,如1 3 5 ....这种后来的技术更容易实现(特别适用于3D数据),但我不确定是否可以通过这种方式有效地使用缓存。


一般来说,共享重叠的内存区域是一个好主意,就像一个线程处理0,2,4 ...和其他进程1,3,5 ...一样。尽管一些体系结构可能支持这种情况,但大多数体系结构不会,而且您可能无法指定代码将在哪些机器上运行。 此外,操作系统可以自由地将您的代码分配给任何它喜欢的内核(一个,两个在同一个物理处理器上,或两个在不同处理器上的内核)。 而且每个CPU通常都有独立的第一级缓存,即使它在同一个处理器上。

在大多数情况下,0,2,4 ... / 1,3,5 ...会极大地降低性能,可能比单个CPU慢。 香草酱“消除假分享”很好地证明了这一点。

使用该方案[... n / 2-1]和[n / 2 ... n]将在大多数系统上扩展得更好。 它甚至可能导致超级线性性能,因为所有CPU的总和的高速缓存大小可以被使用。 使用的线程数量应始终可配置,并应默认为找到的处理器内核数量。


这个问题的答案强烈依赖于体系结构和高速缓存级别,以及线程实际运行的位置。

例如,最近的英特尔多核心CPU具有每个核心的L1缓存和在相同CPU包中的核心之间共享的L2缓存; 但是不同的CPU软件包将拥有自己的L2高速缓存。

即使在线程在一个包中的两个内核上运行的情况下,如果两个线程都访问同一个缓存行中的数据,那么您的缓存行将在两个L1缓存之间弹跳。 这是非常低效的,你应该设计你的算法来避免这种情况。


有几条评论询问了如何避免这个问题。

其实并不是特别复杂 - 你只是想避免两个线程同时尝试访问位于同一缓存行的数据,至少有一个线程正在写入数据。 (只要所有线程只读取数据,就没有问题 - 在大多数体系结构中,只读数据可以存在于多个高速缓存中)。

要做到这一点,您需要知道缓存行大小 - 这取决于体系结构,但目前大多数x86和x86-64系列芯片使用64字节缓存行(请参阅其他体系结构的体系结构手册)。 你还需要知道你的数据结构的大小。

如果您要求编译器将感兴趣的共享数据结构与64字节边界(例如,数组output )对齐,那么您知道它将从高速缓存行开始时开始,并且还可以计算后续缓存行边界是。 如果你的int是4个字节,那么每个cacheline将包含8个int值。 只要阵列从缓存线边界开始,则output[0]output[7]将位于一条缓存线上,并在下一个位置output[8]output[15] 。 在这种情况下,您可以设计算法,使得每个线程在一个8倍数的相邻int值块上工作。

如果你正在存储复杂的struct类型而不是普通的int ,那么pahole工具将会被使用。 它将分析编译后的二进制文件中的struct类型,并显示布局(包括填充)和总大小。 然后,您可以使用此输出来调整struct ,例如,您可能需要手动添加一些填充以便您的struct是缓存行大小的倍数。

在POSIX系统上, posix_memalign()函数可用于分配具有指定对齐方式的内存块。


我可能会误解,但核心的缓存是否共享取决于CPU的实现。 您必须查看制造商页面上的技术表格,以检查CPU中的每个核心是否都有自己的缓存或缓存是否共享。

我正在为安全公司进行图像处理,并且有时在线程上运行批处理操作后,我们遇到了损坏的图像。 经过长时间的调查,我们得出结论:缓存是在CPU Core之间共享的,在极少数情况下,数据被覆盖或被不正确的数据替换。

无论这是需要考虑的事情,还是我无法接受的罕见事件。

链接地址: http://www.djcxy.com/p/82357.html

上一篇: Multiple threads and CPU cache

下一篇: What is the purpose of CS and IP registers in Intel 8086 assembly?