Closed Bug 469347 Opened 16 years ago Closed 15 years ago

TM: for loop with i<sarr.length slower with jit enabled

Categories

(Core :: JavaScript Engine, defect, P2)

x86
Windows Vista
defect

Tracking

()

VERIFIED FIXED
mozilla1.9.2

People

(Reporter: Dpeelen, Assigned: Waldo)

References

()

Details

(Keywords: verified1.9.1, Whiteboard: fixed-in-tracemonkey)

Attachments

(2 files, 2 obsolete files)

User-Agent:       Mozilla/5.0 (Windows; U; Windows NT 6.0; nl; rv:1.9.0.4) Gecko/2008102920 Firefox/3.0.4
Build Identifier: Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.9.2a1pre) Gecko/20081212 Minefield/3.2a1pre

As a follow up of bug 451974, here some more for loop problems.

Both 

var f=function(x){}; for (var i=0, len=sarr.length; i<len; i++) { f(sarr[i]); }

And

for (var i=0; i<hColl.length; i++) { }

are 1.5x to 2x slower with JIT enabled then with JIT disabled. As i got the idea these 2 problems are related, i put them in one bug for now.

Reproducible: Always
Yay me for copying exactly the 2 lines that where supposed to go into the other bug. Anyway, here i'm talking about these:

for (var i=0; i<sarr.length; i++) { }

And

var i = 0; while (i < sarr.length) { i++; }

Not the 2 mentioned in the first report.
Version: unspecified → Trunk
If sarr is a dense array, the loop is about 15x faster with JIT.

In the testcase, it is a sparse array. record_JSOP_LENGTH aborts if we're taking the length of anything other than a string or dense array.  This should be fixed.  If we're going to trace the DOM someday soon, we need to be able to trace `nodelist.length`.
Status: UNCONFIRMED → NEW
Ever confirmed: true
Flags: blocking1.9.1?
Again, shouldn't we be blacklisting?
The testcase in bug 469351 in fact hits the nodelist case.
Blocks: 469351
P2.
Priority: -- → P2
blocking1.9.1+
Flags: blocking1.9.1? → blocking1.9.1+
It looks like we could trace JSOP_LENGTH on a sparse array fairly easily if we just use a guard on "being a sparse array".
Assignee: general → jwalden+bmo
Blocks: 458016
No longer blocks: 458016
Attached patch Patch (obsolete) — Splinter Review
I was confused by record_JSOP_GETPROP for awhile because I assumed it looked at the atom argument to that opcode in bytecode, when in reality it leans quite heavily on the property cache.


find-waldo-now:~/moz/js-tm/js/src jwalden$ cat /tmp/slow.js 
var a = [];
a[9999999] = 0;
for (var i = 0; i < a.length; i++);

find-waldo-now:~/moz/js-tm/js/src jwalden$ time ../../obj-i386-apple-darwin8.11.1/dist/bin/js /tmp/slow.js 

real    0m3.262s
user    0m2.696s
sys     0m0.048s
find-waldo-now:~/moz/js-tm/js/src jwalden$ time ../../obj-i386-apple-darwin8.11.1/dist/bin/js -j /tmp/slow.js 

real    0m0.086s
user    0m0.063s
sys     0m0.014s
find-waldo-now:~/moz/js-tm/js/src jwalden$ cat /tmp/object.js 
var a = {};
a.length = 9999999;
for (var i = 0; i < a.length; i++);

find-waldo-now:~/moz/js-tm/js/src jwalden$ time ../../obj-i386-apple-darwin8.11.1/dist/bin/js /tmp/object.js 

real    0m3.528s
user    0m3.294s
sys     0m0.049s
find-waldo-now:~/moz/js-tm/js/src jwalden$ time ../../obj-i386-apple-darwin8.11.1/dist/bin/js -j /tmp/object.js 

real    0m0.266s
user    0m0.168s
sys     0m0.016s
Attachment #367159 - Flags: review?(jorendorff)
Comment on attachment 367159 [details] [diff] [review]
Patch

