在c++中,多个线程对数组的不同单元的修改是否安全(boost)

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

本文关键字:修改 单元 是否 安全 boost 线程 数组 c++      更新时间:2023-10-16

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

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

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

写入单独的内存位置将正常工作,但是'false sharing'可能会导致性能问题,这取决于数据访问的模式和特定的体系结构。

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

6.2.1什么是虚假共享?

大多数高性能处理器,如UltraSPARC处理器,在慢速存储器和高速寄存器之间插入一个高速缓存缓冲区CPU的状态。访问内存位置会导致实际内存的切片包含被请求的内存位置的内存(缓存行)复制到缓存中。对同一内存的后续引用位置或它周围的人大概可以满意地取出缓存直到系统确定有必要保持一致性在缓存和内存之间。

但是,在同一缓存中同时更新单个元素来自不同处理器的行使整个缓存行无效,尽管这些更新在逻辑上是相互独立的。缓存行的单个元素的每次更新都将该行标记为无效的。其他处理器访问同一元素中的不同元素请查看标记为无效的行。他们被迫多取一些钱最近从内存或其他地方拷贝的行,即使访问的元素未被修改。这是因为缓存一致性是在缓存行基础上维护的,而不是针对单个的元素。因此,互连将会增加交通和头顶。此外,在缓存行更新时进度,禁止访问行中的元素。

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

当出现以下所有情况时,虚假共享会降低性能条件发生。

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

注意,在循环中共享的只读数据不会导致假共享。

在c++ 11之前,标准根本没有解决线程问题。现在它做到了。该规则见于第1.7节:

内存位置要么是标量类型的对象,要么是宽度都不为零的相邻位域的最大序列。[注:语言的各种特性,如引用和虚函数,可能涉及到程序无法访问但由实现管理的额外内存位置。]两个或多个线程的执行(1.10)可以更新和访问不同的内存位置,而不会相互干扰。

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

但是,如果存储在同一缓存行的数据由不同的线程写入,则会给缓存一致性协议带来大量额外的工作。考虑添加填充或交换数据布局,以便线程使用的所有变量都以邻接方式存储。