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

fix(planner): Correct the mapping relationship between RuleID and DEFAULT_REWRITE_RULES #10508

Closed
wants to merge 18 commits into from

Conversation

dusx1981
Copy link
Contributor

@dusx1981 dusx1981 commented Mar 12, 2023

I hereby agree to the terms of the CLA available at: https://databend.rs/dev/policies/cla/

Summary

The new map
The new mapping relationship between RuleID and DEFAULT_REWRITE_RULES looks like this:
image
RuleIDs are put together according to the type,so each operator can apply its own rules in the correct order.

The scalability
Considering that each operator only applies its own rules in the end, the new mapping only reduces the number of loops compared to the old implementation, and does not affect the addition of new rules.

Optimization of the heuristic optimizer

  1. Filter: Reduce the number of loops from 22 to 10
  2. EvalScalar: Reduce the number of loops from 22 to 2
  3. Limit: Reduce the number of loops from 22 to 6
  4. Agg: Reduce the number of loops from 22 to 2
  5. Sort: Reduce the number of loops from 22 to 1
  6. Scan: Reduce the number of loops from 22 to 0
  7. DumpTableScan: Reduce the number of loops from 22 to 0

The sample code
some operators are not required to apply the rules:

impl Operator for Scan {

    // Returns the set of rules that the operator can apply
    fn transformation_candidate_rules(&self) -> roaring::RoaringBitmap {
        RoaringBitmap::new()
    }

impl Operator for DummyTableScan {

    fn transformation_candidate_rules(&self) -> roaring::RoaringBitmap {
        RoaringBitmap::new()
    }

Some operators require only a few rules to be applied:

    // EvalScalar
    EliminateEvalScalar,
    MergeEvalScalar,

    // Sort
    PushDownSortScan,

    // EvalScalar
    EliminateEvalScalar,
    MergeEvalScalar,

And with the continuous increase of rules, it may eventually reach hundreds. With recursive calls, the magnitude of the loop may grow exponentially.So I think it's necessary to just apply it's own rules for each operator.

In this way, the number of loops per optimization can be greatly reduced.

Closes #issue

@vercel
Copy link

vercel bot commented Mar 12, 2023

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated
databend ✅ Ready (Inspect) Visit Preview 💬 Add your feedback Mar 19, 2023 at 7:44PM (UTC)

@dusx1981 dusx1981 marked this pull request as ready for review March 12, 2023 01:23
@mergify mergify bot added the pr-feature this PR introduces a new feature to the codebase label Mar 12, 2023
@dusx1981 dusx1981 marked this pull request as draft March 12, 2023 01:23
@dusx1981 dusx1981 marked this pull request as ready for review March 12, 2023 02:11
@dusx1981 dusx1981 marked this pull request as draft March 12, 2023 02:25
@dusx1981 dusx1981 marked this pull request as ready for review March 12, 2023 02:41
@dusx1981
Copy link
Contributor Author

dusx1981 commented Mar 12, 2023

@leiysky

Because of the precise management of the application of operator rules,please do a benchmark test:)

one of the implementations:

impl Operator for Limit {
    fn transformation_candidate_rules(&self) -> roaring::RoaringBitmap {
        (RuleID::PushDownLimitUnion as u32..(RuleID::PushDownLimitScan as u32) + 1)
            .collect::<RoaringBitmap>()
    }

AND I'm collating the preprocessing logic that needs to be done from GPORCA

@dusx1981 dusx1981 changed the title feat(planner): Correct the mapping relationship between RuleID and DEFAULT_REWRITE_RULES feat(planner ci): Correct the mapping relationship between RuleID and DEFAULT_REWRITE_RULES Mar 13, 2023
@dusx1981 dusx1981 changed the title feat(planner ci): Correct the mapping relationship between RuleID and DEFAULT_REWRITE_RULES feat(planner): Correct the mapping relationship between RuleID and DEFAULT_REWRITE_RULES Mar 13, 2023
@dusx1981 dusx1981 changed the title feat(planner): Correct the mapping relationship between RuleID and DEFAULT_REWRITE_RULES fix(planner): Correct the mapping relationship between RuleID and DEFAULT_REWRITE_RULES Mar 13, 2023
@mergify mergify bot added the pr-bugfix this PR patches a bug in codebase label Mar 13, 2023
@dusx1981 dusx1981 changed the title fix(planner): Correct the mapping relationship between RuleID and DEFAULT_REWRITE_RULES bench(planner): Correct the mapping relationship between RuleID and DEFAULT_REWRITE_RULES Mar 13, 2023
@mergify
Copy link
Contributor

mergify bot commented Mar 13, 2023

This pull request's title is not fulfill the requirements. @dusx1981 please update it 🙏.

Valid format:

fix(query): fix group by string bug
  ^         ^---------------------^
  |         |
  |         +-> Summary in present tense.
  |
  +-------> Type: rfc, feat, fix, refactor, ci, docs, chore

Valid types:

