如何才能最佳地优化循环次数超过十亿次?

时间:2018-03-02 21:50:15

标签: java loops optimization

说我想要经历一个十亿次循环,我怎样才能优化循环以更快地获得结果?

举个例子:

double randompoint;
for(long count =0; count < 1000000000; count++) {
        randompoint = (Math.random() * 1) + 0;  //generate a random point
        if(randompoint <= .75) {
            var++; 
        }
    }

我正在阅读有关版画的内容?但我不太清楚如何去做。任何想法?

1 个答案:

答案 0 :(得分:2)

由于Java是跨平台的,因此您几乎不得不依赖JIT进行矢量化。在你的情况下它不能,因为每次迭代在很大程度上取决于前一次(由于RNG的工作原理)。

但是,还有另外两种主要方法可以改善您的计算。

首先,这项工作非常适合并行化。技术术语是embarrassingly parallel。这意味着多线程将在核心数量上提供完美的线性加速。

第二个是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 docs:

  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启动)对这些方法的时间安排:

  • 你的基线:20.9s
  • 单线程ThreadLocalRandom:6.51s
  • 单线程优化随机:1.75s
  • Multithreaded ThreadLocalRandom:1.67s
  • 多线程优化随机:0.89s
  • 生成高斯:0.14s