Closed Bug 517199 Opened 15 years ago Closed 15 years ago

Typed GC free lists

Categories

(Core :: JavaScript Engine, enhancement)

enhancement
Not set
normal

Tracking

()

RESOLVED FIXED

People

(Reporter: igor, Assigned: igor)

References

Details

(Whiteboard: fixed-in-tracemonkey)

Attachments

(1 file, 8 obsolete files)

+++ This bug was initially created as a followup for bug #516226 comment 2 +++

Currently GC free lists are based on the size of allocated things. This requires to store the type information in the GC flags. As a consequence during finalization the code has to take an extra dispatch on the thing's type to choose the finalizer. 

To remove this overhead a typed free lists can be used with one free list per finalizer. It would allow to to choose the finalizer outside the main sweep loop.
Depends on: 517749
Attached patch v1 - newborn refactoring (obsolete) — Splinter Review
Here is a preparation patch to refactor newborn roots in preparation for typed lists.
Attachment #402319 - Attachment is patch: true
Attachment #402319 - Attachment mime type: application/octet-stream → text/plain
Attached patch v1 - typed free lists (obsolete) — Splinter Review
Here is an implementation itself that uses template inline function to choose the finalizer only once per allocation list. For the following test case where the GC is dominated by the finalization the patch speeds up the GC timing by 10-15% depending on CPU:

function test()
{
    var array = [];
    for (var j = 0; j != 12; ++j) {
        var array2 = [];
        for (var i = 0; i != 1e5; ++i) {
            array2.push({});
            array2.push("test"+i);
        }
        array.push(array2);
    }
    return array;
}

test();
var t = Date.now();
gc();
print(Date.now() - t);

