簡體   English   中英

一個循環中的兩個操作與兩個循環在每個循環中執行相同的操作

[英]Two operations in one loop vs two loops performing the same operations one per loop

這個問題與這兩個循環體或一個(結果相同)相同,但在我的情況下,我使用Java。

我有兩個循環,運行十億次。

int a = 188, b = 144, aMax = 0, bMax = 0;

for (int i = 0; i < 1000000000; i++) {
  int t = a ^ i;
  if (t > aMax) 
    aMax = t;     
}  

for (int i = 0; i < 1000000000; i++) {
  int t = b ^ i;
  if (t > bMax) 
    bMax = t;     
}  

在我的機器中運行這兩個循環所需的時間是4秒。 當我將這兩個循環融合到一個循環中並在該單循環中執行所有操作時,它將在2秒內運行。 正如您所看到的那樣,瑣碎的操作構成了循環內容,因此需要恆定的時間。

我的問題是我在哪里獲得這種性能提升?

我猜測性能在兩個獨立的循環中受影響的唯一可能的地方是它增加i並檢查我是否<1000000000 20億次而不是10億次如果我將循環融合在一起。 還有其他事嗎?

謝謝!

如果你沒有運行預熱階段,第一個循環可能會被優化和編譯而不是第二個循環,而當你合並它們時,整個合並循環就會被編譯。 此外,使用server選項和您的代碼,大多數都會被優化掉,因為您不使用結果。

我已經運行了下面的測試,將每個循環以及合並循環放在他們自己的方法中,並熱化JVM以確保所有內容都被編譯。

結果(JVM選項: -server -XX:+PrintCompilation ):

  • 循環1 = 500ms
  • 循環2 = 900毫秒
  • 合並循環= 1,300毫秒

因此合並的循環稍微快一點,但不是那么多。

public static void main(String[] args) throws InterruptedException {

    for (int i = 0; i < 3; i++) {
        loop1();
        loop2();
        loopBoth();
    }

    long start = System.nanoTime();

    loop1();

    long end = System.nanoTime();
    System.out.println((end - start) / 1000000);

    start = System.nanoTime();
    loop2();
    end = System.nanoTime();
    System.out.println((end - start) / 1000000);

    start = System.nanoTime();
    loopBoth();
    end = System.nanoTime();
    System.out.println((end - start) / 1000000);
}

public static void loop1() {
    int a = 188, aMax = 0;
    for (int i = 0; i < 1000000000; i++) {
        int t = a ^ i;
        if (t > aMax) {
            aMax = t;
        }
    }
    System.out.println(aMax);
}

public static void loop2() {
    int b = 144, bMax = 0;
    for (int i = 0; i < 1000000000; i++) {
        int t = b ^ i;
        if (t > bMax) {
            bMax = t;
        }
    }
    System.out.println(bMax);
}

public static void loopBoth() {
    int a = 188, b = 144, aMax = 0, bMax = 0;

    for (int i = 0; i < 1000000000; i++) {
        int t = a ^ i;
        if (t > aMax) {
            aMax = t;
        }
        int u = b ^ i;
        if (u > bMax) {
            bMax = u;
        }
    }
    System.out.println(aMax);
    System.out.println(bMax);
}

簡而言之,CPU可以並行執行合並循環中的指令,從而使性能提高一倍。

它也可能沒有有效地優化第二個循環。 這是因為第一個循環將觸發整個方法進行編譯,第二個循環將被編譯而沒有任何可能擾亂第二個循環時序的指標。 我會將每個循環放在一個單獨的方法中,以確保不是這種情況。

CPU可以並行執行大量獨立操作( Pentium III上的深度為10,Xeon中為20 )。 它嘗試並行執行的一個操作是使用分支預測的分支,但是如果它幾乎不是每次都采用相同的分支。

我懷疑循環展開你的循環看起來更像是跟隨(在這種情況下可能更多的循環展開)

for (int i = 0; i < 1000000000; i += 2) {
  // this first block is run almost in parallel
  int t1 = a ^ i;
  int t2 = b ^ i;
  int t3 = a ^ (i+1);
  int t4 = b ^ (i+1);
  // this block run in parallel
  if (t1 > aMax) aMax = t1;     
  if (t2 > bMax) bMax = t2;     
  if (t3 > aMax) aMax = t3;     
  if (t4 > bMax) bMax = t4;     
} 

在我看來,在單循環的情況下,JIT 可以選擇進行循環展開,因此性能稍好一些

你用過-server嗎? 如果不是,你應該 - 客戶端JIT不是可預測的,也不是那么好。 如果您真正對正在發生的事情感興趣,可以使用UnlockDiagnostic + LogCompilation來檢查在兩種情況下應用的優化(一直到生成的程序集)。

此外,從您提供的代碼中我無法看到您是否進行預熱,無論是為同一個JVM運行一次還是多次運行,是否進行了幾次運行(不同的JVM)。 無論你是考慮最佳,平均還是中位時間,你都會拋棄異常值嗎?

以下是編寫Java微基准測試主題的一個很好的鏈接: http//www.ibm.com/developerworks/java/library/j-jtp02225/index.html

編輯:還有一個微基准測試技巧,請注意堆疊更換: http//www.azulsystems.com/blog/cliff/2011-11-22-what-the-heck-is-osr-and-why-是-IT-壞或好

暫無
暫無

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

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