簡體   English   中英

如何最好地優化將循環十億次的循環?

[英]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啟動)上使用這些方法的時間:

  • 您的基線:20.9秒
  • 單線程ThreadLocalRandom:6.51s
  • 單線程優化隨機:1.75s
  • 多線程ThreadLocalRandom:1.67s
  • 多線程優化隨機數:0.89s
  • 產生高斯:0.14s

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM