[英]Multithreaded matrix addition taking longer than single threaded version in java
在 Java 中弄脏了我的手,并在多线程中遇到了这个相当常见的问题。 我有一段代码(如下),它只需要两个矩阵 m1 和 m2,并将m1[i][j]
和m2[i][j]
的总和写入result[i][j]
。
for(int i = 0; i < numCols ; i++) {
for(int j = 0 ; j < numRows ; j++) {
int finalI = i;
int finalJ = j;
executorService.execute(
new Runnable() {
@Override
public void run() {
ArrayList<Integer> v1 = m1.get(finalI);
Integer m1Val = v1.get(finalJ);
ArrayList<Integer> v2 = m2.get(finalI);
Integer m2Val = v2.get(finalJ);
result.get(finalI).add(finalJ, m1Val + m2Val);
}
}
);
}
}
arrays 属于ArrayLists<ArrayList<Integer>>
类型,其中每个嵌套的ArrayList
描述一个列。 它们的尺寸为numRows
x numCols
。 我测量了这个操作的时间,对一对随机生成的大小为 10000 x 10000 的矩阵求和,发现单线程版本花了我 123 秒,而多线程(6 核英特尔 i7 上的 11 个线程)版本花了我大约300s。
在这种情况下,我选择使用 ArrayList,因为它们允许不安全的并发访问,即我可以同时修改 ArrayList 的不同部分。 但是,这并没有提供我所期望的任何额外的加速。 我对为什么看不到加速的猜测是因为以下原因:
这些猜测有意义吗? 我可能没有看到任何其他解释?
你有两个主要问题:
ArrayLists<ArrayList<Integer>>
) 使用非常低效的数据结构,其数据局部性较差并且访问单个项目的开销很大。1 和 2 都会导致很多额外的 CPU 周期被完全浪费掉; 它们还导致数据局部性差,超过必要的缓存未命中。
此外,您会得到不正确的结果,因为您使用的是非线程安全的数据结构(“在这种情况下为 ArrayList,因为它们允许不安全的并发访问”)来收集结果; 如果它没有为每个结果预先填充Integer
值,那么随着列表扩展并覆盖早期数据,您将丢失数据。
一种有效的方法是:
Runnable
对该整个部分执行加法。 这意味着,如果您有 8 个内核和 8 个工作线程,那么每个线程将处理一个 Runnable,并且该 Runnable 对矩阵的 12.5% 执行加法。int[][]
,或者更好的是,使用int[]
并对row * width + col
的索引进行自己的计算。 这提供了更好的数据局部性,并且不进行任何自动装箱和拆箱,从而提高了速度。 使用int[]
特别适合添加矩阵,因为您可以将矩阵视为一个数组 - 您不需要了解行和列,只需result[i] = m1[i] + m2[i];
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.