Weight stage duration average by task count - #2147
Conversation
amahussein
left a comment
There was a problem hiding this comment.
The change looks right, and the pooled form matches TaskMetricsAccumRec.finalizeAggregation exactly, so a merged stage now agrees with a single-attempt one. Two asks before merge.
- First, could the new test move into
AnalysisSuiterather than a new suite file? Same package, and it already covers this class of bug on the GPU side, whereSQL and app avg pool the reporting tasks rather than re-averaging stage meansis the same averaging-of-averages defect this fixes for stage attempts. Several tests there build result rows directly without a SparkSession, so the pattern fits. - Second, the validation: the run quoted in the description executes 1 test out of 993, never exercises Scala 2.13 or a non-default Spark version, and the 325-file scalastyle count is stale now that dev has 327. Worth redoing against current dev.
Two things the description could add, neither blocking: job-level and SQL-level duration_avg are unaffected, since TaskMetricsAccumRec recomputes from the pooled sum and count rather than reading the merged value; and the qualx featurizer consumes this column as its duration_mean model feature under the default stage-level filter, so a model input moves with it.
|
|
||
| import org.scalatest.funsuite.AnyFunSuite | ||
|
|
||
| class ProfileClassWarehouseSuite extends AnyFunSuite { |
There was a problem hiding this comment.
P0: Could you move this into AnalysisSuite rather than adding a new file? Same package, and it already covers this class of bug on the GPU side: SQL and app avg pool the reporting tasks rather than re-averaging stage means is the same averaging-of-averages defect that this fixes for stage attempts. Several tests there already build result rows directly without a SparkSession, so the pattern fits.
There was a problem hiding this comment.
Done — I moved the regression into AnalysisSuite, adjacent to the existing pooled-average coverage, and removed the standalone suite.
|
|
||
| assert(result.numTasks === 101) | ||
| assert(result.durationSum === 2000L) | ||
| assert(result.durationAvg === 19.8) |
There was a problem hiding this comment.
The 1 passed to calculateAverage is the rounding precision, and this assertion cannot see a change to it: 2000/101 rounds to 19.8 at one decimal place and 19.80 at two, which are the same Double. That matters because TaskMetricsAccumRec.finalizeAggregation hardcodes the same 1 for a single attempt, and the value reaches the CSV through a bare toString, so a drift would print merged rows at a different precision from unmerged ones in the same column. An input whose roundings differ, say numTasks = 3, durationSum = 1000 asserting 333.3, would pin it.
There was a problem hiding this comment.
Done — the fixture now pools two tasks totaling 800 ms with one retry task totaling 200 ms, and asserts 1000 / 3 = 333.3. This fails as 300.0 under the old averaging-of-averages implementation and pins the one-decimal precision.
|
|
||
| val result = firstAttempt.aggregateStageProfileMetric(retryAttempt) | ||
|
|
||
| assert(result.numTasks === 101) |
There was a problem hiding this comment.
These two assertions were already true before the change; only the durationAvg one exercises the fix.
There was a problem hiding this comment.
Done — I removed the redundant numTasks and durationSum assertions, leaving only the durationAvg assertion that exercises this fix.
| durationSum = this.durationSum + other.durationSum, | ||
| durationSum = mergedDurationSum, | ||
| durationMax = Math.max(this.durationMax, other.durationMax), | ||
| durationMin = Math.min(this.durationMin, other.durationMin), |
There was a problem hiding this comment.
Flagging for awareness; no change requested here. An attempt that ran zero tasks still produces a row, and finalizeAggregation calls resetFields() when numTasks < 1, which sets durationMin to 0. Math.min(0, x) then publishes 0 as the stage minimum. Measured: a stage whose only task took 907 ms reports duration_min of 0 once a second attempt is submitted and aborted before any task end, in an otherwise complete log. Your fix makes durationAvg immune to this, which leaves durationMin the last field in this merge that a zero-task attempt corrupts.
Filed separately as #2151.
There was a problem hiding this comment.
Thanks for isolating and filing this. I have kept the zero-task durationMin behavior out of this PR and referenced #2151 in the updated description.
| durationMax = Math.max(this.durationMax, other.durationMax), | ||
| durationMin = Math.min(this.durationMin, other.durationMin), | ||
| durationAvg = (this.durationAvg + other.durationAvg) / 2, | ||
| durationAvg = ToolUtils.calculateAverage(mergedDurationSum, mergedNumTasks, 1), |
There was a problem hiding this comment.
This is character-for-character what TaskMetricsAccumRec.finalizeAggregation uses for a single attempt, so a merged stage now publishes what one attempt carrying the same tasks would. Job and SQL rows already pooled this way, so the stage row was the only member of the family that did not.
Signed-off-by: WilliamK112 <164879897+WilliamK112@users.noreply.github.com>
f1e096e to
2394a4d
Compare
|
Addressed the review in
I also ran the full JDK 17 matrix locally: Spark 3.5.7 / Scala 2.12 (993 tests), Spark 3.2.1 / Scala 2.12 (974 tests), and Spark 3.5.7 / Scala 2.13 (993 tests), all passing; ScalaStyle checked 326 files with no errors or warnings. When convenient, could you please take another look? |
amahussein
left a comment
There was a problem hiding this comment.
LGTM The regression is now adjacent to the existing pooled-average coverage in AnalysisSuite
- One process note for future updates: please avoid rebasing an active PR, because the force-push discards review context. Merge
devinto the PR branch instead when it needs an update. - I updated the PR description because it has icorrectly stated that the QualX inherits the fix. The corrected fact is that QualX renames stage
duration_avgtoduration_meanbut then overwrites it withduration_sum / numTasks_sumbefore modeling.
Fixes #2145
Summary
durationAvgfrom the pooleddurationSumandnumTasksImpact and scope
duration_avgare unaffected because their aggregation already recomputes from pooled sums and task countsqualx consumes the corrected stage-level column as itsduration_meanmodel feature under the default stage-level filterduration_avgtoduration_meanbut then overwrites it withduration_sum / numTasks_sumbefore modeling.durationMinbehavior remains out of scope and is tracked separately by [BUG] duration_min reports 0 when a stage produced no tasks #2151Review updates
dev(36c1c56)AnalysisSuite333.3, which pins one-decimal precisionValidation
Using JDK 17:
mvn -Dbuildver=357 -DwildcardSuites=com.nvidia.spark.rapids.tool.profiling.AnalysisSuite test: 27/27 passedmvn -q -Dbuildver=357 verify: 993/993 passed (Spark 3.5.7, Scala 2.12)mvn -q -Dbuildver=321 verify: 974/974 passed (Spark 3.2.1, Scala 2.12)mvn -q -Pscala213 -Dbuildver=357 verify: 993/993 passed (Spark 3.5.7, Scala 2.13)