Skip to content
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

[Release/3.1] ReplacingEV uses two arrays instead of dictionary (#19858) #19867

Merged
merged 3 commits into from
Feb 14, 2020

Conversation

roji
Copy link
Member

@roji roji commented Feb 10, 2020

ReplacingExpressionVisitor held original and replacement expressions in a dictionary. Unfortunately that means hash code calculation occurs exponentially, as each TryGetValue on the dictionary must traverse the entire tree.

Replaced with two lists and a simple Equals check, which is much faster.

Fixes #19737

(cherry picked from commit 7c9fc8a)

ReplacingExpressionVisitor held original and replacement expressions
in a dictionary. Unfortunately that means hash code calculation occurs
exponentially, as each TryGetValue on the dictionary must traverse
the entire tree.

Replaced with two lists and a simple Equals check, which is much
faster.

Fixes #19737

(cherry picked from commit 7c9fc8a)
@roji roji changed the title ReplacingEV uses two arrays instead of dictionary (#19858) [Release/3.1] ReplacingEV uses two arrays instead of dictionary (#19858) Feb 10, 2020
@roji
Copy link
Member Author

roji commented Feb 10, 2020

Description

When some operators (e.g. Where) are composed over a deep, self-referencing LINQ query tree, hash code calculation can make translation time exponential and make compilation grind to a halt.

Customer Impact

Query compilation can hang for extremely long times before even sending a SQL query to the database.

How found

Reported by a customer.

Test coverage

Perf issue, only produced by some specific operators (e.g. Where) composed over a deep, self-referencing query tree.

We will add a test for this scenario to our perf tests.

Regression?

Query code has changed significantly since 2.x, so this particular case may or may not be a regression.

Risk

Low. Controlled, pointed change to a simple expression visitor which replaces nodes in the tree.

Also, fix is on by default, but quirk switch is present to revert back to old behavior.

@ajcvickers
Copy link
Member

@roji Can you "quirk" the fix. That is, put the change behind an `AppContext.TryGetSwitch" like Smit has done here: https://github.com/dotnet/efcore/pull/19871/files#diff-baecacd4cd9354c21b22e390097a8d79R511

The new behavior should be enabled by default. When the quirk switch is set, then we should revert to the pre-patch behavior. This allows customers to switch back to the old behavior if we accidentally broke something. These switches never make it into master--they are specifically there for the patches only.

@roji
Copy link
Member Author

roji commented Feb 11, 2020

@ajcvickers done, though this is a bit more complicated then just one condition. Tested that with and without quirks things work as expected, but would be good to get another pair of eyes.

@ajcvickers ajcvickers added this to the 3.1.x milestone Feb 13, 2020
@ajcvickers
Copy link
Member

@roji Can you create an issue to add this scenario to the perf tests? We should then have some coverage that this doesn't regress.

@roji
Copy link
Member Author

roji commented Feb 13, 2020

I was just thinking that, opened #19908.

@ajcvickers
Copy link
Member

@roji @smitpatel Approved by Tactics and branches are open; let's try to get this merged today ASAP since it's already night for you. Not that that seems to matter. ;-)

@roji roji merged commit 0344ad7 into release/3.1 Feb 14, 2020
@roji roji deleted the ReplaceFaster3.1 branch February 14, 2020 23:27
@ajcvickers ajcvickers removed this from the 3.1.3 milestone Feb 15, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants