例如,有两个巨大(长度为 2-3 百万)的数组float []
或double []
. 需要他们很快加起来。怎么做?有没有这方面的图书馆?
5 回答
使用线程数等于处理器内核数的固定线程池。提交与线程一样多的任务。每个任务都会收到它需要求和的索引范围。在主线程中,收集所有Future
返回给您的 s的结果,ExecutorService.submit
并将它们汇总为最终结果。
另一种可能的优化可能是通过部分展开循环来尝试使用 CPU 的超标量能力。
例如,在管道大小为 4 个整数的架构(如果 JVM 是智能的)上,您可以编写:
for(int i = 0; i < array.size(); i += 4)
{
c[i] = a[i] + b[i];
c[i+1] = a[i+1] + b[i+1];
c[i+2] = a[i+2] + b[i+2];
c[i+3] = a[i+3] + b[i+3];
}
但是您必须为每种不同的架构管道大小编写不同的代码。
我不需要做太多真正的高性能编码,但这里没有太多优化空间(除非我很天真),除了将列表分成 n 段(每个核心 1 段)并有每个核心提出一个小计并将小计相加。现在,如果您被要求将值相乘,一旦工人遇到 0,您就会得到答案。
public class ArrayAdder {
public double getTotal(double[] array) {
Worker workers[] = new Worker[Runtime.getRuntime().availableProcessors()];
for (int i = 0; i < workers.length - 1;i++) {
workers[i] = new Worker(array,
i * array.length / workers.length,
(i + 1) * array.length / workers.length);
}
workers[workers.length - 1] = new Worker(array,
(workers.length - 1) * array.length / workers.length,array.length);
double total = 0;
for (int i = 0;i < workers.length;i++) {
try {
workers[i].join();
total += workers[i].getSum();
} catch (InterruptedException e) {
i--; //retry the wait for worker[i]
}
}
return total;
}
static class Worker extends Thread {
public Worker(double[] array, int start, int end) {
super();
this.array = array;
this.start = start;
this.end = end;
start();
}
private double[] array;
private int start;
private int end;
private double sum;
@Override
public void run() {
for (int i=start;i < end;i++) {
sum += array[i];
}
}
public double getSum() { return sum; }
}
}
您可能希望将小计和总计存储为 a,BigDecimal
具体取决于您期望值的大小。当然,除非您需要一个确切的答案,否则将它们添加为整数/长整数会快得多 - 显然您想要四舍五入而不是仅仅投射或只是投射(这可能更快)并假设您的答案会低~array.length / 2
有一半的时间,演员会朝错误的方向“绕”它。
在 Java7 中使用 Fork/Join 框架。
一种方法是决定数组的拆分,让 N 个线程读取数组的指定部分并找到单独的总和。然后,最终线程可以将所有这些单独的总和相加以获得最终输出。