package platform import ( "context" "encoding/json" "fmt" "os" "path/filepath" "regexp" "strconv" "strings" "time" ) const benchmarkVersion = "2" type benchmarkProfileSpec struct { Name string BaselineSec int WarmupSec int SteadySec int NCCLSec int CooldownSec int } type benchmarkGPUInfo struct { Index int UUID string Name string BusID string VBIOS string PowerLimitW float64 DefaultPowerLimitW float64 MinPowerLimitW float64 MaxPowerLimitW float64 MaxGraphicsClockMHz float64 MaxMemoryClockMHz float64 BaseGraphicsClockMHz float64 MultiprocessorCount int // Temperature limits sourced from nvidia-smi -q verbose output. // ShutdownTempC is the hardware thermal shutdown threshold. // SlowdownTempC is the software throttle onset threshold. // Both fall back to safe conservative defaults when not available. ShutdownTempC float64 // fallback: 90°C SlowdownTempC float64 // fallback: 80°C } type benchmarkPowerCalibrationResult struct { Summary BenchmarkTelemetrySummary AppliedPowerLimitW float64 Attempts int Derated bool Completed bool Notes []string // CoolingWarning is set when the GPU throttled thermally with a clock drop // ≥20% while server fans were below 100% duty cycle — a signal that the // cooling system may not be correctly configured for full GPU load. CoolingWarning string // MetricRows holds the telemetry rows from the final (converged) attempt // for this GPU. Used to build per-run gpu-metrics.csv. MetricRows []GPUMetricRow } type benchmarkPowerCalibrationRunSummary struct { LoadedSDR benchmarkSDRSeriesSummary AvgFanRPM float64 AvgFanDutyCyclePct float64 FanSamples int } type benchmarkBurnProfile struct { name string category string supported bool lanes int m uint64 n uint64 k uint64 iterations uint64 notes string } type benchmarkBurnParseResult struct { Device string ComputeCapability string Backend string DurationSec int Profiles []BenchmarkPrecisionResult Fallback bool } type benchmarkRestoreAction struct { name string fn func() } var ( benchmarkReadyPattern = regexp.MustCompile(`^([a-z0-9_]+)\[(\d+)\]=READY dim=(\d+)x(\d+)x(\d+)\b`) benchmarkSkippedPattern = regexp.MustCompile(`^([a-z0-9_]+)(?:\[\d+\])?=SKIPPED (.+)$`) benchmarkIterationsPattern = regexp.MustCompile(`^([a-z0-9_]+)_iterations=(\d+)$`) benchmarkGeteuid = os.Geteuid benchmarkResetNvidiaGPU = resetNvidiaGPU benchmarkSleep = time.Sleep ) // benchmarkPrecisionPhases lists the precision categories run as individual // steady-state windows before the combined steady pass. Order is from lowest // to highest power draw so thermal ramp-up is gradual. // // fp64 and fp4 are intentionally disabled for now: both are currently unstable // on the target fleet and can abort the mixed steady stage after the earlier // phases already collected useful telemetry. var benchmarkPrecisionPhases = []string{"int8", "fp8", "fp16", "fp32"} func computeCapabilityCode(raw string) int { raw = strings.TrimSpace(raw) if raw == "" { return 0 } parts := strings.SplitN(raw, ".", 2) major, _ := strconv.Atoi(strings.TrimSpace(parts[0])) minor := 0 if len(parts) > 1 { minor, _ = strconv.Atoi(strings.TrimSpace(parts[1])) } return major*10 + minor } func benchmarkSupportedPrecisions(computeCapability string) []string { cc := computeCapabilityCode(computeCapability) out := make([]string, 0, len(benchmarkPrecisionPhases)) for _, prec := range benchmarkPrecisionPhases { if prec == "fp4" && cc > 0 && cc < 100 { continue } out = append(out, prec) } return out } func benchmarkPrecisionEnabled(category string) bool { switch category { case "int8", "fp8", "fp16", "fp16_bf16", "fp32", "fp32_tf32": return true default: return false } } func buildBenchmarkSteadyPlan(spec benchmarkProfileSpec, precisions []string, metricStage func(string) string) (planLabels []string, planPhases []benchmarkPlannedPhase, basePhaseSec int, mixedPhaseSec int) { if len(precisions) == 0 { precisions = append([]string(nil), benchmarkPrecisionPhases...) } switch spec.Name { case NvidiaBenchmarkProfileStandard: basePhaseSec = 60 mixedPhaseSec = 300 case NvidiaBenchmarkProfileStability: basePhaseSec = 300 mixedPhaseSec = 3600 case NvidiaBenchmarkProfileOvernight: basePhaseSec = 3600 mixedPhaseSec = 14400 default: totalWeight := len(precisions) + 5 if totalWeight <= 0 { return nil, nil, 0, 0 } basePhaseSec = spec.SteadySec / totalWeight if basePhaseSec <= 0 { basePhaseSec = 1 } mixedPhaseSec = basePhaseSec * 5 } planLabels = make([]string, 0, len(precisions)+1) planPhases = make([]benchmarkPlannedPhase, 0, len(precisions)+1) for _, prec := range precisions { planLabels = append(planLabels, prec) planPhases = append(planPhases, benchmarkPlannedPhase{ PlanLabel: prec, MetricStage: metricStage(prec), DurationSec: basePhaseSec, }) } planLabels = append(planLabels, "mixed") planPhases = append(planPhases, benchmarkPlannedPhase{ PlanLabel: "mixed", MetricStage: metricStage("mixed"), DurationSec: mixedPhaseSec, }) return planLabels, planPhases, basePhaseSec, mixedPhaseSec } func benchmarkPlanDurationsCSV(phases []benchmarkPlannedPhase) string { values := make([]string, 0, len(phases)) for _, phase := range phases { values = append(values, strconv.Itoa(phase.DurationSec)) } return strings.Join(values, ",") } func benchmarkPlannedPhaseStatus(raw []byte) (string, string) { text := strings.ToLower(strings.TrimSpace(string(raw))) switch { case text == "": return "FAILED", "phase produced no output" case strings.Contains(text, "phase_error="): if strings.Contains(text, "unsupported") || strings.Contains(text, "not supported") || strings.Contains(text, "cublaslt_profiles=unsupported") { return "UNSUPPORTED", "precision phase unsupported on this GPU/userspace path" } return "FAILED", "precision phase failed" case strings.Contains(text, "status=failed"): if strings.Contains(text, "unsupported") || strings.Contains(text, "not supported") { return "UNSUPPORTED", "precision phase unsupported on this GPU/userspace path" } return "FAILED", "precision phase failed" default: return "OK", "" } } func benchmarkCalibrationThrottleReason(before, after BenchmarkThrottleCounters) string { diff := diffThrottleCounters(before, after) switch { case diff.HWThermalSlowdownUS > 0: return "hw_thermal" case diff.SWThermalSlowdownUS > 0: return "sw_thermal" default: return "" } } func setBenchmarkPowerLimit(ctx context.Context, verboseLog string, gpuIndex, powerLimitW int) error { if powerLimitW <= 0 { return fmt.Errorf("invalid power limit %d", powerLimitW) } out, err := runSATCommandCtx(ctx, verboseLog, fmt.Sprintf("gpu-%d-set-power-limit-%dw", gpuIndex, powerLimitW), []string{ "nvidia-smi", "-i", strconv.Itoa(gpuIndex), "-pl", strconv.Itoa(powerLimitW), }, nil, nil) if err != nil { return fmt.Errorf("set power limit gpu=%d limit=%dw: %w (%s)", gpuIndex, powerLimitW, err, strings.TrimSpace(string(out))) } return nil } func resetBenchmarkGPU(ctx context.Context, verboseLog string, gpuIndex int, logFunc func(string)) error { if logFunc != nil { logFunc(fmt.Sprintf("power benchmark pre-flight: GPU %d reset via shared NVIDIA recover path", gpuIndex)) } out, err := benchmarkResetNvidiaGPU(gpuIndex) appendSATVerboseLog(verboseLog, fmt.Sprintf("[%s] start power-preflight-gpu-%d-reset.log", time.Now().UTC().Format(time.RFC3339), gpuIndex), "cmd: bee-nvidia-recover reset-gpu "+strconv.Itoa(gpuIndex), ) if trimmed := strings.TrimSpace(out); trimmed != "" && logFunc != nil { for _, line := range strings.Split(trimmed, "\n") { line = strings.TrimSpace(line) if line != "" { logFunc(line) } } } rc := 0 if err != nil { rc = 1 } appendSATVerboseLog(verboseLog, fmt.Sprintf("[%s] finish power-preflight-gpu-%d-reset.log", time.Now().UTC().Format(time.RFC3339), gpuIndex), fmt.Sprintf("rc: %d", rc), "", ) return err } func resetBenchmarkGPUs(ctx context.Context, verboseLog string, gpuIndices []int, logFunc func(string)) []int { if len(gpuIndices) == 0 { return nil } if benchmarkGeteuid() != 0 { if logFunc != nil { logFunc("power benchmark pre-flight: root privileges unavailable, GPU reset skipped") } return append([]int(nil), gpuIndices...) } if killed := KillTestWorkers(); len(killed) > 0 && logFunc != nil { for _, p := range killed { logFunc(fmt.Sprintf("power benchmark pre-flight: killed stale worker pid=%d name=%s", p.PID, p.Name)) } } var failed []int for _, idx := range gpuIndices { if err := resetBenchmarkGPU(ctx, verboseLog, idx, logFunc); err != nil { failed = append(failed, idx) if logFunc != nil { logFunc(fmt.Sprintf("power benchmark pre-flight: GPU %d reset failed: %v", idx, err)) } continue } if logFunc != nil { logFunc(fmt.Sprintf("power benchmark pre-flight: GPU %d reset completed", idx)) } benchmarkSleep(time.Second) } return failed } func benchmarkPowerEngine() string { switch strings.TrimSpace(strings.ToLower(os.Getenv("BEE_BENCH_POWER_ENGINE"))) { case BenchmarkPowerEngineTargetedPower: return BenchmarkPowerEngineTargetedPower default: return BenchmarkPowerEngineDCGMProfTester } } func benchmarkPowerEngineLabel(engine string) string { switch strings.TrimSpace(strings.ToLower(engine)) { case BenchmarkPowerEngineTargetedPower: return "dcgmi diag targeted_power" default: return "dcgmproftester" } } func resolveBenchmarkPowerLoadCommand(durationSec int, gpuIndices []int) ([]string, []string, error) { engine := benchmarkPowerEngine() durationSec = normalizeNvidiaBurnDuration(durationSec) switch engine { case BenchmarkPowerEngineTargetedPower: return nvidiaDCGMNamedDiagCommand("targeted_power", durationSec, gpuIndices), nil, nil default: if len(gpuIndices) > 1 { return []string{ "bee-dcgmproftester-staggered", "--seconds", strconv.Itoa(durationSec), "--stagger-seconds", "0", "--devices", joinIndexList(gpuIndices), }, nil, nil } cmd, err := resolveDCGMProfTesterCommand("--no-dcgm-validation", "-t", "1004", "-d", strconv.Itoa(durationSec)) if err != nil { return nil, nil, err } return cmd, nvidiaVisibleDevicesEnv(gpuIndices), nil } } func (s *System) RunNvidiaBenchmark(ctx context.Context, baseDir string, opts NvidiaBenchmarkOptions, logFunc func(string)) (string, error) { if ctx == nil { ctx = context.Background() } if logFunc == nil { logFunc = func(string) {} } if strings.TrimSpace(baseDir) == "" { baseDir = "/var/log/bee-bench/perf" } spec := resolveBenchmarkProfile(opts.Profile) opts = normalizeNvidiaBenchmarkOptionsForBenchmark(opts) selected, err := resolveNvidiaGPUSelection(opts.GPUIndices, opts.ExcludeGPUIndices) if err != nil { return "", err } if len(selected) == 0 { return "", fmt.Errorf("no NVIDIA GPUs selected") } ts := time.Now().UTC().Format("20060102-150405") runDir := filepath.Join(baseDir, "perf-"+ts) if err := os.MkdirAll(runDir, 0755); err != nil { return "", fmt.Errorf("mkdir %s: %w", runDir, err) } verboseLog := filepath.Join(runDir, "verbose.log") hostname, _ := os.Hostname() result := NvidiaBenchmarkResult{ BenchmarkVersion: benchmarkVersion, GeneratedAt: time.Now().UTC(), Hostname: hostname, ServerModel: readServerModel(), BenchmarkProfile: spec.Name, ParallelGPUs: opts.ParallelGPUs, RampStep: opts.RampStep, RampTotal: opts.RampTotal, RampRunID: opts.RampRunID, SelectedGPUIndices: append([]int(nil), selected...), HostConfig: readBenchmarkHostConfig(), Normalization: BenchmarkNormalization{ Status: "full", }, } logFunc(fmt.Sprintf("NVIDIA benchmark profile=%s gpus=%s", spec.Name, joinIndexList(selected))) psuBefore := psuStatusSnapshot() var metricRows []GPUMetricRow metricTimelineSec := 0.0 gpuBurnLog := filepath.Join(runDir, "gpu-burn.log") // Server power characterization state — populated during per-GPU phases. var serverIdleW, serverLoadedWSum float64 var serverIdleOK, serverLoadedOK bool var serverLoadedSamples int // Run nvidia-smi -q first: used both for the log file and as a fallback // source of max clock values when CSV clock fields are unsupported. var nvsmiQOut []byte if out, err := runSATCommandCtx(ctx, verboseLog, "00-nvidia-smi-q.log", []string{"nvidia-smi", "-q"}, nil, nil); err == nil { nvsmiQOut = out _ = os.WriteFile(filepath.Join(runDir, "00-nvidia-smi-q.log"), out, 0644) } infoByIndex, infoErr := queryBenchmarkGPUInfo(selected) if infoErr != nil { result.Warnings = append(result.Warnings, "gpu inventory query failed: "+infoErr.Error()) result.Normalization.Status = "partial" } // Enrich with verbose nvidia-smi data — covers GPUs where some CSV fields // are unsupported (e.g. clocks.max.* on Blackwell / driver 98.x). enrichGPUInfoWithNvidiaSMIQ(infoByIndex, nvsmiQOut) activeApps, err := queryActiveComputeApps(selected) if err == nil && len(activeApps) > 0 { result.Warnings = append(result.Warnings, "active GPU compute processes detected before benchmark") result.Normalization.Notes = append(result.Normalization.Notes, activeApps...) result.Normalization.Status = "partial" } restoreActions := applyBenchmarkNormalization(ctx, verboseLog, selected, infoByIndex, &result) defer func() { for i := len(restoreActions) - 1; i >= 0; i-- { restoreActions[i].fn() } }() // No power calibration before performance benchmark — GPUs run at their // default power limits. PowerSustainScore is derived from steady-state power // observed during the benchmark itself. calibByIndex := make(map[int]benchmarkPowerCalibrationResult) // Start background CPU load sampler — samples every 10s during GPU phases. cpuStopCh := make(chan struct{}) cpuSamplesCh := startCPULoadSampler(cpuStopCh, 10) if opts.ParallelGPUs { runNvidiaBenchmarkParallel(ctx, verboseLog, runDir, selected, infoByIndex, opts, spec, logFunc, &result, calibByIndex, &serverIdleW, &serverLoadedWSum, &serverIdleOK, &serverLoadedOK, &serverLoadedSamples, &metricRows, &metricTimelineSec, gpuBurnLog) } else { for _, idx := range selected { gpuResult := BenchmarkGPUResult{ Index: idx, Status: "FAILED", } if info, ok := infoByIndex[idx]; ok { gpuResult.UUID = info.UUID gpuResult.Name = info.Name gpuResult.BusID = info.BusID gpuResult.VBIOS = info.VBIOS gpuResult.PowerLimitW = info.PowerLimitW gpuResult.MultiprocessorCount = info.MultiprocessorCount gpuResult.DefaultPowerLimitW = info.DefaultPowerLimitW gpuResult.ShutdownTempC = info.ShutdownTempC gpuResult.SlowdownTempC = info.SlowdownTempC gpuResult.MaxGraphicsClockMHz = info.MaxGraphicsClockMHz gpuResult.BaseGraphicsClockMHz = info.BaseGraphicsClockMHz gpuResult.MaxMemoryClockMHz = info.MaxMemoryClockMHz } if calib, ok := calibByIndex[idx]; ok { gpuResult.CalibratedPeakPowerW = calib.Summary.P95PowerW gpuResult.CalibratedPeakTempC = calib.Summary.P95TempC gpuResult.PowerCalibrationTries = calib.Attempts gpuResult.PowerLimitDerated = calib.Derated gpuResult.Notes = append(gpuResult.Notes, calib.Notes...) if calib.CoolingWarning != "" { gpuResult.CoolingWarning = calib.CoolingWarning } } if norm := findBenchmarkNormalization(result.Normalization.GPUs, idx); norm != nil { gpuResult.LockedGraphicsClockMHz = norm.GPUClockLockMHz gpuResult.LockedMemoryClockMHz = norm.MemoryClockLockMHz } baselineRows, err := collectBenchmarkSamples(ctx, spec.BaselineSec, []int{idx}) if err != nil && err != context.Canceled { gpuResult.Notes = append(gpuResult.Notes, "baseline sampling failed: "+err.Error()) } gpuResult.Baseline = summarizeBenchmarkTelemetry(baselineRows) appendBenchmarkMetrics(&metricRows, baselineRows, fmt.Sprintf("gpu-%d-baseline", idx), &metricTimelineSec, float64(spec.BaselineSec)) // Sample server idle power once (first GPU only — server state is global). if !serverIdleOK { if w, ok := sampleBenchmarkPowerSourceSeries(ctx, opts.ServerPowerSource, maxInt(spec.BaselineSec, 10), benchmarkPowerAutotuneSampleInterval); ok { serverIdleW = w serverIdleOK = true logFunc(fmt.Sprintf("server idle power (%s): %.0f W", opts.ServerPowerSource, w)) } } warmupCmd := []string{ "bee-gpu-burn", "--seconds", strconv.Itoa(spec.WarmupSec), "--size-mb", strconv.Itoa(opts.SizeMB), "--devices", strconv.Itoa(idx), } logFunc(fmt.Sprintf("GPU %d: warmup (%ds)", idx, spec.WarmupSec)) warmupOut, warmupRows, warmupErr := runBenchmarkCommandWithMetrics(ctx, verboseLog, fmt.Sprintf("gpu-%d-warmup.log", idx), warmupCmd, nil, []int{idx}, logFunc) appendBenchmarkMetrics(&metricRows, warmupRows, fmt.Sprintf("gpu-%d-warmup", idx), &metricTimelineSec, float64(spec.WarmupSec)) appendBenchmarkStageLog(gpuBurnLog, "bee-gpu-burn", fmt.Sprintf("gpu-%d-warmup", idx), warmupOut) if warmupErr != nil { gpuResult.Notes = append(gpuResult.Notes, "warmup failed: "+warmupErr.Error()) result.GPUs = append(result.GPUs, finalizeBenchmarkGPUResult(gpuResult)) continue } warmupParse := parseBenchmarkBurnLog(string(warmupOut)) if gpuResult.ComputeCapability == "" { gpuResult.ComputeCapability = warmupParse.ComputeCapability } // Run synthetic precision phases and the combined steady phase as one // uninterrupted command so the GPU stays hot between windows. eccBase, _ := queryECCCounters(idx) supportedPrecisions := benchmarkSupportedPrecisions(gpuResult.ComputeCapability) planLabels, planPhases, basePhaseSec, mixedPhaseSec := buildBenchmarkSteadyPlan(spec, supportedPrecisions, func(label string) string { if label == "mixed" { return fmt.Sprintf("gpu-%d-steady", idx) } return fmt.Sprintf("gpu-%d-steady-%s", idx, label) }) planCmd := []string{ "bee-gpu-burn", "--seconds", strconv.Itoa(basePhaseSec), "--size-mb", strconv.Itoa(opts.SizeMB), "--devices", strconv.Itoa(idx), "--precision-plan", strings.Join(planLabels, ","), "--precision-plan-seconds", benchmarkPlanDurationsCSV(planPhases), } logFunc(fmt.Sprintf("GPU %d: uninterrupted precision plan (%d precision phases x %ds, mixed %ds)", idx, len(supportedPrecisions), basePhaseSec, mixedPhaseSec)) serverPowerStopCh := make(chan struct{}) serverPowerCh := startSelectedPowerSourceSampler(serverPowerStopCh, opts.ServerPowerSource, benchmarkPowerAutotuneSampleInterval) _, phaseRowsByStage, phaseLogs, planErr := runBenchmarkPlannedCommandWithMetrics(ctx, verboseLog, fmt.Sprintf("gpu-%d-precision-plan.log", idx), planCmd, nil, []int{idx}, planPhases, logFunc) close(serverPowerStopCh) if serverPowerSamples := <-serverPowerCh; len(serverPowerSamples) > 0 { serverLoadedWSum += benchmarkMean(serverPowerSamples) serverLoadedSamples++ serverLoadedOK = true logFunc(fmt.Sprintf("GPU %d: server loaded power (%s avg): %.0f W", idx, opts.ServerPowerSource, benchmarkMean(serverPowerSamples))) } for _, phaseSpec := range planPhases { if rows := phaseRowsByStage[phaseSpec.MetricStage]; len(rows) > 0 { appendBenchmarkMetrics(&metricRows, rows, phaseSpec.MetricStage, &metricTimelineSec, float64(phaseSpec.DurationSec)) } appendBenchmarkStageLog(gpuBurnLog, "bee-gpu-burn", phaseSpec.MetricStage, phaseLogs[phaseSpec.PlanLabel]) } for _, prec := range supportedPrecisions { stageName := fmt.Sprintf("gpu-%d-steady-%s", idx, prec) phaseRows := phaseRowsByStage[stageName] phase := BenchmarkPrecisionSteadyPhase{ Precision: prec, Status: "OK", Steady: summarizeBenchmarkTelemetry(phaseRows), } if status, note := benchmarkPlannedPhaseStatus(phaseLogs[prec]); status != "OK" { phase.Status = status phase.Notes = note gpuResult.PrecisionFailures = append(gpuResult.PrecisionFailures, prec+":"+status) } for _, p := range parseBenchmarkBurnLog(string(phaseLogs[prec])).Profiles { if p.Supported { phase.TeraOpsPerSec += p.TeraOpsPerSec phase.WeightedTeraOpsPerSec += p.WeightedTeraOpsPerSec } } gpuResult.PrecisionSteady = append(gpuResult.PrecisionSteady, phase) } beforeThrottle, _ := queryThrottleCounters(idx) logFunc(fmt.Sprintf("GPU %d: steady compute (combined, %ds)", idx, mixedPhaseSec)) afterThrottle, _ := queryThrottleCounters(idx) if planErr != nil { gpuResult.Notes = append(gpuResult.Notes, "precision plan failed: "+planErr.Error()) } steadyRows := phaseRowsByStage[fmt.Sprintf("gpu-%d-steady", idx)] parseResult := parseBenchmarkBurnLog(string(phaseLogs["mixed"])) gpuResult.ComputeCapability = parseResult.ComputeCapability gpuResult.Backend = parseResult.Backend gpuResult.PrecisionResults = parseResult.Profiles if parseResult.Fallback { gpuResult.Notes = append(gpuResult.Notes, "benchmark used driver PTX fallback; tensor throughput score is not comparable") } gpuResult.Steady = summarizeBenchmarkTelemetry(steadyRows) gpuResult.Throttle = diffThrottleCounters(beforeThrottle, afterThrottle) if eccFinal, err := queryECCCounters(idx); err == nil { gpuResult.ECC = diffECCCounters(eccBase, eccFinal) } if spec.CooldownSec > 0 { cooldownRows, err := collectBenchmarkSamples(ctx, spec.CooldownSec, []int{idx}) if err != nil && err != context.Canceled { gpuResult.Notes = append(gpuResult.Notes, "cooldown sampling failed: "+err.Error()) } gpuResult.Cooldown = summarizeBenchmarkTelemetry(cooldownRows) appendBenchmarkMetrics(&metricRows, cooldownRows, fmt.Sprintf("gpu-%d-cooldown", idx), &metricTimelineSec, float64(spec.CooldownSec)) } applyBenchmarkSteadyFallback(&gpuResult) gpuResult.Scores = scoreBenchmarkGPUResult(gpuResult) gpuResult.DegradationReasons = detectBenchmarkDegradationReasons(gpuResult, result.Normalization.Status) if anomaly := detectPowerAnomaly(metricRows, idx); anomaly != "" { gpuResult.Notes = append(gpuResult.Notes, fmt.Sprintf("[HARD STOP] GPU %d: %s", idx, anomaly)) } if warn := detectSlowdownTempExceedance(metricRows, idx, gpuResult.SlowdownTempC); warn != "" { gpuResult.Notes = append(gpuResult.Notes, fmt.Sprintf("[WARNING] GPU %d: %s", idx, warn)) if gpuResult.Status == "OK" { gpuResult.Status = "PARTIAL" } } if planErr != nil { gpuResult.Status = classifySATErrorStatus(phaseLogs["mixed"], planErr) } else if len(gpuResult.PrecisionFailures) > 0 { gpuResult.Status = "PARTIAL" } else if parseResult.Fallback { gpuResult.Status = "PARTIAL" } else { gpuResult.Status = "OK" } result.GPUs = append(result.GPUs, finalizeBenchmarkGPUResult(gpuResult)) } } // end sequential path // Performance scalability ramp-up: run parallel benchmarks for k=2..N GPUs // and compute compute scalability relative to the best single-GPU result. // Only runs in sequential mode (each GPU was tested individually above) and // when there are at least 2 GPUs. if !opts.ParallelGPUs && len(selected) >= 2 { // Find the best single-card SyntheticScore as the 1-GPU baseline. var bestTOPS float64 for _, g := range result.GPUs { if g.Scores.SyntheticScore > bestTOPS { bestTOPS = g.Scores.SyntheticScore } } if bestTOPS > 0 { var rampSteps []NvidiaPerformanceRampStep var scalabilityPcts []float64 for k := 2; k <= len(selected); k++ { subset := append([]int(nil), selected[:k]...) rampDir := filepath.Join(runDir, fmt.Sprintf("ramp-%02d", k)) _ = os.MkdirAll(rampDir, 0755) logFunc(fmt.Sprintf("performance ramp: step %d/%d — running %d GPUs in parallel", k, len(selected), k)) var rampResult NvidiaBenchmarkResult var rampIdleW, rampLoadedWSum float64 var rampIdleOK, rampLoadedOK bool var rampLoadedSamples int var rampMetricRows []GPUMetricRow var rampTimelineSec float64 emptyCalib := make(map[int]benchmarkPowerCalibrationResult) runNvidiaBenchmarkParallel(ctx, verboseLog, rampDir, subset, infoByIndex, opts, spec, logFunc, &rampResult, emptyCalib, &rampIdleW, &rampLoadedWSum, &rampIdleOK, &rampLoadedOK, &rampLoadedSamples, &rampMetricRows, &rampTimelineSec, "") var totalSynth, totalMixed float64 for _, g := range rampResult.GPUs { totalSynth += g.Scores.SyntheticScore totalMixed += g.Scores.MixedScore } scalPct := totalSynth / (float64(k) * bestTOPS) * 100 scalabilityPcts = append(scalabilityPcts, scalPct) stepStatus := "OK" if len(rampResult.GPUs) < k { stepStatus = "PARTIAL" } rampSteps = append(rampSteps, NvidiaPerformanceRampStep{ StepIndex: k, GPUIndices: subset, TotalSyntheticTOPS: totalSynth, TotalMixedTOPS: totalMixed, ScalabilityPct: scalPct, Status: stepStatus, }) } result.PerformanceRampSteps = rampSteps result.PlatformPowerScore = benchmarkMean(scalabilityPcts) if len(scalabilityPcts) > 0 { result.ScalabilityScore = scalabilityPcts[len(scalabilityPcts)-1] } } } if len(selected) > 1 && opts.RunNCCL { result.Interconnect = runBenchmarkInterconnect(ctx, verboseLog, runDir, selected, spec, logFunc) if result.Interconnect != nil && result.Interconnect.Supported { for i := range result.GPUs { result.GPUs[i].Scores.InterconnectScore = result.Interconnect.MaxBusBWGBps } } } // Stop CPU load sampler and attach results. close(cpuStopCh) if cpuSamples := <-cpuSamplesCh; len(cpuSamples) > 0 { result.CPULoad = summarizeCPULoad(cpuSamples) if result.CPULoad != nil && result.CPULoad.Status != "ok" { logFunc(fmt.Sprintf("host CPU load during benchmark: avg=%.1f%% max=%.1f%% status=%s", result.CPULoad.AvgPct, result.CPULoad.MaxPct, result.CPULoad.Status)) } } // Compute server power characterization from accumulated IPMI samples. var gpuReportedSumW float64 for _, gpu := range result.GPUs { gpuReportedSumW += gpu.Steady.AvgPowerW } var serverLoadedW float64 if serverLoadedSamples > 0 { serverLoadedW = serverLoadedWSum / float64(serverLoadedSamples) } result.ServerPower = characterizeServerPower(serverIdleW, serverLoadedW, gpuReportedSumW, opts.ServerPowerSource, serverIdleOK && serverLoadedOK) result.Cooling = summarizeBenchmarkCooling(metricRows) // Apply server-power penalty when IPMI reports the server delta is much // lower than GPU-reported sum: GPU power telemetry is over-stated, making // CalibratedPeakPowerW and PowerSustainScore unreliable. // Penalty factor scales from 1.0 (ratio ≥ 0.75, no penalty) down to 0. if sp := result.ServerPower; sp != nil && sp.Available && sp.ReportingRatio > 0 && sp.ReportingRatio < 0.75 { factor := sp.ReportingRatio / 0.75 for i := range result.GPUs { result.GPUs[i].Scores.CompositeScore *= factor result.GPUs[i].Notes = append(result.GPUs[i].Notes, fmt.Sprintf("server-power penalty applied (reporting_ratio=%.2f < 0.75): composite score reduced to %.1f%%", sp.ReportingRatio, factor*100)) } } result.Findings = buildBenchmarkFindings(result) result.OverallStatus = benchmarkOverallStatus(result) result.PSUIssues = diffPSUStatus(psuBefore, psuStatusSnapshot()) writeBenchmarkMetricsFiles(runDir, metricRows) resultJSON, err := json.MarshalIndent(result, "", " ") if err != nil { return "", fmt.Errorf("marshal benchmark result: %w", err) } if err := os.WriteFile(filepath.Join(runDir, "result.json"), resultJSON, 0644); err != nil { return "", fmt.Errorf("write result.json: %w", err) } report := renderBenchmarkReportWithCharts(result) if err := os.WriteFile(filepath.Join(runDir, "report.md"), []byte(report), 0644); err != nil { return "", fmt.Errorf("write report.md: %w", err) } summary := renderBenchmarkSummary(result) if err := os.WriteFile(filepath.Join(runDir, "summary.txt"), []byte(summary), 0644); err != nil { return "", fmt.Errorf("write summary.txt: %w", err) } return runDir, nil }