[英]How can I best optimize a loop that will loop over a billion times?
假设我要遍历十亿次循环,如何优化循环以更快地获得结果?
举个例子:
double randompoint;
for(long count =0; count < 1000000000; count++) {
randompoint = (Math.random() * 1) + 0; //generate a random point
if(randompoint <= .75) {
var++;
}
}
我正在阅读关于矢量化的信息? 但是我不太确定如何去做。 有任何想法吗?
由于Java是跨平台的,因此您几乎必须依靠JIT进行矢量化。 对于您而言,它不可能,因为每次迭代都很大程度上取决于前一个迭代(由于RNG的工作方式)。
但是,还有另外两种主要方法可以改善您的计算。
首先,这项工作非常适合并行化。 技术术语令人尴尬地是并行的 。 这意味着多线程将在内核数量上实现完美的线性加速。
第二个原因是Math.random()
编写为多线程安全的,这也意味着它很慢,因为它需要使用原子操作。 这没有帮助,因此我们可以使用非线程安全的RNG跳过该开销。
自1.5以来我没有写太多Java,但是这是一个愚蠢的实现:
import java.util.*;
import java.util.concurrent.*;
class Foo implements Runnable {
private long count;
private double threshold;
private long result;
public Foo(long count, double threshold) {
this.count = count;
this.threshold = threshold;
}
public void run() {
ThreadLocalRandom rand = ThreadLocalRandom.current();
for(long l=0; l<count; l++) {
if(rand.nextDouble() < threshold)
result++;
}
}
public static void main(String[] args) throws Exception {
long count = 1000000000;
double threshold = 0.75;
int cores = Runtime.getRuntime().availableProcessors();
long sum = 0;
List<Foo> list = new ArrayList<Foo>();
List<Thread> threads = new ArrayList<Thread>();
for(int i=0; i<cores; i++) {
// TODO: account for count%cores!=0
Foo t = new Foo(count/cores, threshold);
list.add(t);
Thread thread = new Thread(t);
thread.start();
threads.add(thread);
}
for(Thread t : threads) t.join();
for(Foo f : list) sum += f.result;
System.out.println(sum);
}
}
您还可以优化并内联随机生成器,以避免重复使用。 这是从ThreadLocalRandom文档中获取的代码:
public void run() {
long seed = new Random().nextLong();
long limit = (long) ((1L<<48) * threshold);
for(int i=0; i<count; i++) {
seed = (seed * 0x5DEECE66DL + 0xBL) & ((1L << 48) - 1);
if (seed < limit) ++result;
}
}
但是,最好的方法是更聪明地工作,而不是更努力。 随着事件数量的增加,概率趋向于正态分布。 这意味着对于您的巨大范围,您可以随机生成一个具有这种分布的数字并将其缩放:
import java.util.Random;
class StayInSchool {
public static void main(String[] args) {
System.out.println(coinToss(1000000000, 0.75));
}
static long coinToss(long iterations, double threshold) {
double mean = threshold * iterations;
double stdDev = Math.sqrt(threshold * (1-threshold) * iterations);
double p = new Random().nextGaussian();
return (long) (p*stdDev + mean);
}
}
以下是在我的4核心系统(包括VM启动)上使用这些方法的时间:
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.