在java中,多线程矩阵加法比单线程版本耗时更长
Java中的并发性让我的手变得脏兮兮的,并且在多线程处理中遇到了这个相当常见的问题。我有一段代码(如下所示),它只取两个矩阵m1和m2,并将在java中,多线程矩阵加法比单线程版本耗时更长,java,multithreading,concurrency,executorservice,Java,Multithreading,Concurrency,Executorservice,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 fin
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);
}
}
);
}
}
for(int i=0;i
数组的类型为ArrayLists
,其中每个嵌套的ArrayList
描述一列。它们的维度为numRows
xnumCols
。我测量了此操作的时间,将一对随机生成的大小为10000 x 10000的矩阵求和,发现单线程版本需要123秒,多线程(6核intel i7上的11个线程)版本大约需要300秒
在这种情况下,我选择使用ArrayList,因为它们允许不安全的并发访问,即我可以同时修改ArrayList的不同部分。然而,这并没有提供我所期望的任何额外的加速。我想为什么我没有看到加速是因为以下原因:
这些猜测有意义吗?我可能看不到任何其他解释?您有两个主要问题:
ArrayLists
)使用的数据结构非常低效,数据局部性差,访问单个项的开销很大Integer
值,那么当列表扩展并覆盖早期数据时,您将丢失数据
一个有效的办法是:
Runnable
对整个部分执行加法。这意味着,如果您有8个内核和8个工作线程,那么每个线程将处理一个Runnable,该Runnable将在矩阵的12.5%上执行加法int[]]
,或者更好,使用int[]
并对行*宽+列的索引进行自己的计算。这提供了更好的数据局部性,并且不进行任何自动装箱和取消装箱,从而提高了速度。使用int[]
特别适合于添加矩阵,因为您可以将矩阵视为数组-您不需要了解行和列,只需result[i]=m1[i]+m2[i]代码>
这些猜测对我来说很有意义。所有并行都会创建上下文切换,因为您必须管理N个并行上下文并相应地进行切换,而且过度并行可能会造成人为瓶颈,资源可能不得不平衡多个消费者的需求,使其无法以合理的速率提供。我对Java并发并不完全熟悉,但是,您不是为矩阵中的每个元素都创建了一个独特的可运行的,这会导致显著的减速吗?我现在尝试了这个方法,使用ExecutorService(11个线程),每行10000个元素创建一个新任务,需要约15-20秒(有些解决方案需要约5-10秒,但包含并发修改的问题,我对调试不感兴趣)。使用并行流API大约需要25-30秒。我不确定我在这两种情况下都采取了最佳方法,但它明显优于您的结果。谢谢!你的建议明显加快了速度。对于每个正好包含10亿个整数的矩阵,单个线程花费的时间为7346毫秒,而12个线程花费的时间为1567毫秒。