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

perf(expr): new interface for expression directly returning scalar #9049

Merged
merged 10 commits into from
Apr 11, 2023

Conversation

BugenZhao
Copy link
Member

@BugenZhao BugenZhao commented Apr 7, 2023

Signed-off-by: Bugen Zhao i@bugenzhao.comI hereby agree to the terms of the RisingWave Labs, Inc. Contributor License Agreement.

What's changed and what's your intention?

Do not repeat the literal value 1024 times when evaluating an expression that usually takes a literal as one input like EXTRACT(HOUR FROM col). (#9052)

extract(constant)       time:   [10.678 µs 10.702 µs 10.727 µs]
                        change: [-34.576% -34.333% -34.043%] (p = 0.00 < 0.05)
                        Performance has improved.

The simplified implementation for demo purposes is here. Briefly, we introduce a new interface of Expr::eval_new (temporary name) which returns either Array, or directly Datum if the return value is supposed to be a constant array. When using the return value as argument, the Datum variant can be directly iterated with repeat.take instead of allocating a real array.

The eval_new is bidirectionally compatible with eval by always falling back to the Array variant, so most of the hand-written expressions are not touched in this PR. Instead, we rewrite the implementation with eval_new for all templated expressions, and apply the optimization to expr_literal.

  • No performance regress is observed for using Either as the iterator.
  • template_fast is not touched as we're not sure whether this breaks SIMD.

Checklist For Contributors

  • I have written necessary rustdoc comments
  • I have added necessary unit tests and integration tests
  • I have added fuzzing tests or opened an issue to track them. (Optional, recommended for new SQL features Sqlsmith: Sql feature generation #7934).
  • I have demonstrated that backward compatibility is not broken by breaking changes and created issues to track deprecated features to be removed in the future. (Please refer to the issue)
  • All checks passed in ./risedev check (or alias, ./risedev c)

Checklist For Reviewers

  • I have requested macro/micro-benchmarks as this PR can affect performance substantially, and the results are shown.

Documentation

  • My PR DOES NOT contain user-facing changes.
Click here for Documentation

Types of user-facing changes

Please keep the types that apply to your changes, and remove the others.

  • Installation and deployment
  • Connector (sources & sinks)
  • SQL commands, functions, and operators
  • RisingWave cluster configuration changes
  • Other (please specify in the release note below)

Release note

Signed-off-by: Bugen Zhao <i@bugenzhao.com>
Signed-off-by: Bugen Zhao <i@bugenzhao.com>
Signed-off-by: Bugen Zhao <i@bugenzhao.com>
Signed-off-by: Bugen Zhao <i@bugenzhao.com>
Signed-off-by: Bugen Zhao <i@bugenzhao.com>
@BugenZhao BugenZhao changed the title perf(expr): directly return scalar for literal expression perf(expr): new interface for literal expression directly returning scalar Apr 7, 2023
Signed-off-by: Bugen Zhao <i@bugenzhao.com>
@BugenZhao BugenZhao changed the title perf(expr): new interface for literal expression directly returning scalar perf(expr): new interface for expression directly returning scalar Apr 7, 2023
@BugenZhao BugenZhao marked this pull request as ready for review April 7, 2023 11:52
Signed-off-by: Bugen Zhao <i@bugenzhao.com>
@BugenZhao BugenZhao added the component/common Common components, such as array, data chunk, expression. label Apr 7, 2023
@@ -205,6 +205,15 @@ fn bench_expr(c: &mut Criterion) {
.to_async(FuturesExecutor)
.iter(|| constant.eval(&input))
});
c.bench_function("extract(constant)", |bencher| {
Copy link
Member Author

Choose a reason for hiding this comment

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

Not sure if is there any general way to construct this test case. :(

Copy link
Contributor

Choose a reason for hiding this comment

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

I guess not... Let's keep it manual construction :(

@codecov
Copy link

codecov bot commented Apr 7, 2023

Codecov Report

Merging #9049 (5a25ac9) into main (a63807d) will decrease coverage by 0.01%.
The diff coverage is 94.59%.

@@            Coverage Diff             @@
##             main    #9049      +/-   ##
==========================================
- Coverage   70.78%   70.77%   -0.01%     
==========================================
  Files        1195     1196       +1     
  Lines      197687   197696       +9     
==========================================
+ Hits       139926   139927       +1     
- Misses      57761    57769       +8     
Flag Coverage Δ
rust 70.77% <94.59%> (-0.01%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

Impacted Files Coverage Δ
src/expr/src/expr/value.rs 88.88% <88.88%> (ø)
src/expr/src/expr/expr_literal.rs 96.83% <100.00%> (-0.38%) ⬇️
src/expr/src/expr/mod.rs 60.78% <100.00%> (+12.06%) ⬆️
src/expr/src/expr/template.rs 72.04% <100.00%> (+1.07%) ⬆️

... and 9 files with indirect coverage changes

📣 We’re building smart automated test selection to slash your CI/CD build times. Learn more

Copy link
Member

@xxchan xxchan left a comment

Choose a reason for hiding this comment

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

Looks both good and hacky to me at the same time 🥵

@xxchan
Copy link
Member

xxchan commented Apr 7, 2023

🤔 This seems kind of "lazy evaluation" (or "late materialization"?): executor still use eval to get fully-expanded array, while inside expr, scalars can be used directly.

async fn eval(&self, input: &DataChunk) -> Result<ArrayRef>;
/// The default implementation calls `eval` and puts the result into the `Array` variant.
async fn eval_new(&self, input: &DataChunk) -> Result<ValueImpl> {
self.eval(input).map_ok(ValueImpl::Array).await
Copy link
Member

Choose a reason for hiding this comment

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

So it stackoverflows if both not implemented? 😄

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes. 🥵 Not sure if there's a way to avoid this.

Comment on lines 60 to 61
// Otherwise, fallback to array computation.
// TODO: match all possible combinations to further get rid of the overhead of `Either` iterators.
Copy link
Member

Choose a reason for hiding this comment

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

Wait, so EXTRACT(HOUR FROM col) still fallbacks to array computation? Then where's the improvement from? 🥵

Copy link
Member Author

Choose a reason for hiding this comment

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

For execution, yes. The difference is that we're not allocating a new array for HOUR now. Instead, iter here directly calls repeat().take().

Signed-off-by: Bugen Zhao <i@bugenzhao.com>
@BugenZhao
Copy link
Member Author

Looks both good and hacky to me at the same time 🥵

Not that hacky. 🥵 Actually I borrow the ideas partially from https://github.com/datafuselabs/databend/blob/a3ac38f838eb37e8ea5600d526e7d1b2f3c0bb50/src/query/expression/src/evaluator.rs#L138.

Copy link
Contributor

@wangrunji0408 wangrunji0408 left a comment

Choose a reason for hiding this comment

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

Generally LGTM!

@@ -205,6 +205,15 @@ fn bench_expr(c: &mut Criterion) {
.to_async(FuturesExecutor)
.iter(|| constant.eval(&input))
});
c.bench_function("extract(constant)", |bencher| {
Copy link
Contributor

Choose a reason for hiding this comment

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

I guess not... Let's keep it manual construction :(

src/expr/src/expr/template.rs Outdated Show resolved Hide resolved
@y-wei y-wei self-requested a review April 10, 2023 17:17
@BugenZhao BugenZhao enabled auto-merge April 11, 2023 05:14
@BugenZhao BugenZhao added this pull request to the merge queue Apr 11, 2023
Merged via the queue into main with commit 5b5b2e6 Apr 11, 2023
@BugenZhao BugenZhao deleted the bz/scalar-expr branch April 11, 2023 06:02
@BugenZhao BugenZhao mentioned this pull request Apr 11, 2023
5 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
component/common Common components, such as array, data chunk, expression. type/perf
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants