Open Bug 1222086 Opened 9 years ago Updated 2 years ago

Block JavaScript parser thread when source code characters are not available

Categories

(Core :: JavaScript Engine, defect)

defect

Tracking

()

Performance Impact low

People

(Reporter: yury, Unassigned)

References

(Blocks 1 open bug)

Details

(Keywords: perf)

Attachments

(4 files, 16 obsolete files)

175 bytes, application/x-javascript
Details
30.82 KB, patch
djvj
: review+
Details | Diff | Splinter Review
12.47 KB, patch
djvj
: review+
Details | Diff | Splinter Review
42.60 KB, patch
djvj
: review+
Details | Diff | Splinter Review
So the idea is to block parsing thread when end of available source code stream is found. Currently the Parser logic depends on a JavaScript source code to be a continuous char16 buffer -- we either need to replace the initial buffer or pre-allocate enough characters to fit at the beginning. Problem with the latter is that nsIChannel cannot guarantee or does not know total length (hints like Content-Length do not work since they are reflect the encoded content length -- zipped or charset).

Pros:
- Minimal change in the Parser code base
- Practically no impact on the parsing speed
- No intermediate state to save 

Cons:
- Available single Parser thread will be blocked
- Hence more than one Parsing thread must be created (bad due to thread memory overheads) and use additional heuristic needed to detect which files will be processed using this mode
Assignee: nobody → ydelendik
Blocks: 1154987
Attachment #8683948 - Attachment description: Pass JS source as CharsStream object. → [WIP] 1. Pass JS source as CharsStream object.
Attached patch [WIP] 3. jsshell integration (obsolete) — Splinter Review
Attached file jsshell code example
Attachment #8683948 - Attachment is obsolete: true
Attachment #8683949 - Attachment is obsolete: true
Attached patch [WIP] 3. jsshell integration (obsolete) — Splinter Review
Attachment #8683951 - Attachment is obsolete: true
Attachment #8688180 - Attachment is obsolete: true
Attachment #8688181 - Attachment is obsolete: true
Attached patch 3. jsshell integration (obsolete) — Splinter Review
Attachment #8688182 - Attachment is obsolete: true
https://treeherder.mozilla.org/#/jobs?repo=try&revision=1033608147f9

I run Parsemark to see if there is a side effect from the TokenStream redesign -- there was no regression, "Meh." on all files.
Attachment #8689048 - Flags: review?(kvijayan)
Attachment #8689049 - Flags: review?(kvijayan)
Attachment #8689052 - Flags: review?(kvijayan)
Ah!  Just saw these review requests.  Will do them next week.  Sorry for lateness.
Comment on attachment 8689048 [details] [diff] [review]
1. Pass JS source as CharsStream object.

Review of attachment 8689048 [details] [diff] [review]:
-----------------------------------------------------------------

Nice.  Very clean and easy to understand.

::: js/src/builtin/RegExp.cpp
@@ +172,5 @@
>      }
>  
>      /* Steps 9-10. */
>      CompileOptions options(cx);
> +    frontend::TokenStream dummyTokenStream(cx, options, &frontend::NullCharsStream::null(), nullptr);

It seems slightly more readable here to have the NullCharsStream usage reflect the same syntactic structure as the usage of StaticCharsStream: namely, it gets stack-allocated and a pointer gets passed to the stack-allocated item.