No factoring required beyond what's already there, since the property cache test path computes atom correctly for JSOP_LENGTH. It has to, because the do_getprop_with_lval: code is shared in the interpreter for JSOP_LENGTH on values other than strings and dense arrays.

>+    if (OBJ_IS_ARRAY(cx, obj)) {
>+        if (OBJ_IS_DENSE_ARRAY(cx, obj)) {
>+            if (!guardDenseArray(obj, obj_ins))
>+                ABORT_TRACE("OBJ_IS_DENSE_ARRAY but not?!?");
>+        } else {
>+            if (!guardClass(obj, obj_ins, &js_SlowArrayClass, snapshot(MISMATCH_EXIT)))
>+                ABORT_TRACE("can't trace length property access on non-array");
>+        }
>+        v_ins = lir->ins1(LIR_i2f, stobj_get_fslot(obj_ins, JSSLOT_ARRAY_LENGTH));

The guardDenseArray and guardClass could use BRANCH_EXIT not MISMATCH_EXIT since you cover both paths here, and arrays do sometimes go from dense to slow. Why not?

/be
Attached patch With branch exits (obsolete) — Splinter Review
(In reply to comment #10)
 The guardDenseArray and guardClass could use BRANCH_EXIT not MISMATCH_EXIT
> since you cover both paths here, and arrays do sometimes go from dense to slow.
> Why not?

No particular reason other than unlikeliness.
Attachment #367159 - Attachment is obsolete: true
Attachment #367873 - Flags: review?(gal)
Attachment #367159 - Flags: review?(jorendorff)
Could you add an testcase for this transition?
I could probably reason out the exact side-exit count, but this works and is much easier to do.  Adding any one of the three objects to the end of the array doesn't change the triggers/exit counts in jitstats, which should be good enough for verification purposes, I think.
Attachment #367873 - Attachment is obsolete: true
Attachment #367938 - Flags: review?(gal)
Attachment #367873 - Flags: review?(gal)
Comment on attachment 367938 [details] [diff] [review]
With some transition testing from native code

>From: Jeff Walden <jwalden@mit.edu>
>
>Bug 469347 - TM: obj.length and slowArray.length don't trace.  NOT REVIEWED YET
>
>diff --git a/js/src/jstracer.cpp b/js/src/jstracer.cpp
>--- a/js/src/jstracer.cpp
>+++ b/js/src/jstracer.cpp
>@@ -9431,11 +9431,22 @@ TraceRecorder::record_JSOP_LENGTH()
>     }
> 
>     JSObject* obj = JSVAL_TO_OBJECT(l);
>-    if (!OBJ_IS_DENSE_ARRAY(cx, obj))
>-        ABORT_TRACE("only dense arrays supported");
>-    if (!guardDenseArray(obj, get(&l)))
>-        ABORT_TRACE("OBJ_IS_DENSE_ARRAY but not?!?");
>-    LIns* v_ins = lir->ins1(LIR_i2f, stobj_get_fslot(get(&l), JSSLOT_ARRAY_LENGTH));
>+    LIns* obj_ins = get(&l);
>+    LIns* v_ins;
>+    if (OBJ_IS_ARRAY(cx, obj)) {
>+        if (OBJ_IS_DENSE_ARRAY(cx, obj)) {
>+            if (!guardDenseArray(obj, obj_ins, BRANCH_EXIT))
>+                ABORT_TRACE("OBJ_IS_DENSE_ARRAY but not?!?");

Nit: maybe JS_NOT_REACHED?

>+        } else {
>+            if (!guardClass(obj, obj_ins, &js_SlowArrayClass, snapshot(BRANCH_EXIT)))
>+                ABORT_TRACE("can't trace length property access on non-array");
>+        }
>+        v_ins = lir->ins1(LIR_i2f, stobj_get_fslot(obj_ins, JSSLOT_ARRAY_LENGTH));
>+    } else {
>+        if (!OBJ_IS_NATIVE(obj))
>+            ABORT_TRACE("can't trace length property access on non-array, non-native object");
>+        return getProp(obj, obj_ins);
>+    }
>     set(&l, v_ins);
>     return true;
> }
>diff --git a/js/src/trace-test.js b/js/src/trace-test.js
>--- a/js/src/trace-test.js
>+++ b/js/src/trace-test.js
>@@ -4567,6 +4567,79 @@ function testLengthInString()
> testLengthInString.expected = true;
> test(testLengthInString);
> 
>+function testSlowArrayLength()
>+{
>+  var counter = 0;
>+  var a = [];
>+  a[10000000 - 1] = 0;
>+  for (var i = 0; i < a.length; i++)
>+    counter++;
>+  return counter;
>+}
>+testSlowArrayLength.expected = 10000000;
>+testSlowArrayLength.jitstats = {
>+  recorderStarted: 1,
>+  recorderAborted: 0,
>+  sideExitIntoInterpreter: 1
>+};
>+test(testSlowArrayLength);
>+
>+function testObjectLength()
>+{
>+  var counter = 0;
>+  var a = {};
>+  a.length = 10000000;
>+  for (var i = 0; i < a.length; i++)
>+    counter++;
>+  return counter;
>+}
>+testObjectLength.expected = 10000000;
>+testObjectLength.jitstats = {
>+  recorderStarted: 1,
>+  recorderAborted: 0,
>+  sideExitIntoInterpreter: 1
>+};
>+test(testObjectLength);
>+
>+function testChangingObjectWithLength()
>+{
>+  var obj = { length: 10 };
>+  var dense = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
>+  var slow = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; slow.slow = 5;
>+
>+  /*
>+   * The elements of objs constitute a De Bruijn sequence repeated 4x to trace
>+   * and run native code for every object and transition.
>+   */
>+  var objs = [obj, obj, obj, obj,
>+              obj, obj, obj, obj,
>+              dense, dense, dense, dense,
>+              obj, obj, obj, obj,
>+              slow, slow, slow, slow,
>+              dense, dense, dense, dense,
>+              dense, dense, dense, dense,
>+              slow, slow, slow, slow,
>+              slow, slow, slow, slow,
>+              obj, obj, obj, obj];
>+
>+  var counter = 0;
>+
>+  for (var i = 0, sz = objs.length; i < sz; i++)
>+  {
>+    var o = objs[i];
>+    for (var j = 0; j < o.length; j++)
>+      counter++;
>+  }
>+
>+  return counter;
>+}
>+testChangingObjectWithLength.expected = 400;
>+testChangingObjectWithLength.jitstats = {
>+  recorderAborted: 0,
>+  sideExitIntoInterpreter: 15 // empirically determined
>+};
>+test(testChangingObjectWithLength);
>+
> 
> /*****************************************************************************
>  *                                                                           *
Attachment #367938 - Flags: review?(gal) → review+
http://hg.mozilla.org/mozilla-central/rev/b3b8454e003d
Status: NEW → RESOLVED
Closed: 15 years ago
Resolution: --- → FIXED
http://hg.mozilla.org/tracemonkey/rev/b3b8454e003d for the original TM push
Whiteboard: fixed-in-tracemonkey
Target Milestone: --- → mozilla1.9.2
Blocks: 496377
    verified FIXED on builds:

    Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.9.1pre) Gecko/20090601
    Shiretoko/3.5pre (.NET CLR 3.5.30729) ID:20090601044045

    and

    Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.9.2a1pre) Gecko/20090601
    Minefield/3.6a1pre (.NET CLR 3.5.30729) ID:20090601041706

    There's a subsequent bug (bug 496377) when MozMill is installed onto any
    profile (via any platform and whether or not jit is enabled/disabled).
Status: RESOLVED → VERIFIED
No longer blocks: 496377
cvsroot/mozilla/js/tests/js1_8_1/trace/trace-test.js,v  <--  trace-test.js
new revision: 1.14; previous revision: 1.13

/cvsroot/mozilla/js/tests/shell.js,v  <--  shell.js
testSlowArrayLength|testObjectLength|testChangingObjectWithLength
Flags: in-testsuite+
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Creator:
Created:
Updated:
Size: