You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@gluten.apache.org by lg...@apache.org on 2024/09/10 02:07:44 UTC
(incubator-gluten) branch main updated: pushdown aggregation's pre-projection ahead expand node (#7142)
This is an automated email from the ASF dual-hosted git repository.
lgbo pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/incubator-gluten.git
The following commit(s) were added to refs/heads/main by this push:
new d49efdfbb0 pushdown aggregation's pre-projection ahead expand node (#7142)
d49efdfbb0 is described below
commit d49efdfbb04164b4e1ccee5cebacb705c2a20a23
Author: lgbo <lg...@gmail.com>
AuthorDate: Tue Sep 10 10:07:40 2024 +0800
pushdown aggregation's pre-projection ahead expand node (#7142)
---
.../gluten/backendsapi/clickhouse/CHBackend.scala | 9 ++
.../gluten/backendsapi/clickhouse/CHRuleApi.scala | 1 +
...PushdownAggregatePreProjectionAheadExpand.scala | 115 +++++++++++++++++++++
.../execution/GlutenClickHouseTPCHSuite.scala | 16 +++
4 files changed, 141 insertions(+)
diff --git a/backends-clickhouse/src/main/scala/org/apache/gluten/backendsapi/clickhouse/CHBackend.scala b/backends-clickhouse/src/main/scala/org/apache/gluten/backendsapi/clickhouse/CHBackend.scala
index 86a69f8422..163f7568f7 100644
--- a/backends-clickhouse/src/main/scala/org/apache/gluten/backendsapi/clickhouse/CHBackend.scala
+++ b/backends-clickhouse/src/main/scala/org/apache/gluten/backendsapi/clickhouse/CHBackend.scala
@@ -376,6 +376,15 @@ object CHBackendSettings extends BackendSettingsApi with Logging {
)
}
+ // Move the pre-prejection for a aggregation ahead of the expand node
+ // for example, select a, b, sum(c+d) from t group by a, b with cube
+ def enablePushdownPreProjectionAheadExpand(): Boolean = {
+ SparkEnv.get.conf.getBoolean(
+ "spark.gluten.sql.columnar.backend.ch.enable_pushdown_preprojection_ahead_expand",
+ true
+ )
+ }
+
override def enableNativeWriteFiles(): Boolean = {
GlutenConfig.getConf.enableNativeWriter.getOrElse(false)
}
diff --git a/backends-clickhouse/src/main/scala/org/apache/gluten/backendsapi/clickhouse/CHRuleApi.scala b/backends-clickhouse/src/main/scala/org/apache/gluten/backendsapi/clickhouse/CHRuleApi.scala
index fb5147157d..61fed3f999 100644
--- a/backends-clickhouse/src/main/scala/org/apache/gluten/backendsapi/clickhouse/CHRuleApi.scala
+++ b/backends-clickhouse/src/main/scala/org/apache/gluten/backendsapi/clickhouse/CHRuleApi.scala
@@ -76,6 +76,7 @@ private object CHRuleApi {
injector.injectTransform(_ => EliminateLocalSort)
injector.injectTransform(_ => CollapseProjectExecTransformer)
injector.injectTransform(c => RewriteSortMergeJoinToHashJoinRule.apply(c.session))
+ injector.injectTransform(c => PushdownAggregatePreProjectionAheadExpand.apply(c.session))
injector.injectTransform(
c => SparkPlanRules.extendedColumnarRule(c.conf.extendedColumnarTransformRules)(c.session))
injector.injectTransform(c => InsertTransitions(c.outputsColumnar))
diff --git a/backends-clickhouse/src/main/scala/org/apache/gluten/extension/PushdownAggregatePreProjectionAheadExpand.scala b/backends-clickhouse/src/main/scala/org/apache/gluten/extension/PushdownAggregatePreProjectionAheadExpand.scala
new file mode 100644
index 0000000000..a3fab3c954
--- /dev/null
+++ b/backends-clickhouse/src/main/scala/org/apache/gluten/extension/PushdownAggregatePreProjectionAheadExpand.scala
@@ -0,0 +1,115 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.gluten.extension
+
+import org.apache.gluten.backendsapi.clickhouse.CHBackendSettings
+import org.apache.gluten.execution._
+
+import org.apache.spark.internal.Logging
+import org.apache.spark.sql.SparkSession
+import org.apache.spark.sql.catalyst.expressions._
+import org.apache.spark.sql.catalyst.expressions.AttributeReference
+import org.apache.spark.sql.catalyst.rules.Rule
+import org.apache.spark.sql.execution.SparkPlan
+
+// If there is an expression (not a attribute) in an aggregation function's
+// parameters. It will introduce a pr-projection to calculate the expression
+// at first, and make all the parameters be attributes.
+// If it's a aggregation with grouping set, this pre-projection is placed after
+// expand operator. This is not efficient, we cannot move this pre-projection
+// before the expand operator.
+case class PushdownAggregatePreProjectionAheadExpand(session: SparkSession)
+ extends Rule[SparkPlan]
+ with Logging {
+ override def apply(plan: SparkPlan): SparkPlan = {
+ if (!CHBackendSettings.enablePushdownPreProjectionAheadExpand) {
+ return plan
+ }
+ plan.transformUp {
+ case hashAggregate: CHHashAggregateExecTransformer =>
+ tryPushdownAggregatePreProject(hashAggregate)
+ }
+ }
+
+ def isGroupingColumn(e: NamedExpression): Boolean = {
+ e.isInstanceOf[AttributeReference] && e
+ .asInstanceOf[AttributeReference]
+ .name
+ .startsWith(VirtualColumn.groupingIdName, 0)
+ }
+
+ def dropGroupingColumn(expressions: Seq[NamedExpression]): Seq[NamedExpression] = {
+ expressions.filter(!isGroupingColumn(_))
+ }
+
+ def isAttributeOrLiteral(e: Expression): Boolean = {
+ e match {
+ case _: Attribute | _: BoundReference | _: Literal => true
+ case _ => false
+ }
+ }
+
+ def tryPushdownAggregatePreProject(plan: SparkPlan): SparkPlan = {
+ val hashAggregate = plan.asInstanceOf[CHHashAggregateExecTransformer]
+ val originGroupingKeys = dropGroupingColumn(hashAggregate.groupingExpressions)
+ // make things simple, if any grouping key is not attribute, don't change anything
+ if (!originGroupingKeys.forall(isAttributeOrLiteral(_))) {
+ return hashAggregate
+ }
+ hashAggregate.child match {
+ case project @ ProjectExecTransformer(expressions, expand: ExpandExecTransformer) =>
+ val rootChild = expand.child
+
+ // This could not happen
+ if (rootChild.output.exists(isGroupingColumn(_))) {
+ return hashAggregate
+ }
+ // drop the goruping id column
+ val aheadProjectExprs = dropGroupingColumn(project.projectList)
+ val originInputAttributes = aheadProjectExprs.filter(e => isAttributeOrLiteral(e))
+
+ val preProjectExprs = aheadProjectExprs.filter(e => !isAttributeOrLiteral(e))
+ if (preProjectExprs.length == 0) {
+ return hashAggregate
+ }
+
+ // If the expression involves grouping keys, don't change anything
+ // This should not happen.
+ if (preProjectExprs.exists(e => originGroupingKeys.exists(e.references.contains(_)))) {
+ return hashAggregate
+ }
+
+ // The new ahead project node will take rootChild's output and preProjectExprs as the
+ // the projection expressions.
+ val aheadProject = ProjectExecTransformer(rootChild.output ++ preProjectExprs, rootChild)
+ val aheadProjectOuput = aheadProject.output
+
+ val preProjectOutputAttrs = aheadProjectOuput.filter(
+ e =>
+ !originInputAttributes.exists(_.exprId.equals(e.asInstanceOf[NamedExpression].exprId)))
+
+ val newExpandProjections = expand.projections.map {
+ exprs => exprs ++ preProjectOutputAttrs
+ }
+ val newExpandOutput = expand.output ++ preProjectOutputAttrs
+ val newExpand = ExpandExecTransformer(newExpandProjections, newExpandOutput, aheadProject)
+
+ hashAggregate.withNewChildren(Seq(newExpand))
+ case _ => plan
+ }
+ }
+}
diff --git a/backends-clickhouse/src/test/scala/org/apache/gluten/execution/GlutenClickHouseTPCHSuite.scala b/backends-clickhouse/src/test/scala/org/apache/gluten/execution/GlutenClickHouseTPCHSuite.scala
index c517afcb29..a56f45d1ba 100644
--- a/backends-clickhouse/src/test/scala/org/apache/gluten/execution/GlutenClickHouseTPCHSuite.scala
+++ b/backends-clickhouse/src/test/scala/org/apache/gluten/execution/GlutenClickHouseTPCHSuite.scala
@@ -547,5 +547,21 @@ class GlutenClickHouseTPCHSuite extends GlutenClickHouseTPCHAbstractSuite {
compareResultsAgainstVanillaSpark(sql, true, { _ => })
spark.sql("drop table cross_join_t")
}
+
+ test("Pushdown aggregation pre-projection ahead expand") {
+ spark.sql("create table t1(a bigint, b bigint, c bigint, d bigint) using parquet")
+ spark.sql("insert into t1 values(1,2,3,4), (1,2,4,5), (1,3,4,5), (2,3,4,5)")
+ var sql = """
+ | select a, b , sum(d+c) from t1 group by a,b with cube
+ | order by a,b
+ |""".stripMargin
+ compareResultsAgainstVanillaSpark(sql, true, { _ => })
+ sql = """
+ | select a, b , sum(a+c), sum(b+d) from t1 group by a,b with cube
+ | order by a,b
+ |""".stripMargin
+ compareResultsAgainstVanillaSpark(sql, true, { _ => })
+ spark.sql("drop table t1")
+ }
}
// scalastyle:off line.size.limit
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@gluten.apache.org
For additional commands, e-mail: commits-help@gluten.apache.org