::: js/src/frontend/TokenStream.h
@@ +309,5 @@
> +        *start = *end = nullptr;
> +        *final = true;
> +    }
> +
> +    static NullCharsStream& null() {

I'd remove this method and use the same calling convention for NullCharsStream as users of StaticCharsStream do: stack-allocate-and-pass-pointer, instead of having a single statically allocated instance.

Minor nit, though.  I don't feel strongly about it.

@@ +926,5 @@
> +            if (MOZ_LIKELY(ptr < limit_))
> +                return true;
> +            if (final_)
> +                return false;
> +            requestMoreChars();

|requestMoreChars| is guaranteed to grow the buffer, right?  Might be good to assert that after the call, just to make sure that implementors of |CharsStream| are fulfilling that contract.
Attachment #8689048 - Flags: review?(kvijayan) → review+
Comment on attachment 8689048 [details] [diff] [review]
1. Pass JS source as CharsStream object.

Review of attachment 8689048 [details] [diff] [review]:
-----------------------------------------------------------------

One additional note: The |final| parameter/field might better be named something like |streamComplete|.
Comment on attachment 8689049 [details] [diff] [review]
2. Allows OffThreadCompileCallback handle chars in chunks

Review of attachment 8689049 [details] [diff] [review]:
-----------------------------------------------------------------

::: js/src/jsapi.h
@@ +746,5 @@
>          length_(dataLength),
> +        requestChars_(requestChars),
> +        requestCharsData_(requestCharsData),
> +        ownsChars_(ownership == GiveOwnership),
> +        final_(!requestChars)

Nit: |final_| might be better named |streamComplete_|.

@@ +796,5 @@
>          ownsChars_ = false;
>          return const_cast<char16_t*>(data_);
>      }
>  
> +    bool final() const {

see above comment about naming (streamComplete()).
Attachment #8689049 - Flags: review?(kvijayan) → review+
Comment on attachment 8689052 [details] [diff] [review]
3. jsshell integration

Review of attachment 8689052 [details] [diff] [review]:
-----------------------------------------------------------------

::: js/src/jit-test/tests/basic/offThreadCompileScript-03.js
@@ +12,5 @@
> +
> +if (helperThreadCount() === 0)
> +  quit(0);
> +
> +offThreadCompileScript('Math.sin(M', {}, false);

This test case would be clearer if the entire "subscript" being compiled was a single string, and there was an array that listed the indexes at which the script should be split up into pieces, and the repeated calls to |appendOffThreadScript| and |waitSome| were instead a loop over that index array, using substring() to pick off pieces of the script to submit in parts.

::: js/src/shell/js.cpp
@@ +5090,5 @@
>  "  Wait for off-thread compilation to complete. If an error occurred,\n"
>  "  throw the appropriate exception; otherwise, run the script and return\n"
>  "  its value."),
>  
> +JS_FN_HELP("appendOffThreadScript", appendOffThreadScript, 0, 0,

Nice.  This is very clever.
Attachment #8689052 - Flags: review?(kvijayan) → review+
Attachment #8689048 - Attachment is obsolete: true
(In reply to Kannan Vijayan [:djvj] from comment #14)
> Comment on attachment 8689048 [details] [diff] [review]
> 1. Pass JS source as CharsStream object.
> 
> Review of attachment 8689048 [details] [diff] [review]:
> -----------------------------------------------------------------
> 
> One additional note: The |final| parameter/field might better be named
> something like |streamComplete|.

Fixed.

(In reply to Kannan Vijayan [:djvj] from comment #13)
> Comment on attachment 8689048 [details] [diff] [review]
> 1. Pass JS source as CharsStream object.
> 
> Review of attachment 8689048 [details] [diff] [review]:
> -----------------------------------------------------------------
> 
> Nice.  Very clean and easy to understand.
> 
> ::: js/src/builtin/RegExp.cpp
> @@ +172,5 @@
> >      }
> >  
> >      /* Steps 9-10. */
> >      CompileOptions options(cx);
> > +    frontend::TokenStream dummyTokenStream(cx, options, &frontend::NullCharsStream::null(), nullptr);
> 
> It seems slightly more readable here to have the NullCharsStream usage
> reflect the same syntactic structure as the usage of StaticCharsStream:
> namely, it gets stack-allocated and a pointer gets passed to the
> stack-allocated item.
> 
> ::: js/src/frontend/TokenStream.h
> @@ +309,5 @@
> > +        *start = *end = nullptr;
> > +        *final = true;
> > +    }
> > +
> > +    static NullCharsStream& null() {
> 
> I'd remove this method and use the same calling convention for
> NullCharsStream as users of StaticCharsStream do:
> stack-allocate-and-pass-pointer, instead of having a single statically
> allocated instance.
> 
> Minor nit, though.  I don't feel strongly about it.

Fixed.

> 
> @@ +926,5 @@
> > +            if (MOZ_LIKELY(ptr < limit_))
> > +                return true;
> > +            if (final_)
> > +                return false;
> > +            requestMoreChars();
> 
> |requestMoreChars| is guaranteed to grow the buffer, right?  Might be good
> to assert that after the call, just to make sure that implementors of
> |CharsStream| are fulfilling that contract.

requestMoreChars is non-virtual, so it's safe to add this check at the requestMoreChars method body.
And the described check is already present there. Is it what you had in mind?
Flags: needinfo?(kvijayan)
(In reply to Kannan Vijayan [:djvj] from comment #15)
> Comment on attachment 8689049 [details] [diff] [review]
> 2. Allows OffThreadCompileCallback handle chars in chunks
> 
> Review of attachment 8689049 [details] [diff] [review]:
> -----------------------------------------------------------------
> 
> ::: js/src/jsapi.h
> @@ +746,5 @@
> >          length_(dataLength),
> > +        requestChars_(requestChars),
> > +        requestCharsData_(requestCharsData),
> > +        ownsChars_(ownership == GiveOwnership),
> > +        final_(!requestChars)
> 
> Nit: |final_| might be better named |streamComplete_|.
> 
> @@ +796,5 @@
> >          ownsChars_ = false;
> >          return const_cast<char16_t*>(data_);
> >      }
> >  
> > +    bool final() const {
> 
> see above comment about naming (streamComplete()).

Fixed.
Attachment #8689049 - Attachment is obsolete: true
Attached patch Part 3. jsshell integration (obsolete) — Splinter Review
(In reply to Kannan Vijayan [:djvj] from comment #16)
> Comment on attachment 8689052 [details] [diff] [review]
> 3. jsshell integration
> 
> Review of attachment 8689052 [details] [diff] [review]:
> -----------------------------------------------------------------
> 
> ::: js/src/jit-test/tests/basic/offThreadCompileScript-03.js
> @@ +12,5 @@
> > +
> > +if (helperThreadCount() === 0)
> > +  quit(0);
> > +
> > +offThreadCompileScript('Math.sin(M', {}, false);
> 
> This test case would be clearer if the entire "subscript" being compiled was
> a single string, and there was an array that listed the indexes at which the
> script should be split up into pieces, and the repeated calls to
> |appendOffThreadScript| and |waitSome| were instead a loop over that index
> array, using substring() to pick off pieces of the script to submit in parts.
>

Fixed.

Last try https://treeherder.mozilla.org/#/jobs?repo=try&revision=4228eb759fbd
Attachment #8689052 - Attachment is obsolete: true
Fixed "js/src/frontend/BytecodeCompiler.cpp:49:5: error: bad implicit conversion constructor for 'SourceBufferCharsStream'"

(https://treeherder.mozilla.org/#/jobs?repo=try&revision=9d2607159a52)
Attachment #8693845 - Attachment is obsolete: true
The hazards test was failing (https://treeherder.mozilla.org/#/jobs?repo=try&revision=4228eb759fbd&selectedJob=14272132):

Function '_ZN2js8frontend6ParserINS0_16FullParseHandlerEEC1EPNS_16ExclusiveContextEPNS_9LifoAllocERKN2JS22ReadOnlyCompileOptionsEPNS0_11CharsStreamEbPNS1_INS0_18SyntaxParseHandlerEEEPNS_10LazyScriptE *INTERNAL* |js::frontend::Parser<ParseHandler>::Parser(js::ExclusiveContext*, js::LifoAlloc*, const JS::ReadOnlyCompileOptions&, js::frontend::CharsStream*, bool, js::frontend::Parser<js::frontend::SyntaxParseHandler>*, js::LazyScript*) [with ParseHandler = js::frontend::FullParseHandler]' has unrooted 'lazyOuterFunction' of type 'js::LazyScript*' live across GC call '_ZN2js8frontend11TokenStreamC1EPNS_16ExclusiveContextERKN2JS22ReadOnlyCompileOptionsEPNS0_11CharsStreamEPNS0_16StrictModeGetterE|void js::frontend::TokenStream::TokenStream(js::ExclusiveContext*, JS::ReadOnlyCompileOptions*, js::frontend::CharsStream*, js::frontend::StrictModeGetter*)' at js/src/frontend/Parser.cpp:662
    ...
    js/src/frontend/Parser.cpp:662: Call(6,7, this*.tokenStream.TokenStream(cx*,options*,chars*,__temp_1*.field:0)) [[GC call]]
    ....
    js/src/frontend/Parser.cpp:662: Call(22,23, this*.handler.FullParseHandler(cx*,alloc*,this*.tokenStream,syntaxParser*,lazyOuterFunction*))

I changed the lazyOuterFunction type from LazyScript* to Handle<LazyScript*> for the ParseHandler

(Please also find requestMoreChars body for contract verification of CharsStream.request implementation)
Attachment #8693804 - Attachment is obsolete: true
Flags: needinfo?(kvijayan)
Attachment #8694406 - Flags: review?(kvijayan)
rebased
Attachment #8693862 - Attachment is obsolete: true
Attachment #8694406 - Flags: review?(kvijayan) → review+
Found an issue with lazy function parsing: ScriptSource::setSourceCopy was getting initial/incomplete portion of the source and it was never updated. Fixing that by calling setSourceCopy after entire source is loaded.
Attachment #8694407 - Attachment is obsolete: true
Attachment #8700597 - Flags: review?(kvijayan)
Updated/added a test cases for the finding above.

https://treeherder.mozilla.org/#/jobs?repo=try&revision=a4f9bb47eb0c
Attachment #8694408 - Attachment is obsolete: true
Attachment #8700599 - Flags: review?(kvijayan)
Attachment #8700597 - Flags: review?(kvijayan) → review+
Attachment #8700599 - Flags: review?(kvijayan) → review+
Found yesterday that the similar lazy function parsing issue happens when TokenStream's seek/tell are used. The TokenBuf.setOffsetOfNextRawChar (and requestMoreChars) is modified to sync chars buffer.

https://treeherder.mozilla.org/#/jobs?repo=try&revision=4b98db5f463d
Attachment #8694406 - Attachment is obsolete: true
Attachment #8702607 - Flags: review?(kvijayan)
Comment on attachment 8702607 [details] [diff] [review]
1-bug1222086-charsstream.patch

Review of attachment 8702607 [details] [diff] [review]:
-----------------------------------------------------------------

::: js/src/frontend/BytecodeCompiler.cpp
@@ +233,2 @@
>                               /* foldConstants = */ false, (Parser<SyntaxParseHandler>*) nullptr,
> +                             Handle<LazyScript*>(nullptr));

Just a question.  There are many places where you convert lazyscript from being a raw pointer to rooted behaviour (using Rooted and Handle<LazyScript*>).  Why is this necessary now when it wasn't before?
Attachment #8702607 - Flags: review?(kvijayan) → review+
Whiteboard: [qf]
Whiteboard: [qf] → [qf-]
Whiteboard: [qf-] → [qf:p1]
Whiteboard: [qf:p1] → [qf:p3]
Keywords: perf
Performance Impact: --- → P3
Whiteboard: [qf:p3]
Assignee: ydelendik → nobody
Severity: normal → S3
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: