![](/img/trans.png)
[英]How is it possible for Java HashMap to perform constant time lookup O(1) for “get” operations?
[英]How to get around for loop bottleneck for constant time operations?
在規則不可知的撲克模擬器上工作以獲得樂趣。 測試枚舉中的瓶頸,以及總是從“獨特”陣列中獲取的手,我發現了一個有趣的瓶頸。 我測量了運行每個變量低於1,000,000,000次的平均計算時間,然后在100次重復中進行了最佳操作,以允許JIT和Hotspot發揮他們的魔力。 我發現之間的計算時間(6ns vs 27ns)存在差異
public int getRank7(int ... cards) {
int q = (cards[0] >> 16) | (cards[1] >> 16) | (cards[2] >> 16) | (cards[3] >> 16) | (cards[4] >> 16) | (cards[5] >> 16) | (cards[6] >> 16);
int product = ((cards[0] & 0xFF) * (cards[1] & 0xFF) * (cards[2] & 0xFF) * (cards[3] & 0xFF) * (cards[4] & 0xFF) * (cards[5] & 0xFF) * (cards[6] & 0xFF));
if(flushes[q] > 0) return flushes[q];
if(unique[q] > 0) return unique[q];
int x = Arrays.binarySearch(products, product);
return rankings[x];
}
和
public int getRank(int ... cards) {
int q = 0;
long product = 1;
for(int c : cards) {
q |= (c >> 16);
product *= (c & 0xFF);
}
if(flushes[q] > 0) return flushes[q];
if(unique[q] > 0) return unique[q];
int x = Arrays.binarySearch(products, product);
return rankings[x];
}
問題肯定是for循環,而不是在函數頂部添加處理乘法。 我對此感到困惑,因為我在每個場景中都運行了相同數量的操作...我意識到我在這個功能中總是有6張或更多卡片所以我通過將它改為
public int getRank(int c0, int c1, int c2, int c3, int c4, int c5, int ... cards)
但是,隨着卡數量的增加,我將面臨同樣的瓶頸。 有沒有辦法解決這個問題,如果沒有,有人可以向我解釋為什么相同數量的操作的for循環要慢得多?
我想你會發現最大的不同就是分支。 for循環場景需要在for循環的每次迭代中進行檢查和條件分支。 你的CPU將嘗試預測將采用哪個分支,並相應地預測管道指令,但是當它錯誤預測時(每個函數調用至少一次,因為循環終止),管道停滯,這是非常昂貴的。
要嘗試的一件事是具有固定上限的常規for循環(而不是基於數組長度的循環); Java JRE可以展開這樣的循環,這將導致與更高效版本相同的操作序列。
增強的for
循環需要設置一個迭代器,當你只有少數幾個項目時這是相對昂貴的。
如果你寫了一個傳統的for
循環,看看你的時間是多么有趣:
for (int i = 0; i < cards.length; ++i)
{
q |= (cards[i] >> 16);
product *= (cards[i] & 0xFF);
}
但即使這可能比第一個例子略慢,因為有一些循環開銷(遞增索引,將其與長度進行比較,並分支到循環的開頭)。
在任何情況下,循環開銷都會為每次迭代添加增量,比較和分支。 而這種比較很可能需要一個指針去引用才能到達cards.length
。 循環開銷比你在循環中所做的工作要昂貴得多,這是非常合理的。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.