This is an automated email from the ASF dual-hosted git repository.

philo-he pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/gluten.git


The following commit(s) were added to refs/heads/main by this push:
     new 26d35367fb [GLUTEN-12702][VL] Introduce shouldIgnoreInFallbackStats to 
skip certain plan nodes in fallback counting (#12703)
26d35367fb is described below

commit 26d35367fb61e84d34dbce555e33995b445483bd
Author: Wechar Yu <[email protected]>
AuthorDate: Fri Aug 28 08:47:38 2026 +0800

    [GLUTEN-12702][VL] Introduce shouldIgnoreInFallbackStats to skip certain 
plan nodes in fallback counting (#12703)
---
 .../AutoAdjustStageResourceProfileSuite.scala      | 35 +++++++++++++
 .../GlutenAutoAdjustStageResourceProfile.scala     | 11 ++++-
 .../spark/sql/execution/GlutenExplainUtils.scala   | 57 +++++++++++++---------
 .../spark/sql/execution/GlutenImplicits.scala      | 34 +++----------
 4 files changed, 85 insertions(+), 52 deletions(-)

diff --git 
a/backends-velox/src/test/scala/org/apache/gluten/execution/AutoAdjustStageResourceProfileSuite.scala
 
b/backends-velox/src/test/scala/org/apache/gluten/execution/AutoAdjustStageResourceProfileSuite.scala
index 8e2eca4f78..2ef614e415 100644
--- 
a/backends-velox/src/test/scala/org/apache/gluten/execution/AutoAdjustStageResourceProfileSuite.scala
+++ 
b/backends-velox/src/test/scala/org/apache/gluten/execution/AutoAdjustStageResourceProfileSuite.scala
@@ -23,6 +23,7 @@ import org.apache.spark.annotation.Experimental
 import org.apache.spark.sql.execution.{ApplyResourceProfileExec, 
ColumnarShuffleExchangeExec, SparkPlan}
 import org.apache.spark.sql.execution.adaptive.AdaptiveSparkPlanHelper
 import org.apache.spark.sql.execution.exchange.ShuffleExchangeExec
+import org.apache.spark.sql.internal.SQLConf
 
 @Experimental
 class AutoAdjustStageResourceProfileSuite
@@ -131,6 +132,40 @@ class AutoAdjustStageResourceProfileSuite
     }
   }
 
+  test("Structural Spark nodes should not be counted as fallback nodes") {
+    withSQLConf(
+      GlutenConfig.AUTO_ADJUST_STAGE_RESOURCES_FALLEN_NODE_RATIO_THRESHOLD.key 
-> "0.5",
+      SQLConf.COALESCE_PARTITIONS_ENABLED.key -> "true"
+    ) {
+      runQueryAndCompare(
+        """
+          |SELECT /*+ BROADCAST(t2) */ t1.c1, t2.c1
+          |FROM tmp1 t1
+          |JOIN (
+          |  SELECT /*+ REBALANCE(c1) */ c1
+          |  FROM tmp2
+          |) t2
+          |ON t1.c1 = t2.c1
+          |""".stripMargin) {
+        // scalastyle:off
+        // format: off
+        /*
+        ColumnarBroadcastExchange 
HashedRelationBroadcastMode(List(cast(input[0, int, false] as bigint)),false), 
[plan_id=1267]
+          +- AQEShuffleRead coalesced
+            +- ShuffleQueryStage 0
+                +- ColumnarExchange hashpartitioning(c1#10, 5), 
REBALANCE_PARTITIONS_BY_COL, [c1#10], [plan_id=1194], 
[shuffle_writer_type=hash], [output=[c1#10: int]]
+                    +- VeloxResizeBatches
+                      +- ^(1) ProjectExecTransformer [hash(c1#10, 42) AS 
hash_partition_key#31, c1#10]
+                        +- ^(1) FilterExecTransformer isnotnull(c1#10)
+                          +- ^(1) FileFileSourceScanExecTransformer parquet 
spark_catalog.default.tmp2[c1#10] Batched: true, DataFilters: [isnotnull(c1#10)]
+        */
+        // format: on
+        // scalastyle:on
+        df => 
assert(collectApplyResourceProfileExec(df.queryExecution.executedPlan) == 0)
+      }
+    }
+  }
+
   test("Apply new resource profile when whole stage fallback") {
     withSQLConf(
       GlutenConfig.COLUMNAR_FALLBACK_PREFER_COLUMNAR.key -> "false",
diff --git 
a/gluten-substrait/src/main/scala/org/apache/spark/sql/execution/GlutenAutoAdjustStageResourceProfile.scala
 
b/gluten-substrait/src/main/scala/org/apache/spark/sql/execution/GlutenAutoAdjustStageResourceProfile.scala
index 8682fa6889..14669c51ac 100644
--- 
a/gluten-substrait/src/main/scala/org/apache/spark/sql/execution/GlutenAutoAdjustStageResourceProfile.scala
+++ 
b/gluten-substrait/src/main/scala/org/apache/spark/sql/execution/GlutenAutoAdjustStageResourceProfile.scala
@@ -19,6 +19,7 @@ package org.apache.spark.sql.execution
 import org.apache.gluten.config.{GlutenConfig, GlutenCoreConfig}
 import org.apache.gluten.execution.{ColumnarToRowExecBase, CudfTag, 
GlutenPlan, WholeStageTransformer}
 import org.apache.gluten.logging.LogLevelUtil
+import org.apache.gluten.utils.PlanUtil
 
 import org.apache.spark.SparkConf
 import org.apache.spark.annotation.Experimental
@@ -29,6 +30,7 @@ import org.apache.spark.sql.SparkSession
 import org.apache.spark.sql.catalyst.rules.Rule
 import org.apache.spark.sql.execution.{GlutenAutoAdjustStageResourceProfile => 
GlutenResourceProfile}
 import org.apache.spark.sql.execution.adaptive.QueryStageExec
+import org.apache.spark.sql.execution.columnar.InMemoryTableScanExec
 import org.apache.spark.sql.execution.command.{DataWritingCommandExec, 
ExecutedCommandExec}
 import org.apache.spark.sql.execution.exchange.Exchange
 import org.apache.spark.sql.internal.SQLConf
@@ -145,8 +147,13 @@ case class 
GlutenAutoAdjustStageResourceProfile(glutenConf: GlutenConfig, spark:
 
     // case 2: check whether fallback exists and decide whether increase heap 
memory
     // and decrease offheap memory.
-    val fallenNodeCnt = planNodes.count(p => !p.isInstanceOf[GlutenPlan])
-    val totalCount = planNodes.size
+    val countedPlanNodes = 
planNodes.filterNot(GlutenExplainUtils.shouldIgnoreInFallbackStats)
+    val fallenNodeCnt = countedPlanNodes.count {
+      case _: GlutenPlan => false
+      case i: InMemoryTableScanExec => !PlanUtil.isGlutenTableCache(i)
+      case _ => true
+    }
+    val totalCount = countedPlanNodes.size
 
     if (1.0 * fallenNodeCnt / totalCount >= 
glutenConf.autoAdjustStageFallenNodeThreshold) {
       val newMemoryAmount = memoryRequest.get.amount * 
glutenConf.autoAdjustStageRPHeapRatio
diff --git 
a/gluten-substrait/src/main/scala/org/apache/spark/sql/execution/GlutenExplainUtils.scala
 
b/gluten-substrait/src/main/scala/org/apache/spark/sql/execution/GlutenExplainUtils.scala
index 21694eea07..1151f4584a 100644
--- 
a/gluten-substrait/src/main/scala/org/apache/spark/sql/execution/GlutenExplainUtils.scala
+++ 
b/gluten-substrait/src/main/scala/org/apache/spark/sql/execution/GlutenExplainUtils.scala
@@ -45,6 +45,34 @@ import scala.collection.mutable.{ArrayBuffer, BitSet}
 object GlutenExplainUtils extends AdaptiveSparkPlanHelper {
   type FallbackInfo = (Int, Map[String, String])
 
+  /**
+   * Returns whether a plan should be ignored when collecting fallback 
statistics.
+   *
+   * Such plans may be execution-framework or implementation wrappers.
+   */
+  def shouldIgnoreInFallbackStats(plan: SparkPlan): Boolean = plan match {
+    case _: ExecutedCommandExec => true
+    case _: CommandResultExec => true
+    case p: V2CommandExec =>
+      !FallbackTags.nonEmpty(p) && 
p.logicalLink.forall(FallbackTags.getOption(_).isEmpty)
+    case _: DataWritingCommandExec => true
+    case _: WholeStageCodegenExec => true
+    case _: WholeStageTransformer => true
+    case _: InputAdapter => true
+    case _: ColumnarInputAdapter => true
+    case _: InputIteratorTransformer => true
+    case _: ColumnarToRowTransition => true
+    case _: RowToColumnarTransition => true
+    case _: ReusedExchangeExec => true
+    case _: NoopLeaf => true
+    case w: WriteFilesExec => w.child.isInstanceOf[NoopLeaf]
+    case _: AdaptiveSparkPlanExec => true
+    case _: QueryStageExec => true
+    case _: AQEShuffleReadExec => true
+    case _: ColumnarAQEShuffleReadExec => true
+    case _ => false
+  }
+
   def addFallbackNodeWithReason(
       p: SparkPlan,
       reason: String,
@@ -89,8 +117,7 @@ object GlutenExplainUtils extends AdaptiveSparkPlanHelper {
    *   - native Gluten operator: invokes `onGluten`
    *   - vanilla Spark operator considered a fallback: invokes `onFallback` 
with the reason resolved
    *     from physical or logical [[FallbackTags]], or a synthetic default 
when no tag is present
-   *   - structural / non-operator nodes (commands, transitions, codegen 
wrappers, query stages, AQE
-   *     shuffle reads, reused exchanges, etc.): no callback
+   *   - nodes ignored by [[shouldIgnoreInFallbackStats]]: no callback
    *
    * Recurses into AQE subqueries and query stages, and into each visited 
operator's
    * `innerChildren`. Subqueries reached purely via expressions on a vanilla 
Spark plan are not
@@ -111,38 +138,22 @@ object GlutenExplainUtils extends AdaptiveSparkPlanHelper 
{
 
     def visit(tmp: QueryPlan[_]): Unit = {
       tmp.foreachUp {
-        case _: ExecutedCommandExec =>
         case cmd: CommandResultExec => visit(cmd.commandPhysicalPlan)
         case p: V2CommandExec
-            if FallbackTags.nonEmpty(p) ||
-              p.logicalLink.exists(FallbackTags.getOption(_).nonEmpty) =>
+            if !shouldIgnoreInFallbackStats(p) =>
           onFallback(p, fallbackReason(p))
-        case _: V2CommandExec =>
-        case _: DataWritingCommandExec =>
-        case _: WholeStageCodegenExec =>
-        case _: WholeStageTransformer =>
-        case _: InputAdapter =>
-        case _: ColumnarInputAdapter =>
-        case _: InputIteratorTransformer =>
-        case _: ColumnarToRowTransition =>
-        case _: RowToColumnarTransition =>
-        case _: ReusedExchangeExec =>
-        case _: NoopLeaf =>
-        case w: WriteFilesExec if w.child.isInstanceOf[NoopLeaf] =>
         case sub: AdaptiveSparkPlanExec if sub.isSubquery => 
visit(sub.executedPlan)
-        case _: AdaptiveSparkPlanExec =>
         case p: QueryStageExec => visit(p.plan)
-        case p: GlutenPlan =>
-          onGluten(p)
-          p.innerChildren.foreach(visit)
         case i: InMemoryTableScanExec =>
           if (PlanUtil.isGlutenTableCache(i)) {
             onGluten(i)
           } else {
             onFallback(i, "Columnar table cache is disabled")
           }
-        case _: AQEShuffleReadExec => // Ignore
-        case _: ColumnarAQEShuffleReadExec => // Ignore
+        case p: SparkPlan if shouldIgnoreInFallbackStats(p) => // Ignore
+        case p: GlutenPlan =>
+          onGluten(p)
+          p.innerChildren.foreach(visit)
         case p: SparkPlan =>
           onFallback(p, fallbackReason(p))
           p.innerChildren.foreach(visit)
diff --git 
a/gluten-substrait/src/main/scala/org/apache/spark/sql/execution/GlutenImplicits.scala
 
b/gluten-substrait/src/main/scala/org/apache/spark/sql/execution/GlutenImplicits.scala
index 6e54ec5dfd..a4354220b7 100644
--- 
a/gluten-substrait/src/main/scala/org/apache/spark/sql/execution/GlutenImplicits.scala
+++ 
b/gluten-substrait/src/main/scala/org/apache/spark/sql/execution/GlutenImplicits.scala
@@ -17,8 +17,7 @@
 package org.apache.spark.sql.execution
 
 import org.apache.gluten.exception.GlutenException
-import org.apache.gluten.execution.{GlutenPlan, WholeStageTransformer}
-import org.apache.gluten.extension.columnar.FallbackTags
+import org.apache.gluten.execution.GlutenPlan
 import org.apache.gluten.sql.shims.SparkShimLoader
 import org.apache.gluten.utils.PlanUtil
 
@@ -27,13 +26,9 @@ import org.apache.spark.sql.catalyst.plans.QueryPlan
 import org.apache.spark.sql.catalyst.plans.logical.{CommandResult, LogicalPlan}
 import org.apache.spark.sql.catalyst.util.StringUtils.PlanStringConcat
 import org.apache.spark.sql.classic.ClassicConversions._
-import org.apache.spark.sql.execution.ColumnarWriteFilesExec.NoopLeaf
-import org.apache.spark.sql.execution.adaptive.{AdaptiveSparkPlanExec, 
AQEShuffleReadExec, ColumnarAQEShuffleReadExec, QueryStageExec}
+import org.apache.spark.sql.execution.adaptive.{AdaptiveSparkPlanExec, 
QueryStageExec}
 import org.apache.spark.sql.execution.columnar.InMemoryTableScanExec
-import org.apache.spark.sql.execution.command.{DataWritingCommandExec, 
ExecutedCommandExec}
-import org.apache.spark.sql.execution.datasources.WriteFilesExec
 import org.apache.spark.sql.execution.datasources.v2.V2CommandExec
-import org.apache.spark.sql.execution.exchange.ReusedExchangeExec
 import org.apache.spark.sql.internal.SQLConf
 
 import scala.collection.mutable
@@ -108,24 +103,10 @@ object GlutenImplicits {
 
     def collect(tmp: QueryPlan[_]): Unit = {
       tmp.foreachUp {
-        case _: ExecutedCommandExec =>
         case cmd: CommandResultExec => collect(cmd.commandPhysicalPlan)
         case p: V2CommandExec
-            if FallbackTags.nonEmpty(p) ||
-              p.logicalLink.exists(FallbackTags.getOption(_).nonEmpty) =>
+            if !GlutenExplainUtils.shouldIgnoreInFallbackStats(p) =>
           GlutenExplainUtils.handleVanillaSparkPlan(p, fallbackNodeToReason)
-        case _: V2CommandExec =>
-        case _: DataWritingCommandExec =>
-        case _: WholeStageCodegenExec =>
-        case _: WholeStageTransformer =>
-        case _: InputAdapter =>
-        case _: ColumnarInputAdapter =>
-        case _: InputIteratorTransformer =>
-        case _: ColumnarToRowTransition =>
-        case _: RowToColumnarTransition =>
-        case p: ReusedExchangeExec =>
-        case _: NoopLeaf =>
-        case w: WriteFilesExec if w.child.isInstanceOf[NoopLeaf] =>
         case p: AdaptiveSparkPlanExec if isFinalAdaptivePlan(p) =>
           collect(p.executedPlan)
         case p: AdaptiveSparkPlanExec =>
@@ -149,9 +130,6 @@ object GlutenImplicits {
           numGlutenNodes += innerNumGlutenNodes
           fallbackNodeToReason.++=(innerFallbackNodeToReason)
         case p: QueryStageExec => collect(p.plan)
-        case p: GlutenPlan =>
-          numGlutenNodes += 1
-          p.innerChildren.foreach(collect)
         case i: InMemoryTableScanExec =>
           if (PlanUtil.isGlutenTableCache(i)) {
             numGlutenNodes += 1
@@ -162,8 +140,10 @@ object GlutenImplicits {
               fallbackNodeToReason)
           }
           collect(i.relation.cachedPlan)
-        case _: AQEShuffleReadExec => // Ignore
-        case _: ColumnarAQEShuffleReadExec => // Ignore
+        case p: SparkPlan if GlutenExplainUtils.shouldIgnoreInFallbackStats(p) 
=> // Ignore
+        case p: GlutenPlan =>
+          numGlutenNodes += 1
+          p.innerChildren.foreach(collect)
         case p: SparkPlan =>
           GlutenExplainUtils.handleVanillaSparkPlan(p, fallbackNodeToReason)
           p.innerChildren.foreach(collect)


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to