[英]Branch prediction in a java for loop
我在if
條件旁邊看到了此評論:
// branch prediction favors most often used condition
protected double computeMinWidth(double height, double topInset, double rightInset, double bottomInset, double leftInset) {
double minX = 0;
double maxX = 0;
boolean firstManagedChild = true;
for (int i = 0; i < children.size(); i++) {
Node node = children.get(i);
if (node.isManaged()) {
final double x = node.getLayoutBounds().getMinX() + node.getLayoutX();
if (!firstManagedChild) { // branch prediction favors most often used condition
minX = Math.min(minX, x);
maxX = Math.max(maxX, x + node.minWidth(-1));
} else {
minX = x;
maxX = x + node.minWidth(-1);
firstManagedChild = false;
}
}
}
double minWidth = maxX - minX;
return leftInset + minWidth + rightInset;
}
我相信開發人員想解釋為什么他寫了一個否定if 。
這種優化真的有用嗎?
JavaFX團隊的成員非常專注於性能,因此他們肯定知道切換if和else對分支預測沒有實質性影響。
我認為這表明重構沒有顯着的性能改進,因為:
double minX = Double.MAX_VALUE;
double maxX = Double.MIN_VALUE;
for (int i = 0; i < children.size(); i++) {
Node node = children.get(i);
if (node.isManaged()) {
final double x = node.getLayoutBounds().getMinX() + node.getLayoutX();
minX = Math.min(minX, x);
maxX = Math.max(maxX, x + node.minWidth(-1));
}
}
因為分支預測實際上會將if / else變成類似的東西(給定或接受)。
這個提交確認了這個解釋,盡管我不確定為什么他們要更改它-可能是因為當isManaged
條件從不成立時它具有副作用。
進一步調查,該提交涉及一個錯誤“在Controls基准測試中性能下降高達50%” (您需要登錄才能訪問它)。
似乎他們遇到了性能問題,在調查時注意到代碼中存在錯誤(與我上面的示例類似)。 他們修復了該錯誤,並添加了一條注釋以闡明該修復由於分支預測而不會影響性能。
摘錄:
[..]在查看代碼時,在沒有管理任何皮膚孩子的情況下,我注意到一個錯誤。 在這種情況下,minX / Y和maxX / Y最終將是MAX / MIN_VALUE,我們真正希望它們為零。 因此,此更改集解決了該問題。 在測試中,我發現性能略有提高(〜1 fps),因此我認為此更改不能解決性能問題。 無論如何,代碼必須保持原樣。
[...]請注意,使用MIN / MAX時有一個錯誤-那些值不是Float和Double的最大值和最小值(與整數類型對稱是有意義的,但不是)指定方式)。 為了對浮點值執行最小/最大運算,您想使用NEGATIVE / POSITIVE_INFINITY值代替以獲得所需的結果。
“分支預測偏愛最常用的條件”這句話並沒有說明評估條件的值,無論是肯定的還是否定的。 它只是說分支預測可以幫助更頻繁使用的條件分支,即循環中的那些。 所以它基本上說,在循環內使用if
是可以的。
雖然結論是正確的,但您不必擔心s if
循環(除非分析器告訴您存在瓶頸,否則您無需擔心任何事情),在Java上下文中,該語句本身是毫無意義的。
分支預測是CPU的功能,因此在解釋執行中,它與Java級別分支無關,因為它們只是修改解釋器的狀態(即,將通過其讀取下一條指令的指針),但與特定於CPU的指令無關。特定的分支。
HotSpot啟動后,情況將完全不同。 如果此代碼是熱點,那么優化器將應用大量代碼轉換,從而使大多數關於Java代碼將如何執行的假設都是錯誤的。
一種常見的優化是循環展開 。 與其有一個代表循環主體的代碼塊,不如說有一個循環的多個實例相互圍繞,並對其特定迭代的不變量進行了優化。 此設置允許完全消除if
相關分支,因為完全可以預測,在firstManagedChild
從true
到false
的第一次轉換之后,它將永遠不會返回,因此,盡管第一次迭代始終看到它的true
值,但是可以優化后續迭代,以將變量始終視為false
。
因此,在那種情況下,分支預測將再也沒有意義,因為if
語句的分支可以預先預測,因此沒有分支。
在這里可以找到有關分支預測出租車的詳細文章
回答您的問題-根據我的理解,不可以。 我認為否定“ if”不會有任何影響。 它將優化重復假值的條件,就像重復多個真值一樣。
除了現有的答案:
該評論似乎不是“否定如果”的理由(因為無論如何在性能方面都不會有所不同)。 相反,這可能是不嘗試“優化”代碼並避免使用單個if
的理由。 可以通過以下方式實現:
protected double computeMinWidth(double height, double topInset,
double rightInset, double bottomInset, double leftInset) {
double minX = 0;
double maxX = 0;
// Initialize minX/maxX with the coordinate of the FIRST managed child
int i = 0;
for (i = 0; i < children.size(); i++) {
Node node = children.get(i);
if (node.isManaged()) {
final double x = node.getLayoutBounds().getMinX() + node.getLayoutX();
minX = x;
maxX = x + node.minWidth(-1);
}
}
// Continue the loop at the last index, updating the
// minX/maxX with the remaining managed children
for (; i < children.size(); i++) {
Node node = children.get(i);
if (node.isManaged()) {
final double x = node.getLayoutBounds().getMinX() + node.getLayoutX();
// Don't have to check whether it's the first managed child here.
// Just do the update.
minX = Math.min(minX, x);
maxX = Math.max(maxX, x + node.minWidth(-1));
}
}
double minWidth = maxX - minX;
return leftInset + minWidth + rightInset;
}
注釋表明,由於保存了if
,這不會帶來任何性能上的好處,因為實際上,由於分支預測,該if
實際上將是免費的。
旁注:我認為可能還有其他“微優化”,例如避免使用Math#min(double,double)
。 但是JavaFX的家伙(希望)知道他們的知識,並且如果情況可能有所不同,可能會這樣做)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.