way*_*yne 2 java optimization jit jvm
我有一个函数,用于检查映射是否具有键 ( map.get(key) != null) 的某个值,然后返回该值,否则创建新值。
\n我想知道当给定键的值存在时,是否有任何 JVM 或 JIT 魔法不必执行 2 次映射查找?
Value someValue = map.get(key) != null ? map.get(key) : new Value();\nRun Code Online (Sandbox Code Playgroud)\n根据我的基准测试,似乎无法进行优化,因为它明显比我们有一个局部变量来保存该值要慢:
\n@Benchmark\npublic String duplicateCall() {\n return map.get(1) != null ? map.get(1) : DEFAULT;\n}\n\n@Benchmark\npublic String nonDuplicateCall() {\n final String s = map.get(1);\n return s != null ? s : DEFAULT;\n}\nRun Code Online (Sandbox Code Playgroud)\n结果:
\nBenchmark Mode Cnt Score Error Units\nduplicateCall thrpt 5 634001.515 \xc2\xb1 69181.631 ops/ms\nnonDuplicateCall thrpt 5 869980.580 \xc2\xb1 66572.021 ops/ms\nRun Code Online (Sandbox Code Playgroud)\n
只有优化器能够证明这两个操作是幂等的,消除重复的方法调用才能起作用,这需要查看 method\xe2\x80\x99s 的实际实现代码。
\n您假设两次出现map.get(1)将执行相同的操作,这依赖于几个前提,JVM 可以\xe2\x80\x99t 认为这是理所当然的。
您\xe2\x80\x99正在同一对象实例上调用该方法。即使在这个具有相邻调用的简单代码中,优化器也可以\xe2\x80\x99t 假设这一点,而不知道get实际的作用。如果get改变map参考,这个假设将无效。
您\xe2\x80\x99 正在传递相同的密钥。您的代码表明我们\xe2\x80\x99正在谈论 a Map<Integer, String>,因此表达式会受到自动装箱的影响。您\xe2\x80\x99有效地传递Integer.valueOf(1)到另一个调用get的另一个出现。\n自动装箱/的特定合同允许用一个替换另一个,无论实现如何,问题是优化器是否会知道并利用这一点。Integer.valueOf(1)getInteger.valueOf(int)
该方法没有副作用。虽然假设 \xe2\x80\x99sMap实现get不会修改映射是合理的,但它可能包含默认情况下无效但可以激活的日志记录或基准语句,具体取决于某些运行时状态。如果有这样的语句,证明它们无效需要预测运行时状态。
由于消除冗余只有在了解实现代码时才有效,因此只有在满足内联先决条件(例如,调用总是以相同的实现结束)时内联代码后才有效。然后,将应用常见子表达式消除等优化。
\n这种优化的有效性取决于实际的实现代码。Map.of()对于像或 这样的空地图以及像或 这样Collections.emptyMap()的单例地图,这可能会很有效。但对于像or \xc2\xb9 这样的映射,实际实现太复杂,无法假设可以完全消除冗余计算。重要的是要记住,这还需要内联密钥的and (或)实现,以证明它们的幂等性。对于密钥而言不是问题,但对于其他密钥类型可能存在问题。Map.of(1, "foo")Collections.singletonMap(1, "foo")TreeMapHashMaphashCodeequalscompareToInteger
为了说明这一点,我们\xe2\x80\x99正在讨论这样的实现HashMap:
public V get(Object key) {\n Node<K,V> e;\n return (e = getNode(hash(key), key)) == null ? null : e.value;\n}\nfinal Node<K,V> getNode(int hash, Object key) {\n Node<K,V>[] tab; Node<K,V> first, e; int n; K k;\n if ((tab = table) != null && (n = tab.length) > 0 &&\n (first = tab[(n - 1) & hash]) != null) {\n if (first.hash == hash && // always check first node\n ((k = first.key) == key || (key != null && key.equals(k))))\n return first;\n if ((e = first.next) != null) {\n if (first instanceof TreeNode)\n return ((TreeNode<K,V>)first).getTreeNode(hash, key);\n do {\n if (e.hash == hash &&\n ((k = e.key) == key || (key != null && key.equals(k))))\n return e;\n } while ((e = e.next) != null);\n }\n }\n return null;\n}\nRun Code Online (Sandbox Code Playgroud)\n优化器必须内联此代码两次(由于代码大小阈值,它很可能不会\xe2\x80\x99t),然后证明两次出现的效果相同并且没有副作用\xe2\x80\xa6
\n正如评论中已经提到的,map.getOrDefault(1, DEFAULT)如果您想要简洁的代码并避免重复的查找操作,您可以简单地使用。
\xc2\xb9 和并发或同步映射无论如何都不在游戏中,除非优化器可以证明它们从未被另一个线程见过。
\n| 归档时间: |
|
| 查看次数: |
130 次 |
| 最近记录: |