-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Description
I received a report from a customer mentioning that after they changed some piece of JavaScript code to use "classical" for loops instead of "for of" they obtained a huge performance increase in their application. After doing some benchmarking on my own I also noticed that there is quite a big difference in performance between the two kinds of loops. In all cases the loop was supposed to iterate on arrays of primitive integers.
Please, see below the two versions that I tried.
function classic(array) {
let ll = array.length;
for (let i=0; i<ll; i++) {
let entry = array[i];
if (entry == 1) {
return true;
}
}
return false;
}
and
function forof(array) {
for (let entry of array) {
if (entry == 1) {
return true;
}
}
return false;
}
I call each method using another "main" method. The main method is simply:
function main(length) {
const array = new Array(1_000_000).fill(0);
classic(array); // forof(array);
}
My experiments were controlled to make sure both methods are compiled at tier 2. The results I get show that the classical for loop is about 50% faster than the for .. of
. The performance difference increases based on the number of nested loops, size of the arrays, etc. With two nested loops I see about 3x difference in performance.
I understand that the for ... of
version uses an Iterator behind the scenes, but I was expecting all the iterator machinery to go away (or be largely eliminated) after Partial Evaluation and the other optimizations. However, AFAIU the "bulk" of the iterator machinery is still present in the IR even after the "Low Tier" phase.
In the IR graph I see a couple of nodes for allocating objects. Particularly, I see two allocations that are due to boxing of int to Integer because of the return value of this method AbstractIntArray::getInBoundsFast
. Please, see stacktrace below.
The boxing seems to be required because the Iterator uses "Object" as the type of the elements being iterated upon. I'm wondering if we can have Iterators specialized for different data types, may be at least for numeric values?
Please let me know what you think.
jdk.graal.compiler.hotspot.replacements.HotSpotAllocationSnippets.newInstanceOrNull (HotSpotAllocationSnippets.java:-1) [bci:-1]
jdk.graal.compiler.hotspot.replacements.HotSpotAllocationSnippets.callNewInstanceStub (HotSpotAllocationSnippets.java:351) [bci:13]
jdk.graal.compiler.replacements.AllocationSnippets.allocateInstanceImpl (AllocationSnippets.java:68) [bci:126]
jdk.graal.compiler.hotspot.replacements.HotSpotAllocationSnippets.allocateInstance (HotSpotAllocationSnippets.java:150) [bci:20]
jdk.graal.compiler.hotspot.replacements.HotSpotAllocationSnippets.allocateInstance (HotSpotAllocationSnippets.java:-1) [bci:-1]
java.lang.Integer.valueOf (Integer.java:1005) [bci:23]
jdk.graal.compiler.replacements.BoxingSnippets.intValueOf (BoxingSnippets.java:82) [bci:5]
jdk.graal.compiler.replacements.BoxingSnippets.intValueOf (BoxingSnippets.java:-1) [bci:-1]
java.lang.Integer.valueOf (Integer.java:-1) [bci:-1]
com.oracle.truffle.js.runtime.array.dyn.AbstractIntArray.getInBoundsFast (AbstractIntArray.java:101) [bci:6]
com.oracle.truffle.js.nodes.access.ReadElementNode$WritableArrayReadElementCacheNode.doWritableArray (ReadElementNode.java:1039) [bci:19]
com.oracle.truffle.js.nodes.access.ReadElementNodeFactory$WritableArrayReadElementCacheNodeGen.executeArrayGet (ReadElementNodeFactory.java:1167) [bci:43]
com.oracle.truffle.js.nodes.access.ReadElementNode$ArrayReadElementCacheDispatchNode.doDispatch (ReadElementNode.java:870) [bci:101]
com.oracle.truffle.js.nodes.access.ReadElementNodeFactory$ArrayReadElementCacheDispatchNodeGen$Inlined.executeExpectReturn (ReadElementNodeFactory.java:667) [bci:64]
com.oracle.truffle.js.nodes.access.ReadElementNode$ArrayReadElementCacheDispatchNode.executeArrayGet (ReadElementNode.java:834) [bci:13]
com.oracle.truffle.js.nodes.access.ReadElementNode$ArrayReadElementCacheDispatchNode.executeDelegateReturn (ReadElementNode.java:829) [bci:101]
com.oracle.truffle.js.nodes.access.ReadElementNode$JSObjectReadElementTypeCacheNode.doLongIndex (ReadElementNode.java:657) [bci:60]
com.oracle.truffle.js.nodes.access.ReadElementNodeFactory$JSObjectReadElementTypeCacheNodeGen.executeExpectReturn (ReadElementNodeFactory.java:536) [bci:53]
com.oracle.truffle.js.nodes.access.ReadElementNode$JSObjectReadElementTypeCacheNode.executeWithTargetAndIndexUnchecked (ReadElementNode.java:597) [bci:10]
com.oracle.truffle.js.nodes.access.ReadElementNode$ReadElementTypeCacheDispatchNode.doJSObjectLongIndex (ReadElementNode.java:506) [bci:92]
com.oracle.truffle.js.nodes.access.ReadElementNodeFactory$ReadElementTypeCacheDispatchNodeGen.executeExpectReturn (ReadElementNodeFactory.java:215) [bci:70]
com.oracle.truffle.js.nodes.access.ReadElementNode$ReadElementTypeCacheDispatchNode.executeWithTargetAndIndexUnchecked (ReadElementNode.java:462) [bci:10]
com.oracle.truffle.js.nodes.access.ReadElementNode.executeTypeDispatch (ReadElementNode.java:405) [bci:26]
com.oracle.truffle.js.nodes.access.ReadElementNode.executeWithTargetAndIndex (ReadElementNode.java:348) [bci:7]
com.oracle.truffle.js.builtins.ArrayIteratorPrototypeBuiltins$ArrayIteratorNextNode.doArrayIterator (ArrayIteratorPrototypeBuiltins.java:150) [bci:174]
com.oracle.truffle.js.builtins.ArrayIteratorPrototypeBuiltinsFactory$ArrayIteratorNextNodeGen.execute (ArrayIteratorPrototypeBuiltinsFactory.java:164) [bci:97]
com.oracle.truffle.js.nodes.function.FunctionRootNode.executeInRealm (FunctionRootNode.java:155) [bci:5]
com.oracle.truffle.js.runtime.JavaScriptRealmBoundaryRootNode.execute (JavaScriptRealmBoundaryRootNode.java:96) [bci:110]
com.oracle.truffle.runtime.OptimizedCallTarget.executeRootNode (OptimizedCallTarget.java:823) [bci:5]
com.oracle.truffle.runtime.OptimizedCallTarget.callInlined (OptimizedCallTarget.java:619) [bci:23]
com.oracle.truffle.runtime.OptimizedDirectCallNode.call (OptimizedDirectCallNode.java:94) [bci:30]
com.oracle.truffle.js.nodes.function.JSFunctionCallNode$DirectJSFunctionCacheNode.executeCall (JSFunctionCallNode.java:1330) [bci:5]
com.oracle.truffle.js.nodes.function.JSFunctionCallNode.executeCall (JSFunctionCallNode.java:249) [bci:24]
com.oracle.truffle.js.nodes.access.IteratorNextUnaryNode.iteratorNext (IteratorNextUnaryNode.java:84) [bci:21]
com.oracle.truffle.js.nodes.access.IteratorNextUnaryNode.doDefault (IteratorNextUnaryNode.java:78) [bci:15]
com.oracle.truffle.js.nodes.access.IteratorNextUnaryNodeGen.execute (IteratorNextUnaryNodeGen.java:48) [bci:5]
com.oracle.truffle.js.nodes.access.JSWriteCurrentFrameSlotNodeGen.execute_generic3 (JSWriteCurrentFrameSlotNodeGen.java:136) [bci:7]
com.oracle.truffle.js.nodes.access.JSWriteCurrentFrameSlotNodeGen.execute (JSWriteCurrentFrameSlotNodeGen.java:67) [bci:71]
com.oracle.truffle.js.nodes.access.IteratorCompleteUnaryNode.executeBoolean (IteratorCompleteUnaryNode.java:77) [bci:5]
com.oracle.truffle.js.nodes.unary.JSNotNodeGen.executeBoolean_boolean2 (JSNotNodeGen.java:116) [bci:7]
com.oracle.truffle.js.nodes.unary.JSNotNodeGen.executeBoolean (JSNotNodeGen.java:106) [bci:20]
com.oracle.truffle.js.nodes.binary.DualNode.executeBoolean (DualNode.java:134) [bci:13]
com.oracle.truffle.js.nodes.control.StatementNode.executeConditionAsBoolean (StatementNode.java:58) [bci:2]
com.oracle.truffle.js.nodes.control.AbstractRepeatingNode.executeCondition (AbstractRepeatingNode.java:63) [bci:5]
com.oracle.truffle.js.nodes.control.WhileNode$WhileDoRepeatingNode.executeRepeating (WhileNode.java:237) [bci:2]
com.oracle.truffle.api.nodes.RepeatingNode.executeRepeatingWithValue (RepeatingNode.java:112) [bci:2]
com.oracle.truffle.runtime.OptimizedOSRLoopNode.execute (OptimizedOSRLoopNode.java:149) [bci:210]
com.oracle.truffle.js.nodes.control.WhileNode.execute (WhileNode.java:175) [bci:5]
com.oracle.truffle.js.nodes.control.IteratorCloseWrapperNode.doDefault (IteratorCloseWrapperNode.java:81) [bci:5]
com.oracle.truffle.js.nodes.control.IteratorCloseWrapperNodeGen.execute (IteratorCloseWrapperNodeGen.java:69) [bci:11]
com.oracle.truffle.js.nodes.control.IteratorCloseWrapperNodeGen.executeVoid (IteratorCloseWrapperNodeGen.java:74) [bci:2]
com.oracle.truffle.js.nodes.control.TryFinallyNode.executeVoid (TryFinallyNode.java:105) [bci:5]
...