The benchmark is rather sensitive to the amount of if checks in the finalizer. It should possible to make things run faster via using few more typed free lists like separated free lists for dependent strings or fast arrays. But that can be costly from the heap fragmentation point of view especially in view of bigger GC arenas.
OS: Mac OS X → All
Hardware: x86 → All
Attached patch v2 - newborn refactoring (obsolete) — Splinter Review
This patch decouples newborn weak roots from the GCX_ enumeration and avoids passing gcflags to various js_NewGCSomething functions to minimize the following patch.
Attachment #402319 - Attachment is obsolete: true
Attachment #403129 - Flags: review?(brendan)
Attached patch v2 - typed free lists (obsolete) — Splinter Review
The patch implements that idea about typed GC lists to optimize the finalization. The patch uses specialized arena list finalizers to avoid checks for external string type and cx->debugHooks->objectHook which is particularly costly. Still there are few more possibilities for faster finalization. I will file them as separated bugs.
Attachment #402322 - Attachment is obsolete: true
Attachment #403132 - Flags: review?(brendan)
SFX uses a template class for each GC arena type. Do you think thats worth a try? It generates more specialized code.
(In reply to comment #5)
> SFX uses a template class for each GC arena type. Do you think thats worth a
> try? It generates more specialized code.

What I have seen from benchmarks like in the comment 2 is that the issue is not the choice of the finalizer itself. Rather it is a complexity of the finalizer for each particular type that hurts most.

For example, in that benchmark avoiding cx->debugHooks->objectHook check was responsible for almost 30% of the speedup in the object finalizer. Another example is the check for the deflated bit in the string finalizer. If it would be possible to avoid it, a gain like 10% speedup of string finalization should be possible. So using more specialized GC type is just one of the ways to make things faster.
Attached patch v3 - newborn refactoring (obsolete) — Splinter Review
The new version fixes aliasing warning in v2 when it stored JSFunction in newbornObject. v3 just extends JSFunction from JSObject to fix that.
Attachment #403284 - Flags: review?(brendan)
Attached patch v3 - typed free lists (obsolete) — Splinter Review
Attachment #403129 - Attachment is obsolete: true
Attachment #403132 - Attachment is obsolete: true
Attachment #403285 - Flags: review?(brendan)
Attachment #403129 - Flags: review?(brendan)
Attachment #403132 - Flags: review?(brendan)
Comment on attachment 403284 [details] [diff] [review]
v3 - newborn refactoring

> JS_NewExternalString(JSContext *cx, jschar *chars, size_t length, intN type)
> {
>-    JSString *str;
>-
>     CHECK_REQUEST(cx);
>-    JS_ASSERT((uintN) type < (uintN) (GCX_NTYPES - GCX_EXTERNAL_STRING));
>+    JS_ASSERT(uint32(type) < JS_EXTERNAL_STRING_LIMIT);

Is there a need for the uint32 type here rather than uintN or just unsigned? On an ILP64 target this will cost and appear to change semantics, even if it doesn't for reasons that can't be proven with types.

> 
>-    str = js_NewGCString(cx, (uintN) type + GCX_EXTERNAL_STRING);
>+    JSString *str = js_NewGCExternalString(cx, uint32(type));

Ditto (see below on js_NewGCExternalString parameter type).

>+template <typename T, typename NewbornType> static JS_INLINE T*

Nit, pre-existing: newline instead of space before static.

>@@ -1990,17 +1990,17 @@ testReservedObjects:
>             *flagp = GCF_FINAL;
>             goto fail;
>         }
>     } else {
>         /*
>          * No local root scope, so we're stuck with the old, fragile model of
>          * depending on a pigeon-hole newborn per type per context.
>          */
>-        cx->weakRoots.newborn[flags & GCF_TYPEMASK] = thing;
>+        *newbornRoot = (T *) thing;

This is interesting in view of the added NewbornType type-parameter to the template. Is it as strong a claim as we can make about the necessary relationship between T and NewbornType?

>+JSObject*

Space before * (pointer-to declarator mode), same nit throughout.

>+js_NewGCExternalString(JSContext *cx, uint32 type)

Is it necessary or desirable to use uint32 here? A much narrower int type could be used, to match domain, but our general approach is to use natural unsigned and int types for such parameters.

>+JSFunction*
>+js_NewGCFunction(JSContext *cx)
> {
>-    return NewGCThing<JSXML>(cx, flags);
>+    return NewGCThing<JSFunction>(cx, GCX_OBJECT,
>+                                      &cx->weakRoots.newbornObject);

Indentation nit. Could slightly exceed old line length max?

>+struct JSFunction: JSObject {

Space before :, and don't we want public JSObject as the base specifier?

/be
Comment on attachment 403285 [details] [diff] [review]
v3 -  typed free lists

Looks pretty good, but JSALK_ hurts my ears/eyes.

Observing MapArenaListKindToTrace I wonder if MapArenaListToTraceKind would be better. This brings to light JSTRACE_ as the prefix (now-misleading in light of trace-JITting, but still more readable than a JSTK_ or JSTRACEKIND_ prefix).

How about working from GC THING KIND or (better) GC TYPE as the "name seed"?

GC_TYPE_OBJECT
GC_TYPE_FUNCTION
GC_TYPE_STRING
GC_TYPE_XML
GC_TYPE_EXTERNAL_STRING*

Looking for simpler names, fewer magic TLAs.

/be
(In reply to comment #10)
> Looks pretty good, but JSALK_ hurts my ears/eyes.

Agreed, although I bet it would do wonders for stiff legs!

Why not FinalizerKind and FINALIZE_OBJECT, FINALIZE_XML, FINALIZE_FUN, &c.?  Throwing out ideas here, less abbreviation is much better.
The "type" is more than the finalizer, although that's the narrow interpretation.

/be
(In reply to comment #10)
> How about working from GC THING KIND or (better) GC TYPE as the "name seed"?

The purpose of that enumeration is just to give names for indexes into the array of free lists. The array is rather specific since it does not include doubles. Moreover, it may not include dependent strings if it would be worth to allocate them on a separated list to avoid running the finalizer for them. So it is not a GC type.

> Why not FinalizerKind and FINALIZE_OBJECT, FINALIZE_XML, FINALIZE_FUN, &c.? 

The names do not corresponds to the finalizers since there are 2 finalizers for object and function and one for all external strings. Still this is a good hint since those list names are for lists containing GC things with finalizers.

So what about JSFAL_ or JSFALK_ aka finalizable arena list kind ?
(In reply to comment #13)
> > Why not FinalizerKind and FINALIZE_OBJECT, FINALIZE_XML, FINALIZE_FUN, &c.? 

I started to like this suggesting since "finalize" can be interpreted to "finalize by what ever means but do finalize".
(In reply to comment #9)
> >+template <typename T, typename NewbornType> static JS_INLINE T*
NewbornType **newbornRoot
...
> >+        *newbornRoot = (T *) thing;
> 
> This is interesting in view of the added NewbornType type-parameter to the
> template. Is it as strong a claim as we can make about the necessary
> relationship between T and NewbornType?

This requires that NewbornType* is assignable from T*. Since T cannot be void (the function calls sizeof(T)) this implies that NewbornType is either T or subclasses T.
Attached patch v4 - newborn refactoring (obsolete) — Splinter Review
the new version addresses the comment 9
Attachment #403284 - Attachment is obsolete: true
Attachment #403503 - Flags: review?(brendan)
Attachment #403284 - Flags: review?(brendan)
Attached patch v4 - typed free lists (obsolete) — Splinter Review
In the new patch I use FINALIZE_ as the prefix following the suggestion from the comment 11.
Attachment #403285 - Attachment is obsolete: true
Attachment #403504 - Flags: review?(brendan)
Attachment #403285 - Flags: review?(brendan)
Blocks: 519476
Attachment #403503 - Flags: review?(brendan) → review+
Comment on attachment 403504 [details] [diff] [review]
v4 -  typed free lists

>+JS_STATIC_ASSERT(FINALIZE_EXTERNAL_STRING_LAST - FINALIZE_EXTERNAL_STRING0 ==
>+                JS_EXTERNAL_STRING_LIMIT - 1);

Nit: indent second line one more space.

r=me with that -- good patch.

/be
Attachment #403504 - Flags: review?(brendan) → review+
Blocks: 519902
landed with two changesets:

http://hg.mozilla.org/tracemonkey/rev/19b4c1cacdb8
http://hg.mozilla.org/tracemonkey/rev/47619e6bad9a
No longer blocks: 519902
Whiteboard: fixed-in-tracemonkey
Blocks: 519902
The error logs for failed windows tests shows at the end:

 END OF RUN - NOW DOING SCHEDULED REBOOT; FOLLOWING ERROR MESSAGE EXPECTED 

I am not sure how this could affect the tests.
The try server shows a success on Windows. I backed out the back out and relanded the patch assuming that failures on Windows was due to the reboot.
I backed out the second patch again due to persistent Windows-only failures on tracemonkey's tinderboxes. If the first part, the newborns changes, will stay, I will file a separated bug for that so that can be tracked separately.
Whiteboard: fixed-in-tracemonkey
Depends on: 520046
Attached patch v5Splinter Review
My current theory about Windows failures is that the patch went too far optimizing the finalizers and made a bug that is visible only on Windows. So here is v4 that just optimizes the switch over GC thing kind without changing the finalizers itself. It is still a win for the benchmarks, so I plan to land this and optimize the finalizers in a different bug. Note also that newborns refactoring was landed as a part of bug 520046.
Attachment #403503 - Attachment is obsolete: true
Attachment #403504 - Attachment is obsolete: true
Attachment #404242 - Flags: review+
http://hg.mozilla.org/tracemonkey/rev/ee059eb9d9e2
Whiteboard: fixed-in-tracemonkey
http://hg.mozilla.org/mozilla-central/rev/ee059eb9d9e2
Status: NEW → RESOLVED
Closed: 15 years ago
Resolution: --- → FIXED
Blocks: 536734
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Creator:
Created:
Updated:
Size: