sunchao commented on code in PR #57332:
URL: https://github.com/apache/spark/pull/57332#discussion_r3627318835


##########
core/src/main/scala/org/apache/spark/api/python/PythonRunner.scala:
##########
@@ -154,6 +155,41 @@ private[spark] object BasePythonRunner extends Logging {
     } else None
   }
 
+  /**
+   * Splits the executor-wide pyspark memory allocation evenly across the 
executor's task
+   * slots. The Python worker pool can grow to the number of concurrently 
running tasks,
+   * which is floor(execCores / taskCpus) rather than the plain core count: a 
fractional
+   * `spark.task.cpus` below 1 admits more concurrent tasks than there are 
cores, and
+   * dividing by the core count alone would let the workers' aggregate limits 
exceed the
+   * executor-wide allocation.
+   */
+  private[spark] def getWorkerMemoryMb(
+      mem: Option[Long],
+      execCores: Int,
+      taskCpus: BigDecimal): Option[Long] = {
+    // The task cpus amount can exceed the announced cores in misconfigured 
corners (e.g.
+    // standalone mode, where EXECUTOR_CORES defaults to 1 regardless of the 
actual core
+    // count, see SPARK-30299); never split into less than one slot.
+    val taskSlots =
+      math.max(1, ResourceProfile.numTasksBasedOnCores(BigDecimal(execCores), 
taskCpus))

Review Comment:
   [P2] Account for the actual limiting resource before rejecting PySpark memory
   
   The new zero-share check assumes that `floor(executor cores / task CPUs)` is 
the number of concurrently runnable Python tasks, but a custom resource can 
impose a much lower limit.
   
   For example:
   
   - Executor: 64 cores, one GPU, and `spark.executor.pyspark.memory=512m`.
   - Task: `spark.task.cpus=0.1` and one GPU.
   
   The scheduler can run only one task because the GPU is the limiting 
resource, so a 512-MiB worker limit is entirely enforceable. This code instead 
calculates 640 CPU slots, rounds `512 / 640` down to zero, and fails every task 
as though the configuration were unsatisfiable.
   
   Please derive both the memory split and the fail-fast decision from the 
active resource profile's actual maximum concurrent tasks, including 
custom-resource limits, and add a GPU-limited regression test.



##########
sql/core/src/main/scala/org/apache/spark/sql/execution/BaseScriptTransformationExec.scala:
##########
@@ -84,10 +100,19 @@ trait BaseScriptTransformationExec extends UnaryExecNode {
     val path = System.getenv("PATH") + File.pathSeparator +
       SparkFiles.getRootDirectory()
     builder.environment().put("PATH", path)
-    // if OMP_NUM_THREADS is not explicitly set, override it with the value of 
"spark.task.cpus"
-    if (System.getenv("OMP_NUM_THREADS") == null) {
-      builder.environment().put("OMP_NUM_THREADS", 
conf.getConfString("spark.task.cpus", "1"))
-    }
+    // Derive the script process's OMP_NUM_THREADS from the task's own cpu 
amount, which honors
+    // stage-level resource profiles (a script transform can be a child of 
mapInPandas/mapInArrow
+    // carrying a different profile), rounded up to an integer >= 1. We check 
the config rather
+    // than System.getenv for an explicit user override, because Spark seeds 
the executor's
+    // OMP_NUM_THREADS with the global default, so a getenv check would always 
suppress the
+    // per-stage value.
+    val ompUserOverride =
+      
Option(SparkEnv.get).exists(_.conf.contains("spark.executorEnv.OMP_NUM_THREADS"))

Review Comment:
   [P2] Preserve OMP overrides supplied through the inherited environment
   
   The previous `System.getenv("OMP_NUM_THREADS")` check preserved any value 
explicitly inherited by the executor. This replacement recognizes only 
`spark.executorEnv.OMP_NUM_THREADS`, so an override supplied through a 
Kubernetes executor pod template, container image, or exported local-mode 
environment is no longer recognized.
   
   For example, an executor pod template can set `OMP_NUM_THREADS=8` without 
adding a SparkConf entry. A `0.5`-CPU task now silently replaces that inherited 
value with `1`, whereas the previous code preserved `8`.
   
   Please preserve inherited user-provided overrides while distinguishing them 
from Spark's automatically generated global default, and cover the actual 
script child-process environment in a regression test.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


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

Reply via email to