Repository navigation
expr: Evaluate binary function arguments through a named iterator - #39903
Merged
antiguru merged 2 commits intoOct 19, 2026
Merged
Conversation
The blanket `LazyBinaryFunc` impl evaluated both arguments into an array of results before handing them to `InputDatumType::try_from_iter`. This made every binary function evaluation about 12% slower on the `case_literal/if_chain` benchmarks. A named iterator that evaluates each argument on demand restores the previous speed and keeps a single iterator type per `Eval` implementor, so the generated code size stays the same. Closes: CPU-315 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
bosconi
approved these changes
Oct 19, 2026
bosconi
left a comment
Member
There was a problem hiding this comment.
Replaces #39061's [Result<Datum, EvalError>; 2] with a named iterator that evaluates each argument when try_from_iter asks for it, keeping one instantiation per input type and Eval implementor. Semantics are unchanged: every binary function's Input is a 2-tuple generated by #[sqlfunc], and the tuple try_from_iter pulls every argument before it checks for errors and NULLs. So both arguments are still evaluated, and an eval error still wins over a NULL. Looks good.
Reviewed at a765877.
Suggestion
- With the array, both arguments were evaluated whatever
try_from_iterdid. Now that rests on the tuple impl consuming everything before it decides, which is easy to "optimize" into a short-circuit later, and I found no test that pins the resulting priority for binary functions. A column-based case intest/sqllogictest/error_semantics.sltwould: a table(a int, b int)holding(NULL, 0)andSELECT a + 1/b, expecting the division-by-zero error. Columns keep the optimizer from folding the NULL away.
Nits
- nit: the description says this restores the previous speed but gives no numbers. The A/B figures from CPU-315 (
if_chain/4_arms/last300-313 ns before #39061, 344-351 ns after) plus this PR's number would make that checkable.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Member
Author
|
I adjusted the PR based on the feedback. PR body now includes measurements, and there's an SLT to assert the error semantics. Thanks for the review! |
antiguru
enabled auto-merge (squash)
October 19, 2026 08:19
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
#39061 made the blanket
LazyBinaryFuncimpl evaluate both arguments into an array of results. That madecase_literal/if_chain/*11-13% slower, which nightly cargo-bench flags. A named iterator that evaluates each argument on demand restores most of the previous speed and keeps #39061's code-size reduction (cargo llvm-lines -p mz-expr --lib: 1,933,557 lines vs. 1,934,453 on main).Local A/B against ae3cc46, #39061's parent (median of 3, pinned core):
if_chain/64_arms/last4741 ns before #39061, 5319 ns on main, 4816 ns with this PR. Most of the family lands within 2.3% of the parent.4_arms/lastis the exception (306 / 340 / 333 ns), and it reproduced in two runs.Every binary function's input is a 2-tuple whose
try_from_iterpulls both arguments before it checks for errors and NULLs, so evaluation semantics are unchanged. A new case inerror_semantics.sltpins that an eval error wins over a NULL in either argument position.Closes: CPU-315
🤖 Generated with Claude Code