Skip to content

Fix exp-time parse backtrack by saving if '[' starts Array or Dictionary #20502

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from

Conversation

modelorganism
Copy link
Contributor

Save the results of discovering whether a given '[' is the start of an Array or a Dictionary. Remembering this prevents parseExprCollection() from having to parse the first sub-expr in square brackets {2^n} times, where n is # of nested square brackets.

Swift in parseExprCollection parses the first expr in a [x..], a square-bracketed expr-list, twice. Once to see if there is a colon after x or not, [x : v ..] vs, [x , y ..], then a second parse 'for real'. If there is a [..] inside of the first expr of another [..], [[..]...], the first expr of the inner list is parsed 4-times, or more generally 2^n, where n is the depth of nesting.

This PR adds a structure to class Parser, ArrayOrDictStartLocCache, which remembers whether each [ starts a Array or Dict for as long any surrounding [...] is being parsed. For the duration of a [...] being parsed this cached value will be used in place a performing a subsequent speculative parse. There are still 2 parses of each head of a [h ...], but not 2^n. Once the outermost ] is encountered, the information is discarded as the parser can longer backtrack over the subexprs.

Tests ran successfully on Mac at this rev (that is with this change after Merge pull request #20500 from xedin/additional-tests-for-autoclosure…)

Resolves SR-9220.

Save the results of discovering whether a given '[' is the start of an Array or a Dictionary. Remembering this prevents `parseExprCollection()` from having to parse the first sub-expr in square brackets {2^n} times, where n is # of nested square brackets.
]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]] ]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]
]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]] ]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]
]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]] ]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]
]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]] ]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder how quickly does this parse now?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let l = [[[[[[[[[[[[[[[ ]]]]]]]]]]]]]]]
0.09 real 0.06 user 0.02 sys
let l = [[[[[[[[[[[[[[[[ ]]]]]]]]]]]]]]]]
0.11 real 0.07 user 0.02 sys
let l = [[[[[[[[[[[[[[[[[ ]]]]]]]]]]]]]]]]]
0.13 real 0.10 user 0.02 sys
let l = [[[[[[[[[[[[[[[[[[ ]]]]]]]]]]]]]]]]]]
0.18 real 0.14 user 0.03 sys
let l = [[[[[[[[[[[[[[[[[[[ ]]]]]]]]]]]]]]]]]]]
0.27 real 0.23 user 0.03 sys
let l = [[[[[[[[[[[[[[[[[[[[ ]]]]]]]]]]]]]]]]]]]]
0.47 real 0.42 user 0.04 sys
let l = [[[[[[[[[[[[[[[[[[[[[ ]]]]]]]]]]]]]]]]]]]]]
0.86 real 0.78 user 0.06 sys
let l = [[[[[[[[[[[[[[[[[[[[[[ ]]]]]]]]]]]]]]]]]]]]]]
1.69 real 1.50 user 0.11 sys
let l = [[[[[[[[[[[[[[[[[[[[[[[ ]]]]]]]]]]]]]]]]]]]]]]]
3.30 real 2.94 user 0.21 sys
let l = [[[[[[[[[[[[[[[[[[[[[[[[ ]]]]]]]]]]]]]]]]]]]]]]]]
6.47 real 5.85 user 0.39 sys
let l = [[[[[[[[[[[[[[[[[[[[[[[[[ ]]]]]]]]]]]]]]]]]]]]]]]]]
13.47 real 12.44 user 0.80 sys
let l = [[[[[[[[[[[[[[[[[[[[[[[[[[ ]]]]]]]]]]]]]]]]]]]]]]]]]]
26.47 real 24.59 user 1.61 sys
let l = [[[[[[[[[[[[[[[[[[[[[[[[[[[ ]]]]]]]]]]]]]]]]]]]]]]]]]]]
51.32 real 47.20 user 3.63 sys
let l = [[[[[[[[[[[[[[[[[[[[[[[[[[[[ ]]]]]]]]]]]]]]]]]]]]]]]]]]]]
103.45 real 95.47 user 7.31 sys
let l = [[[[[[[[[[[[[[[[[[[[[[[[[[[[[ ]]]]]]]]]]]]]]]]]]]]]]]]]]]]]
280.94 real 195.70 user 14.23 sys
let l = [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ ]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]
463.93 real 386.87 user 27.48 sys

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Huh, 463.93 is still pretty long. Maybe it's the best we can do.

@rintaro rintaro self-assigned this Nov 12, 2018
@rintaro
Copy link
Member

rintaro commented Nov 12, 2018

Thanks for tackling this!
This is clever idea. But, we want to eliminate backtracking here. Because parsing expression might have some side effects, and backtracking might not be able to cancel them. Not sure how it's bad, but still.

Previously, we weren't using backtracking:
https://github.com/apple/swift/blob/bb157a070ec6534e4b534456d208b03adc07704b/lib/Parse/ParseExpr.cpp#L3042-L3058
However, when we introduced libSyntax parsing, we started to use backtracking here because parseList() which is used in parseExprDictionary()/parseExprArray() could not insert pre-parsed expression to the list. But using SyntaxParsingContext::popIf() in parseExprCollection() and SyntaxParsingContext::addSyntax() in parseExprDictionary()/parseExprArray(), we can inject pre-parsed expression to the list.

@modelorganism do you want to try that?

@rintaro rintaro removed their assignment Nov 12, 2018
@modelorganism
Copy link
Contributor Author

@modelorganism do you want to try that?

Yes let me give that a shot.

@rintaro
Copy link
Member

rintaro commented Dec 21, 2018

@modelorganism Sorry, I'm taking over this in #21487

@rintaro rintaro closed this Jan 7, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants