繁体   English   中英

在C ++中通过多个线程对数组的各个单元格进行修改是否安全?

[英]Is modification of various cells of an array by many threads safe in c++ (boost)

我有一个大小为n和n线程的数组,每个 i 线程只能对数组的 i 单元进行读/写操作。 我不使用任何内存锁。 这对于C ++ Boost线程安全吗? 这与处理器中的缓存有何关系,存储的是存储块,而不是单个值。 我猜想处理器的核心共享缓存,并且缓存内没有数据块的重复,因此,当对同一块进行多次修改(但是在各个位置)时,版本之间就不会发生冲突。

在任何现代处理器上,写入单独的内存位置(即使相邻)也不会造成危害。 否则,线程化将变得困难得多。

实际上,让线程“填充”数组的元素是一种相对常见的习惯用法:例如,这正是线性代数程序的典型线程实现所要做的。

写入单独的内存位置将正常工作,但是“错误共享”可能会导致性能问题,具体取决于数据访问的模式和特定的体系结构。

Oracle的OpenMP API文档对错误共享进行了很好的描述:

6.2.1什么是虚假共享?

大多数高性能处理器(例如UltraSPARC处理器)会在慢速存储器和CPU的高速寄存器之间插入缓存缓冲区。 访问内存位置会导致包含请求的内存位置的一部分实际内存(缓存行)被复制到缓存中。 直到系统确定有必要维持高速缓存和内存之间的一致性之前,可能在高速缓存之外满足对同一内存位置或其周围内存位置的后续引用。

但是,来自不同处理器的同一缓存行中各个元素的同时更新会使整个缓存行无效,即使这些更新在逻辑上彼此独立。 高速缓存行的单个元素的每次更新都会将该行标记为无效。 其他访问同一行中不同元素的处理器将看到标记为无效的行。 即使未修改访问的元素,他们也被迫从内存或其他地方获取该行的最新副本。 这是因为缓存一致性是基于缓存行而不是单个元素来维护的。 结果,互连通信量和开销将增加。 另外,在进行高速缓存行更新时,禁止访问该行中的元素。

这种情况称为虚假共享。 如果经常发生这种情况,则OpenMP应用程序的性能和可伸缩性将受到极大影响。

当满足以下所有条件时,错误共享会降低性能。

  • 共享数据由多个处理器修改。
  • 多个处理器更新同一高速缓存行中的数据。
  • 此更新非常频繁地发生(例如,紧密循环)。

请注意,循环中只读的共享数据不会导致错误共享。

在C ++ 11之前,标准根本不解决线程问题。 现在可以了。 在1.7节中可以找到此规则:

存储器位置是标量类型的对象,或者是全部具有非零宽度的相邻位域的最大序列。 [注意:语言的各种功能(例如引用和虚函数)可能涉及程序无法访问但由实现管理的其他内存位置。 —结束说明]两个或多个执行线程(1.10)可以更新和访问单独的内存位置,而不会互相干扰。

数组不是标量,但元素是。 因此,每个元素都是一个不同的内存位置,因此,不同的元素可以同时由不同的线程使用,而无需锁定或同步(只要最多一个线程访问任何给定的元素)。

但是,如果存储在同一高速缓存行中的数据是由不同的线程写入的,则将为高速缓存一致性协议带来大量的额外工作。 考虑添加填充或交换数据布局,以便线程使用的所有变量都相邻存储。 (结构数组而不是数组结构)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM