我有一個函式,它檢查地圖是否具有鍵(map.get(key) != null)的某些值,然后回傳該值,否則創建新值。
我想知道當給定鍵的值存在時,是否有任何 JVM 或 JIT 魔術不需要進行 2 次映射查找?
Value someValue = map.get(key) != null ? map.get(key) : new Value();
根據我的基準,似乎無法進行優化,因為它明顯比我們有一個區域變數來保存值要慢:
@Benchmark
public String duplicateCall() {
return map.get(1) != null ? map.get(1) : DEFAULT;
}
@Benchmark
public String nonDuplicateCall() {
final String s = map.get(1);
return s != null ? s : DEFAULT;
}
結果:
Benchmark Mode Cnt Score Error Units
duplicateCall thrpt 5 634001.515 ± 69181.631 ops/ms
nonDuplicateCall thrpt 5 869980.580 ± 66572.021 ops/ms
uj5u.com熱心網友回復:
只有優化器能夠證明這兩個操作是冪等的,才能消除重復的方法呼叫,這需要查看方法的實際實作代碼。
您假設兩次出現的情況map.get(1)相同,這依賴于幾個前提,JVM 不能認為是理所當然的。
您正在同一個物件實體上呼叫該方法。即使在這個帶有相鄰呼叫的簡單代碼中,優化器也不能假設這一點,而不知道
get實際做了什么。如果get更改map參考,此假設將無效。您正在傳遞相同的密鑰。您的代碼表明我們正在談論 a
Map<Integer, String>,因此該運算式受自動裝箱的影響。您有效地傳遞Integer.valueOf(1)給另一個呼叫get并再次出現。 自動裝箱 / 的特定合約允許用一個替換另一個,無論實作如何,問題是優化器是否會知道并利用這一點。Integer.valueOf(1)getInteger.valueOf(int)該方法沒有副作用。雖然假設 a
Map的get實作不會修改映射是合理的,但它可能包含默認情況下無效但可以根據某些運行時狀態激活的日志記錄或基準測驗陳述句。如果它有這樣的陳述句,證明它們的無效性需要預測運行時狀態。
由于消除冗余只有在知道實作代碼的情況下才能起作用,所以它只有在滿足行內的先決條件時才行內代碼后才起作用(例如,呼叫總是以相同的實作結束)。然后,將應用諸如公共子運算式消除之類的優化。
這種優化的有效性取決于實際的實作代碼。對于像 or 這樣的空地圖和像Map.of()or這樣Collections.emptyMap()的單例地圖Map.of(1, "foo"),Collections.singletonMap(1, "foo")這可能會很好。但是對于像TreeMapor HashMap1 這樣的地圖,實際實作過于復雜,無法假設可以完全消除冗余評估。重要的是要記住,這還需要行內密鑰的hashCode和equals(或compareTo)實作,以證明它們的冪等性。鍵不是問題Integer,但可能是其他鍵型別。
為了說明這一點,我們正在討論這樣的實作HashMap:
public V get(Object key) {
Node<K,V> e;
return (e = getNode(hash(key), key)) == null ? null : e.value;
}
final Node<K,V> getNode(int hash, Object key) {
Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
if ((tab = table) != null && (n = tab.length) > 0 &&
(first = tab[(n - 1) & hash]) != null) {
if (first.hash == hash && // always check first node
((k = first.key) == key || (key != null && key.equals(k))))
return first;
if ((e = first.next) != null) {
if (first instanceof TreeNode)
return ((TreeNode<K,V>)first).getTreeNode(hash, key);
do {
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
return e;
} while ((e = e.next) != null);
}
}
return null;
}
優化器必須將此代碼行內兩次(由于代碼大小閾值,它不會行內的可能性很高),然后證明兩次出現的情況相同并且沒有副作用……
正如評論中已經提到的,map.getOrDefault(1, DEFAULT)如果您想要簡潔的代碼并避免重復的查找操作,您可以簡單地使用。
1 并且并發或同步的地圖無論如何都不在游戲中,除非優化器可以證明它們從未被另一個執行緒看到過。
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/516842.html
標籤:爪哇优化虚拟机吉特
上一篇:房間資料庫查詢-洗掉用戶
