-
Notifications
You must be signed in to change notification settings - Fork 717
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
Conversation
This reverts commit 22b26ca.
The latest updates on your projects. Learn more about Vercel for Git ↗︎
|
Because of the precise management of the application of operator rules,please do a benchmark test:) one of the implementations:
AND I'm collating the preprocessing logic that needs to be done from GPORCA |
This pull request's title is not fulfill the requirements. @dusx1981 please update it 🙏. Valid format:
Valid types:
|
Could you please run a benchmark test to see if this PR should be reviewed? |
@mergify update |
❌ Pull request can't be updated with latest base branch changesMergify needs the author permission to update the base branch of the pull request. |
@mergify update |
☑️ Nothing to do
|
|
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.
pub(crate) memo: Memo, | ||
pub(crate) cost_model: Box<dyn CostModel>, | ||
pub memo: Memo, | ||
pub explore_rule_set: roaring::RoaringBitmap, |
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 think we'd better not expose the RoaringBitmap
here, should use RuleSet
instead.
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 think we'd better not expose the
RoaringBitmap
here, should useRuleSet
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?
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.
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); |
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.
We should distinguish between RuleSet
and RuleList
. RuleSet
is order-insensitive, and RuleList
is order-sensitive.
In this case, a RuleList
is required.
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.
We should distinguish between
RuleSet
andRuleList
.RuleSet
is order-insensitive, andRuleList
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.
And Please refer to the benchmark results
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.
Any other question?
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.
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
tpch: from 1.02 to 0.98
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.
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 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.
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.
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 { |
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 do we need unsafe
here?
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.
// 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.
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.
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) }, |
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.
Transmuting an enum
to u8
is dangerous. Why do we need this here?
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.
Transmuting an
enum
tou8
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?
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.
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) } |
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.
Ditto, why do we need unsafe
here?
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.
// 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.
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.
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.
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:
|
pub(crate) explore_rule_set: RuleSet, | ||
pub(crate) metadata: MetadataRef, | ||
pub best_cost_map: HashMap<IndexType, CostContext>, | ||
_ctx: Arc<dyn TableContext>, |
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.
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 { |
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.
Please refrain from breaking the current abstraction.
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>(), | ||
} | ||
} |
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 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?
Thank you for your feedback, I also roughly know the workflow of this project, and hope to submit more qualified codes later. |
Thanks for your effort! |
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. |
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. |
Please allow me to add an important information, I learned some key information about real database development from this Review, thank you. |
I hereby agree to the terms of the CLA available at: https://databend.rs/dev/policies/cla/
Summary
The new map
![image](https://user-images.githubusercontent.com/7598029/224577815-ec13ae47-57bf-4c10-b512-de5691b98081.png)
The new mapping relationship between RuleID and DEFAULT_REWRITE_RULES looks like this:
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
The sample code
some operators are not required to apply the rules:
Some operators require only a few rules to be applied:
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