  • rfc: this PR proposes a new RFC
  • feat: this PR introduces a new feature to the codebase
  • fix: this PR patches a bug in codebase
  • refactor: this PR changes the code base without new features or bugfix
  • ci: this PR changes build/testing/ci steps
  • docs: this PR changes the documents or websites
  • chore: this PR only has small changes that no need to record

@dusx1981 dusx1981 changed the title bench(planner): Correct the mapping relationship between RuleID and DEFAULT_REWRITE_RULES ci(planner): Correct the mapping relationship between RuleID and DEFAULT_REWRITE_RULES Mar 13, 2023
@mergify mergify bot added the pr-build this PR changes build/testing/ci steps label Mar 13, 2023
@dusx1981 dusx1981 changed the title ci(planner): Correct the mapping relationship between RuleID and DEFAULT_REWRITE_RULES fix(planner): Correct the mapping relationship between RuleID and DEFAULT_REWRITE_RULES Mar 13, 2023
@dusx1981 dusx1981 changed the title fix(planner): Correct the mapping relationship between RuleID and DEFAULT_REWRITE_RULES ci(bench): Correct the mapping relationship between RuleID and DEFAULT_REWRITE_RULES Mar 13, 2023
@dusx1981 dusx1981 changed the title ci(bench): Correct the mapping relationship between RuleID and DEFAULT_REWRITE_RULES fix(planner): Correct the mapping relationship between RuleID and DEFAULT_REWRITE_RULES Mar 13, 2023
@dusx1981
Copy link
Contributor Author

dusx1981 commented Mar 13, 2023

@BohuTANG

Could you please run a benchmark test to see if this PR should be reviewed?

@xudong963
Copy link
Member

@mergify update

@mergify
Copy link
Contributor

mergify bot commented Mar 19, 2023

update

❌ Pull request can't be updated with latest base branch changes

Mergify needs the author permission to update the base branch of the pull request.
@dusx1981 needs to authorize modification on its head branch.
err-code: ED112

@xudong963 xudong963 added the ci-benchmark Benchmark: run all test label Mar 19, 2023
@dusx1981
Copy link
Contributor Author

@mergify update

@mergify
Copy link
Contributor

mergify bot commented Mar 19, 2023

update

☑️ Nothing to do

