-
Notifications
You must be signed in to change notification settings - Fork 63
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
Adds performance optimizations for ExprCallDynamic #1388
Conversation
Conformance comparison report-Cross Engine
Number failing in both: 264 Number passing in eval engine but fail in legacy engine: 172 Number failing in eval engine but pass in legacy engine: 758 Conformance comparison report-Cross Commit-EVAL
Number failing in both: 996 Number passing in Base (609f8b8) but now fail: 26 Number failing in Base (609f8b8) but now pass: 4 Click here to see
Conformance comparison report-Cross Commit-LEGACY
Number failing in both: 435 Number passing in Base (609f8b8) but now fail: 1 Number failing in Base (609f8b8) but now pass: 0 Click here to see
|
Codecov ReportAll modified and coverable lines are covered by tests ✅
Additional details and impacted files@@ Coverage Diff @@
## partiql-eval #1388 +/- ##
===============================================
Coverage ? 50.32%
Complexity ? 1045
===============================================
Files ? 165
Lines ? 13129
Branches ? 2452
===============================================
Hits ? 6607
Misses ? 5862
Partials ? 660
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. |
Adds casts for nulls, fixes toSet() for candidates, and increases performance Fixes dynamic candidate ordering Fixes NULL/MISSING equality
f397a84
to
7d3aadd
Compare
If we know that the function will be always null, in my opinion we should type the function to For two reasons:
|
True. Good call out. Seemingly our typing of casts is wrong. From the SQL:1999 Spec Section 6.22:
Since we model NULL as a distinct type, we should still insert the casts, but the typer really should fold the CAST (since we know the input is null). If you're aligned, I can fix the typing of rexOpCast. |
private val args: Array<Operator.Expr> | ||
) : Operator.Expr { | ||
|
||
private val candidateIndex = CandidateIndex.All(candidates) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I say create the index in the Compiler and pass to the ExprCallDynamic. In this scenario, we're passing an arg into the constructor just to be passed to another constructor.
partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprCallDynamic.kt
Show resolved
Hide resolved
partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprCallDynamic.kt
Outdated
Show resolved
Hide resolved
@@ -729,7 +729,7 @@ class PlanTyperTestsPorted { | |||
SuccessTestCase( | |||
name = "BITWISE_AND_NULL_OPERAND", | |||
query = "1 & NULL", | |||
expected = StaticType.NULL, | |||
expected = StaticType.unionOf(StaticType.INT4, StaticType.NULL), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not StaticType.NULL?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The value will be NULL, however, what is the type of the null value? I'm using our normal rules for type/function precedence to resolve the output types. See the following PostgreSQL DB Fiddle Example.
partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprCallDynamic.kt
Show resolved
Hide resolved
partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprCallDynamic.kt
Outdated
Show resolved
Hide resolved
partiql-spi/src/main/kotlin/org/partiql/spi/connector/sql/builtins/FnNot.kt
Outdated
Show resolved
Hide resolved
partiql-planner/src/main/kotlin/org/partiql/planner/internal/FnMatch.kt
Outdated
Show resolved
Hide resolved
partiql-eval/src/main/kotlin/org/partiql/eval/internal/operator/rex/ExprCallDynamic.kt
Show resolved
Hide resolved
null -> { | ||
if (!(inputType == parameterType || inputType == PartiQLValueType.NULL || parameterType == PartiQLValueType.ANY)) { | ||
return false | ||
init { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Curious if we see any notable CPU utilization jump from this init
block since there is more work when an ExprCallDynamic
is initialized. Wondering if there are any situations in which this construction of the CandidateIndex
results in worse evaluation performance/utilization (e.g. if candidates
is just a list of two elements)? I assume this change is better in the general case in which there are more than just a few candidate elements though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good call out -- in the future, I'd say it'd be a good idea for the Compiler
to dictate what version of ExprCallDynamic
to create (the indexing O(1) vs iteration O(n)) depending on the number of candidates (and our own testing). As for init
by itself, it is only invoked at compilation.
For now, we know dynamic is quite expensive for our customers.
Relevant Issues
Description
CandidateIndex.All
that preserves the initial order of the candidate functions, however, it internally wraps a list ofCandidateIndex.Direct
andCandidateIndex.Indirect
.CandidateIndex.Direct
holds a map between runtime parameter types and the associated candidate to invoke.CandidateIndex.Indirect
holds an ordered list of any non-runtime parameter types (akaANY
). With this,CandidateIndex.All
can reduce the number of checks by taking advantage of these two subclasses.SELECT ... WHERE <ANY> = 2
, the lack of ordering would for some reason always placeEQ(ANY, ANY)
first -- which would invoke PartiQL's comparator -- which is seemingly very slow (taking up 25% of the entire test's CPU usage). By re-ordering the candidates, it significantly impacted our performance for the better (the matchedEQ
function did not even show up on the profiler, indicating that it took up less than 1% of the CPU usage).Ref.Cast
to containisNullable
-- at least until we truly represent casts with function calls.ADD(NULL, INT)
were initially returning the typeNULL
, but it really should have been returningINT?
. This is modeled slightly different in SQL:1999 since types are nullable, but until we add this, we'll need to support this.MISSINGs
are extremely expensive (causing up to 33.2% of our CPU usage for evaluation) due to thefillInStackTrace
method. Since we are programming a language, we can/should be architecting our own error messaging system.Note
NULL = MISSING
. See the Summary. I think these tests are wrong. See PartiQL Spec: "Equality never fails in the type-checking mode and never returns MISSING in the permissive mode."Other Information
Updated Unreleased Section in CHANGELOG: NO
Any backward-incompatible changes? YES -- the
isNullable
flag, which is requiredAny new external dependencies? NO
Do your changes comply with the Contributing Guidelines
and Code Style Guidelines? YES
License Information
By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.