我有一个int array[100]
,我想要5个线程来计算所有数组元素的总和。
每个线程在其专用范围内迭代20个元素,并将总和写入全局sum
变量。
这是必需的互斥体吗?由于所有线程都从独立源读取,因此不需要同步。
for(i=offset; i<offset+range; i++){
// not used pthread_mutex_lock(&mutex);
sum += array[i];
// not used pthread_mutex_unlock(&mutex);
}
这是否会导致不可预测的行为或操作系统是否实际处理此问题?
在这种情况下,是否可以省略互斥锁?我注意到没有它,这些算法运行得更快。
答案 0 :(得分:3)
是的,您需要同步,因为所有线程都在同时修改sum
。这是一个例子:
您有4个元素[a1, a2, a3, a4]
和2个帖子t1
以及t2
和sum
的数组。首先,让我们说t1
获取值a1
并将其添加到sum
。但它不是原子操作,所以他将sum
(它的0)的当前值复制到他的本地空间,让它称之为t1_s
,添加a1
然后写{{1} }}。但同时sum = t1_s
做同样的事情,他得t2
值{0,因为sum
尚未完成操作)到t1
,添加{{1}并写入t2_s
。因此,我们获得了a3
sum
sum
a3
的{{1}}。这称为数据竞赛。
有多种解决方案:
a1 + a3
,但正如您所提到的那样,它可能很慢,因为互斥锁是昂贵的,所有其他线程都在等待它。mutex
,最后使用互斥锁将所有这些总和添加到共享变量sum_local
。我想它会更快(但需要检查)。然而,正如@gavinb提到的那样,只有大量数据才有意义。
答案 1 :(得分:3)
我有一个int数组[100],我想要5个线程来计算所有数组元素的总和。每个线程在其专用范围内迭代20个元素,并将总和写入全局和变量。
首先,值得指出的是,处理这些少量数据的这么多线程的开销可能不是一个优势。创建线程,序列化访问以及等待它们完成是有代价的。使用这么小的数据集,优化良好的顺序算法可能更快。用不同数量的线程来衡量加速是一个有趣的练习。
这是必需的互斥体吗?由于所有线程都从独立源读取,因此不需要同步。
是的 - array
变量的读取是独立的,但更新 sum
变量不是,因此您需要一个互斥锁来序列化对{{1}的访问权限根据你上面的描述。
然而,这是计算总和的一种非常低效的方式,因为每个线程将竞争(并且等待,因此浪费时间)来访问增量sum
。如果计算每个子集的中间总和(如@Werkov也提到的那样),那么等待它们完成并添加中间总和以创建最终总和,不会有读写或写入的争用,因此您不需要互斥锁并且每个线程都可以尽快运行。然后,性能的限制因素可能是内存访问模式和缓存行为。
这是否会导致不可预测的行为或操作系统是否实际处理此问题?
是的,当然。操作系统不会为您处理此问题,因为它无法预测您何时/何时访问内存的不同部分,以及出于何种原因。只要其中任何一个数据可能正在写入数据,就必须在线程之间保护共享数据。因此,当线程相互更新sum
时,你几乎肯定会得到错误的结果。
在这种情况下,是否可以省略互斥锁?我注意到没有它,这些算法运行得更快。
不,绝对不是。它可能运行得更快,但几乎肯定不会给你正确的结果!
答案 2 :(得分:0)
在可以以这种方式对数据进行分区的情况下,跨分区不存在依赖性(即读/写)。在您的示例中,sum
变量具有依赖关系,并且互斥锁是必需的。但是,您可以为每个线程使用部分求和累加器,然后只需对这些子结果求和,而无需使用互斥锁。
当然,您无需手动完成此操作。有各种各样的实现,例如参见OpenMP&parallel for and reduction。