[英]Enhanced for loop performance worse than traditional indexed lookup?
我剛剛看到這個看似無害的評論 ,對ArrayList和原始String數組進行基准測試。 它來自幾年前,但OP寫道
我注意到使用String s:stringsList比使用舊式for循環訪問列表慢約50%。 去搞清楚...
沒有人在原帖中評論它,測試看起來有點可疑(太短不准確),但是當我讀到它時,我差點從椅子上掉下來。 我從來沒有對一個“傳統”的循環增強循環進行基准測試,但我目前正在開發一個項目,它使用增強循環對ArrayList實例進行數億次迭代,所以這是我關注的問題。
我打算做一些基准測試並在此發表我的發現,但這顯然是我的一個大問題。 我可以在網上找到關於相對性能的寶貴的小信息,除了一些隨便提到的ArrayLists的增強循環在Android下運行速度慢得多 。
有沒有人經歷過這個? 這種性能差距是否仍然存在? 我會在這里發布我的發現,但讀到它時非常驚訝。 我懷疑如果這個性能差距確實存在,它已經在更現代的VM中得到修復,但我想我現在必須做一些測試並確認。
更新:我對我的代碼進行了一些更改,但是已經懷疑其他人已經指出的內容:確保增強的for循環速度較慢,但是在非常簡單的緊密循環之外,成本應該是成本的一小部分。循環的邏輯。 在我的情況下,即使我使用增強型循環迭代非常大的字符串列表,我在循環中的邏輯也足夠復雜,甚至在切換到基於索引的循環之后我甚至無法測量差異。
TL; DR:增強循環確實比傳統的基於索引的循環慢於arraylist; 但對於大多數應用來說,差異應該可以忽略不計。
您遇到的問題是使用Iterator比使用直接查找要慢。 在我的機器上,每次迭代的差異大約為0.13 ns。 使用數組代替每次迭代節省大約0.15 ns。 在99%的情況下,這應該是微不足道的。
public static void main(String... args) {
int testLength = 100 * 1000 * 1000;
String[] stringArray = new String[testLength];
Arrays.fill(stringArray, "a");
List<String> stringList = new ArrayList<String>(Arrays.asList(stringArray));
{
long start = System.nanoTime();
long total = 0;
for (String str : stringArray) {
total += str.length();
}
System.out.printf("The for each Array loop time was %.2f ns total=%d%n", (double) (System.nanoTime() - start) / testLength, total);
}
{
long start = System.nanoTime();
long total = 0;
for (int i = 0, stringListSize = stringList.size(); i < stringListSize; i++) {
String str = stringList.get(i);
total += str.length();
}
System.out.printf("The for/get List loop time was %.2f ns total=%d%n", (double) (System.nanoTime() - start) / testLength, total);
}
{
long start = System.nanoTime();
long total = 0;
for (String str : stringList) {
total += str.length();
}
System.out.printf("The for each List loop time was %.2f ns total=%d%n", (double) (System.nanoTime() - start) / testLength, total);
}
}
當以10億個條目運行條目打印時(使用Java 6更新26)
The for each Array loop time was 0.76 ns total=1000000000
The for/get List loop time was 0.91 ns total=1000000000
The for each List loop time was 1.04 ns total=1000000000
以10億個條目運行時打印條目(使用OpenJDK 7)
The for each Array loop time was 0.76 ns total=1000000000
The for/get List loop time was 0.91 ns total=1000000000
The for each List loop time was 1.04 ns total=1000000000
即完全相同。 ;)
GravityBringer的數字似乎不正確,因為我知道ArrayList.get()
與VM優化后的原始數組訪問速度一樣快。
我在我的機器-server
模式下運行了兩次GravityBringer測試
50574847
43872295
30494292
30787885
(2nd round)
33865894
32939945
33362063
33165376
這種測試的瓶頸實際上是內存讀/寫。 從數字來看,整個2個陣列都在我的L2緩存中。 如果我們減小大小以適應L1緩存,或者如果我們將大小增加到L2緩存之外,我們將看到10倍的吞吐量差異。
ArrayList
的迭代器使用單個int
計數器。 即使VM沒有將它放入寄存器(循環體太復雜),至少它將在L1緩存中,因此r / w基本上是免費的。
最終的答案當然是在您的特定環境中測試您的特定程序。
雖然每當提出基准問題時玩不可知論者都沒有幫助。
ArrayLists的情況變得更糟。 在運行Java 6.26的計算機上,存在四倍的差異。 有趣的是(也許在邏輯上相當),原始數組沒有區別。 我運行了以下測試:
int testSize = 5000000;
ArrayList<Double> list = new ArrayList<Double>();
Double[] arr = new Double[testSize];
//set up the data - make sure data doesn't have patterns
//or anything compiler could somehow optimize
for (int i=0;i<testSize; i++)
{
double someNumber = Math.random();
list.add(someNumber);
arr[i] = someNumber;
}
//ArrayList foreach
long time = System.nanoTime();
double total1 = 0;
for (Double k: list)
{
total1 += k;
}
System.out.println (System.nanoTime()-time);
//ArrayList get() method
time = System.nanoTime();
double total2 = 0;
for (int i=0;i<testSize;i++)
{
total2 += list.get(i);
}
System.out.println (System.nanoTime()-time);
//array foreach
time = System.nanoTime();
double total3 = 0;
for (Double k: arr)
{
total3 += k;
}
System.out.println (System.nanoTime()-time);
//array indexing
time = System.nanoTime();
double total4 = 0;
for (int i=0;i<testSize;i++)
{
total4 += arr[i];
}
System.out.println (System.nanoTime()-time);
//would be strange if different values were produced,
//but no, all these are the same, of course
System.out.println (total1);
System.out.println (total2);
System.out.println (total3);
System.out.println (total4);
循環中的算法是防止JIT編譯器可能優化掉一些代碼。 算術對性能的影響很小,因為運行時由ArrayList訪問控制。
運行時間(以納秒為單位):
ArrayList foreach:248,351,782
ArrayList get():60,657,907
陣列foreach:27,381,576
數組直接索引:27,468,091
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.