  • #commits-behind>0 [:pushpin: update requirement]
  • -closed [:pushpin: update requirement]

@dusx1981
Copy link
Contributor Author

dusx1981 commented Mar 19, 2023

@xudong963

@mergify update

image

@dusx1981
Copy link
Contributor Author

image

@sundy-li sundy-li removed the ci-benchmark Benchmark: run all test label Mar 20, 2023
Copy link
Member

@xudong963 xudong963 left a comment

Choose a reason for hiding this comment

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

Changes make sense to me. Sorry for late review, thanks @dusx1981

Any thoughts? @leiysky

pub(crate) memo: Memo,
pub(crate) cost_model: Box<dyn CostModel>,
pub memo: Memo,
pub explore_rule_set: roaring::RoaringBitmap,
Copy link
Collaborator

@leiysky leiysky Mar 21, 2023

Choose a reason for hiding this comment

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

I think we'd better not expose the RoaringBitmap here, should use RuleSet instead.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think we'd better not expose the RoaringBitmap here, should use RuleSet instead.

I understand what you mean, I want to transform the RuleSet to use bitmap management, and then submit a PR. If there is no problem with this PR, can it be passed first?

Copy link
Member

Choose a reason for hiding this comment

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

If there is no problem with this PR, can it be passed first?

Please address it in this PR.

/// Try to apply the rules to the expression.
/// Return the final result that no rule can be applied.
fn apply_transform_rules(&self, s_expr: &SExpr) -> Result<SExpr> {
let mut s_expr = s_expr.clone();
let rule_set = self.calc_operator_rule_set(&s_expr.plan);
Copy link
Collaborator

Choose a reason for hiding this comment

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

We should distinguish between RuleSet and RuleList. RuleSet is order-insensitive, and RuleList is order-sensitive.

In this case, a RuleList is required.

Copy link
Contributor Author

@dusx1981 dusx1981 Mar 21, 2023

Choose a reason for hiding this comment

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

We should distinguish between RuleSet and RuleList. RuleSet is order-insensitive, and RuleList is order-sensitive.

In this case, a RuleList is required.

Please refer to the introduction of my implementation ideas above. If you use RuleList, you cannot quickly calculate the applicable rules for each operator, which will cause too many loops and reduce performance.
image

And Please refer to the benchmark results

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@leiysky

Any other question?

Copy link
Member

Choose a reason for hiding this comment

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

And Please refer to the benchmark results

Could you please provide more details about the benchmark? The benefits appear to be relatively low.

hits: from 1.01 to 0.99

image

tpch: from 1.02 to 0.98

image

Although performance is important, I prefer to prioritize code readability and maintainability. It's not worth sacrificing those aspects for the sake of minor performance improvements.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I disagree with this point of view. With the increase of rules, each rule will increase the number of cycles optimized by unrelated operators, and it is exponential. When I think of it, the performance comparison will be more obvious.

Copy link
Member

Choose a reason for hiding this comment

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

With the increase of rules, each rule will increase the number of cycles optimized by unrelated operators, and it is exponential.

You are correct, but for now, it is not worth it. We can optimize when we identify the bottleneck.

optimizer: &CascadesOptimizer,
operator: &RelOperator,
) -> roaring::RoaringBitmap {
unsafe {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why do we need unsafe here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

// read only, so thread safe
pub static mut RULE_FACTORY: Lazy<RuleFactory> = Lazy::new(RuleFactory::create);

RULE_FACTORY is read-only, so it is thread-safe. Here, we need to use unsafe to solve the problem of syntax errors.

Copy link
Member

Choose a reason for hiding this comment

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

RULE_FACTORY is read-only, so it is thread-safe.

It's not a good reason to use unsafe.

Here, we need to use unsafe to solve the problem of syntax errors.

This reasoning is flawed. Implementing unsafe practices everywhere will ultimately lead to increased long-term maintenance costs.


for rule_id in rule_set.iter() {
let apply_rule_task = ApplyRuleTask::with_parent(
rule_id,
unsafe { std::mem::transmute::<u8, RuleID>(rule_id as u8) },
Copy link
Collaborator

Choose a reason for hiding this comment

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

Transmuting an enum to u8 is dangerous. Why do we need this here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Transmuting an enum to u8 is dangerous. Why do we need this here?

Ok, first of all, the current RuleID is safe under this transformation. could you all me to handle it with standard casts in next PR?

Copy link
Member

Choose a reason for hiding this comment

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

Doesn't make sense to use mem::transmute here. Also, it should be addressed in this PR.

@@ -113,13 +121,27 @@ impl HeuristicOptimizer {
Ok(result)
}

fn calc_operator_rule_set(&self, operator: &RelOperator) -> roaring::RoaringBitmap {
unsafe { operator.transformation_candidate_rules() & (&RULE_FACTORY.transformation_rules) }
Copy link
Collaborator

Choose a reason for hiding this comment

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

Ditto, why do we need unsafe here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

// read only, so thread safe
pub static mut RULE_FACTORY: Lazy<RuleFactory> = Lazy::new(RuleFactory::create);

RULE_FACTORY is read-only, so it is thread-safe. Here, we need to use unsafe to solve the problem of syntax errors.

Copy link
Member

Choose a reason for hiding this comment

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

RULE_FACTORY is read-only, so it is thread-safe. Here, we need to use unsafe to solve the problem of syntax errors.

Doesn't make sense to me.

@dusx1981
Copy link
Contributor Author

@leiysky
Copy link
Collaborator

leiysky commented Mar 21, 2023

@leiysky

Hasn’t the same code been reviewed by you in this PR?

I appreciate your effort, but I must admit that I couldn't accept that pull request due to its low quality.

To be frank, I don't have the energy to point out every issue with your code, and it's unnecessary.

Despite its shortcomings, I recognize the value of your work and have accepted it. However, it has caused a significant impact on the project's maintenance, and as a result, I had to refactor it.

This pull request is merely a minor patch on the previous one and even corrupts the latest codebase.

Although there is always room for improvement, I don't think this is the right way.

@dusx1981
Copy link
Contributor Author

dusx1981 commented Mar 21, 2023

@leiysky
Hasn’t the same code been reviewed by you in this PR?

I appreciate your effort, but I must admit that I couldn't accept that pull request due to its low quality.

To be frank, I don't have the energy to point out every issue with your code, and it's unnecessary.

Despite its shortcomings, I recognize the value of your work and have accepted it. However, it has caused a significant impact on the project's maintenance, and as a result, I had to refactor it.

This pull request is merely a minor patch on the previous one and even corrupts the latest codebase.

Although there is always room for improvement, I don't think this is the right way.

So I feel a little weird about a few points:

  • You completely negate your original evaluation, there is no logic at all
    image
  • You completely ignore my implementation ideas above
  • AND in this pr, I see what you call professionalism:
    image
  • You completely negate what other people have said about this project
  • You completely ignore the benchmark result

pub(crate) explore_rule_set: RuleSet,
pub(crate) metadata: MetadataRef,
pub best_cost_map: HashMap<IndexType, CostContext>,
_ctx: Arc<dyn TableContext>,
Copy link
Member

Choose a reason for hiding this comment

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

Introducing TableContext in CascadesOptimizer may not be a good idea. Additionally, I noticed that this context has been named as _ctx. If we do not intend to use it, perhaps it would be better to remove it?


pub fn get_explore_rule_set(enable_bushy_join: bool) -> RuleSet {
pub fn calc_explore_rule_set(enable_bushy_join: bool) -> RoaringBitmap {
Copy link
Member

Choose a reason for hiding this comment

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

Please refrain from breaking the current abstraction.

Comment on lines +62 to +69
RuleFactory {
transformation_rules: (RuleID::NormalizeScalarFilter as u32
..RuleID::CommuteJoin as u32)
.collect::<RoaringBitmap>(),
exploration_rules: (RuleID::CommuteJoin as u32..(RuleID::RightExchangeJoin as u32) + 1)
.collect::<RoaringBitmap>(),
}
}
Copy link
Member

Choose a reason for hiding this comment

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

I don't understand that code. Could you explain it further? Is it safe to use as u32 for collecting? When do we need to update them? After this change, the order RuleID is sensitive?

@dusx1981
Copy link
Contributor Author

dusx1981 commented Mar 22, 2023

@Xuanwo

Thank you for your feedback, I also roughly know the workflow of this project, and hope to submit more qualified codes later.
I think there is no need to discuss this PR anymore, I will close it later.

@Xuanwo
Copy link
Member

Xuanwo commented Mar 22, 2023

Thank you for your feedback, I also roughly know the workflow of this project, and hope to submit more qualified codes later.
I think there is no need to discuss this PR anymore, I will close it later.

Thanks for your effort!

@dusx1981
Copy link
Contributor Author

dusx1981 commented Mar 22, 2023

@Xuanwo

On the point of performance, I disagree.. With the increase of rules, each rule will increase the number of cycles optimized by unrelated operators, and it is exponential. When I think of it, the performance comparison will be more obvious.

For fast optimization, the Cascacdes optimizer has designed a way of bitmap management rules, which can quickly calculate the rules required by each operator and avoid invalid loops. Coupled with the recursive factor, the number of cycles increases exponentially. The main purpose of my implementation of these codes is to achieve this optimization.

@Xuanwo
Copy link
Member

Xuanwo commented Mar 22, 2023

The main purpose of my implementation of these codes is to achieve this optimization.

Thanks for your effort again. We acknowledge that there is still room for improvement in Databend. However, based on the performance reports, this particular aspect is not currently a bottleneck. Therefore, we will prioritize maintaining readability and ease of use.

@dusx1981
Copy link
Contributor Author

The main purpose of my implementation of these codes is to achieve this optimization.

Thanks for your effort again. We acknowledge that there is still room for improvement in Databend. However, based on the performance reports, this particular aspect is not currently a bottleneck. Therefore, we will prioritize maintaining readability and ease of use.

Understood,Thank you.

@dusx1981 dusx1981 closed this Mar 22, 2023
@dusx1981
Copy link
Contributor Author

Please allow me to add an important information, I learned some key information about real database development from this Review, thank you.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
pr-bugfix this PR patches a bug in codebase
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

6 participants