Realisation v1
This commit is contained in:
@@ -0,0 +1,186 @@
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$InputDirectory,
|
||||
|
||||
[string]$OutputFile = "",
|
||||
|
||||
[string]$Pattern = "*-compare.csv",
|
||||
|
||||
[string]$CellMedianFile = ""
|
||||
)
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$root = (Resolve-Path -LiteralPath $InputDirectory).Path
|
||||
if (-not $OutputFile) {
|
||||
$OutputFile = Join-Path $root 'aggregate.csv'
|
||||
}
|
||||
if (-not $CellMedianFile) {
|
||||
$CellMedianFile = Join-Path $root 'cell-medians.csv'
|
||||
}
|
||||
|
||||
$files = @(Get-ChildItem -LiteralPath $root -Filter $Pattern -File)
|
||||
if ($files.Count -eq 0) {
|
||||
throw "No $Pattern files found in $root"
|
||||
}
|
||||
|
||||
$rows = @($files | ForEach-Object { Import-Csv -LiteralPath $_.FullName }) |
|
||||
Where-Object { $_.status -eq 'ok' }
|
||||
|
||||
function Get-Median([double[]]$Values) {
|
||||
if ($Values.Count -eq 0) { return [double]::NaN }
|
||||
$ordered = @($Values | Sort-Object)
|
||||
$middle = [int]($ordered.Count / 2)
|
||||
if (($ordered.Count % 2) -eq 0) {
|
||||
return ($ordered[$middle - 1] + $ordered[$middle]) / 2.0
|
||||
}
|
||||
return $ordered[$middle]
|
||||
}
|
||||
|
||||
function Get-GeometricMean([double[]]$Values) {
|
||||
$positive = @($Values | Where-Object { $_ -gt 0 -and -not [double]::IsNaN($_) })
|
||||
if ($positive.Count -eq 0) { return [double]::NaN }
|
||||
$logSum = 0.0
|
||||
foreach ($value in $positive) { $logSum += [math]::Log($value) }
|
||||
return [math]::Exp($logSum / $positive.Count)
|
||||
}
|
||||
|
||||
function Get-Mode($Values) {
|
||||
$groups = @($Values | Group-Object | Sort-Object Count -Descending)
|
||||
if ($groups.Count -eq 0) { return '' }
|
||||
return $groups[0].Name
|
||||
}
|
||||
|
||||
# Every implementation must produce the same observable result for an
|
||||
# identical trace. A mismatch invalidates the performance run.
|
||||
$mismatches = @($rows |
|
||||
Group-Object profile,type,workload,n,repeat,seed |
|
||||
Where-Object { @($_.Group.checksum | Select-Object -Unique).Count -ne 1 })
|
||||
if ($mismatches.Count -ne 0) {
|
||||
throw "Checksum mismatch in $($mismatches.Count) benchmark cell(s)"
|
||||
}
|
||||
|
||||
$medians = @($rows |
|
||||
Group-Object profile,type,structure,workload,n,leaf,fanout,levels |
|
||||
ForEach-Object {
|
||||
$group = $_.Group
|
||||
$candidate = $group[0].structure
|
||||
if ($candidate -like 'adaptive_*' -or $candidate -like 'tiered_forced_*') {
|
||||
$candidate += "@initial_$($group[0].leaf)_$($group[0].fanout)_$($group[0].levels)"
|
||||
}
|
||||
[pscustomobject]@{
|
||||
profile = $group[0].profile
|
||||
type = $group[0].type
|
||||
structure = $candidate
|
||||
workload = $group[0].workload
|
||||
n = [uint64]$group[0].n
|
||||
leaf = [uint64]$group[0].leaf
|
||||
fanout = [uint64]$group[0].fanout
|
||||
levels = [uint64]$group[0].levels
|
||||
final_leaf = Get-Mode @($group.final_leaf)
|
||||
final_fanout = Get-Mode @($group.final_fanout)
|
||||
final_levels = Get-Mode @($group.final_levels)
|
||||
final_mode = Get-Mode @($group.final_mode)
|
||||
median_ns = Get-Median @($group.total_ns | ForEach-Object { [double]$_ })
|
||||
median_ns_per_op = Get-Median @($group.ns_per_op | ForEach-Object { [double]$_ })
|
||||
median_bytes = Get-Median @($group.allocated_bytes | ForEach-Object { [double]$_ })
|
||||
median_p99_ns = Get-Median @($group.p99_ns | ForEach-Object { [double]$_ })
|
||||
median_max_maintenance_ns = Get-Median @($group.max_maintenance_ns | ForEach-Object { [double]$_ })
|
||||
median_max_transition_ns = Get-Median @($group.max_transition_ns | ForEach-Object { [double]$_ })
|
||||
median_switches = Get-Median @($group.switches | ForEach-Object { [double]$_ })
|
||||
median_shape_rebuilds = Get-Median @($group.shape_rebuilds | ForEach-Object { [double]$_ })
|
||||
median_leaf_rebuilds = Get-Median @($group.leaf_rebuilds | ForEach-Object { [double]$_ })
|
||||
median_directory_rebuilds = Get-Median @($group.directory_rebuilds | ForEach-Object { [double]$_ })
|
||||
median_policy_evaluations = Get-Median @($group.policy_evaluations | ForEach-Object { [double]$_ })
|
||||
median_last_forecast_operations = Get-Median @($group.last_forecast_operations | ForEach-Object { [double]$_ })
|
||||
median_last_evidence_windows = Get-Median @($group.last_evidence_windows | ForEach-Object { [double]$_ })
|
||||
median_last_vector_cost = Get-Median @($group.last_vector_cost | ForEach-Object { [double]$_ })
|
||||
median_last_tiered_cost = Get-Median @($group.last_tiered_cost | ForEach-Object { [double]$_ })
|
||||
median_last_current_cost = Get-Median @($group.last_current_cost | ForEach-Object { [double]$_ })
|
||||
median_last_conversion_cost = Get-Median @($group.last_conversion_cost | ForEach-Object { [double]$_ })
|
||||
median_last_expected_saving = Get-Median @($group.last_expected_saving | ForEach-Object { [double]$_ })
|
||||
median_last_local_edit_fraction = Get-Median @($group.last_local_edit_fraction | ForEach-Object { [double]$_ })
|
||||
median_last_edit_fraction = Get-Median @($group.last_edit_fraction | ForEach-Object { [double]$_ })
|
||||
}
|
||||
})
|
||||
|
||||
$medians | Sort-Object profile,type,workload,n,structure |
|
||||
Export-Csv -LiteralPath $CellMedianFile -NoTypeInformation -Encoding utf8
|
||||
|
||||
$fixedNames = @('std::vector_reserved', 'std::deque',
|
||||
'std::list_index_semantics')
|
||||
$bestFixed = @{}
|
||||
$medians | Group-Object profile,type,workload,n | ForEach-Object {
|
||||
$eligible = @($_.Group | Where-Object {
|
||||
$fixedNames -contains $_.structure -or $_.structure -like 'tiered_forced_leaf_*'
|
||||
})
|
||||
if ($eligible.Count -gt 0) {
|
||||
$key = "$($_.Group[0].profile)|$($_.Group[0].type)|$($_.Group[0].workload)|$($_.Group[0].n)"
|
||||
$bestFixed[$key] = ($eligible.median_ns | Measure-Object -Minimum).Minimum
|
||||
}
|
||||
}
|
||||
|
||||
$cellScores = @($medians | ForEach-Object {
|
||||
$key = "$($_.profile)|$($_.type)|$($_.workload)|$($_.n)"
|
||||
if ($bestFixed.ContainsKey($key) -and $_.median_ns -gt 0) {
|
||||
[pscustomobject]@{
|
||||
profile = $_.profile
|
||||
structure = $_.structure
|
||||
type = $_.type
|
||||
workload = $_.workload
|
||||
n = $_.n
|
||||
score = [double]$bestFixed[$key] / [double]$_.median_ns
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
# Hierarchical aggregation prevents a family with more sizes from receiving a
|
||||
# larger implicit weight: sizes -> workloads -> types -> compiler profiles.
|
||||
$byWorkload = @($cellScores | Group-Object profile,structure,type,workload |
|
||||
ForEach-Object {
|
||||
$g = $_.Group
|
||||
[pscustomobject]@{
|
||||
profile = $g[0].profile; structure = $g[0].structure
|
||||
type = $g[0].type; workload = $g[0].workload
|
||||
score = Get-GeometricMean @($g.score)
|
||||
}
|
||||
})
|
||||
$byType = @($byWorkload | Group-Object profile,structure,type |
|
||||
ForEach-Object {
|
||||
$g = $_.Group
|
||||
[pscustomobject]@{
|
||||
profile = $g[0].profile; structure = $g[0].structure
|
||||
type = $g[0].type; score = Get-GeometricMean @($g.score)
|
||||
}
|
||||
})
|
||||
$byProfile = @($byType | Group-Object profile,structure |
|
||||
ForEach-Object {
|
||||
$g = $_.Group
|
||||
[pscustomobject]@{
|
||||
profile = $g[0].profile
|
||||
structure = $g[0].structure
|
||||
score = Get-GeometricMean @($g.score)
|
||||
types = $g.Count
|
||||
cells = @($cellScores | Where-Object {
|
||||
$_.profile -eq $g[0].profile -and $_.structure -eq $g[0].structure
|
||||
}).Count
|
||||
}
|
||||
})
|
||||
$overall = @($byProfile | Group-Object structure | ForEach-Object {
|
||||
$g = $_.Group
|
||||
[pscustomobject]@{
|
||||
profile = 'ALL'
|
||||
structure = $g[0].structure
|
||||
score = Get-GeometricMean @($g.score)
|
||||
types = ($g.types | Measure-Object -Maximum).Maximum
|
||||
cells = ($g.cells | Measure-Object -Sum).Sum
|
||||
}
|
||||
})
|
||||
|
||||
@($byProfile + $overall) | Sort-Object profile,@{Expression='score';Descending=$true} |
|
||||
Export-Csv -LiteralPath $OutputFile -NoTypeInformation -Encoding utf8
|
||||
|
||||
Write-Host "Validated $($rows.Count) raw rows from $($files.Count) profile file(s)."
|
||||
Write-Host "Cell medians: $CellMedianFile"
|
||||
Write-Host "Aggregate: $OutputFile"
|
||||
@($overall | Sort-Object score -Descending) |
|
||||
Format-Table structure,score,types,cells -AutoSize
|
||||
@@ -0,0 +1,127 @@
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[ValidateSet('scalar', 'baseline', 'avx2')]
|
||||
[string]$Profile
|
||||
)
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$build = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot "..\out\build\msvc-$Profile"))
|
||||
$targets = @('uc_demo', 'uc_tests', 'uc_bench')
|
||||
|
||||
function Require-Flag(
|
||||
[string]$Command,
|
||||
[string]$Pattern,
|
||||
[string]$Description,
|
||||
[string]$Context
|
||||
) {
|
||||
if ($Command -notmatch $Pattern) {
|
||||
throw "${Profile}/${Context}: missing $Description"
|
||||
}
|
||||
}
|
||||
|
||||
function Reject-Flag(
|
||||
[string]$Command,
|
||||
[string]$Pattern,
|
||||
[string]$Description,
|
||||
[string]$Context
|
||||
) {
|
||||
if ($Command -match $Pattern) {
|
||||
throw "${Profile}/${Context}: unexpected $Description"
|
||||
}
|
||||
}
|
||||
|
||||
function Assert-ProfileCommand(
|
||||
[string]$Command,
|
||||
[string]$Context
|
||||
) {
|
||||
$optimizationFlags = @([regex]::Matches(
|
||||
$Command,
|
||||
'(?i)(?:^|\s)/(?:O1|O2|Od|Ox)(?=\s|$)'
|
||||
) | ForEach-Object { $_.Value.Trim() })
|
||||
if ($optimizationFlags.Count -ne 1 -or $optimizationFlags[0] -ine '/O2') {
|
||||
$actual = if ($optimizationFlags.Count -eq 0) { 'none' } else { $optimizationFlags -join ', ' }
|
||||
throw "${Profile}/${Context}: expected exactly /O2, found $actual"
|
||||
}
|
||||
|
||||
$runtimeFlags = @([regex]::Matches(
|
||||
$Command,
|
||||
'(?i)(?:^|\s)/(?:MDd?|MTd?)(?=\s|$)'
|
||||
) | ForEach-Object { $_.Value.Trim() })
|
||||
if ($runtimeFlags.Count -ne 1 -or $runtimeFlags[0] -ine '/MT') {
|
||||
$actual = if ($runtimeFlags.Count -eq 0) { 'none' } else { $runtimeFlags -join ', ' }
|
||||
throw "${Profile}/${Context}: expected exactly /MT, found $actual"
|
||||
}
|
||||
|
||||
Reject-Flag $Command '(?i)(?:^|\s)/GL(?=\s|$)' '/GL (IPO/LTO must be off)' $Context
|
||||
|
||||
switch ($Profile) {
|
||||
'scalar' {
|
||||
Require-Flag $Command '(?i)(?:^|\s)/d2Qvec-(?=\s|$)' '/d2Qvec-' $Context
|
||||
Require-Flag $Command '(?i)(?:^|\s)/Qvec-report:1(?=\s|$)' '/Qvec-report:1' $Context
|
||||
Require-Flag $Command '(?i)(?:^|\s)/Oi-(?=\s|$)' '/Oi-' $Context
|
||||
Require-Flag $Command '(?i)UC_SIMD_PROFILE_SCALAR=1' 'UC_SIMD_PROFILE_SCALAR=1' $Context
|
||||
Require-Flag $Command '(?i)_USE_STD_VECTOR_ALGORITHMS=0' '_USE_STD_VECTOR_ALGORITHMS=0' $Context
|
||||
Reject-Flag $Command '(?i)(?:^|\s)/arch:AVX\S*' '/arch:AVX*' $Context
|
||||
Reject-Flag $Command '(?i)UC_SIMD_PROFILE_(?:BASELINE|AVX2)=1' 'a foreign SIMD profile definition' $Context
|
||||
}
|
||||
'baseline' {
|
||||
Require-Flag $Command '(?i)UC_SIMD_PROFILE_BASELINE=1' 'UC_SIMD_PROFILE_BASELINE=1' $Context
|
||||
Reject-Flag $Command '(?i)(?:^|\s)/(?:d2Qvec-|Qvec-)(?=\s|$)' 'vectorizer-disable flag' $Context
|
||||
Reject-Flag $Command '(?i)(?:^|\s)/Oi-(?=\s|$)' '/Oi-' $Context
|
||||
Reject-Flag $Command '(?i)(?:^|\s)/arch:AVX\S*' '/arch:AVX*' $Context
|
||||
Reject-Flag $Command '(?i)_USE_STD_VECTOR_ALGORITHMS=0|UC_SIMD_PROFILE_(?:SCALAR|AVX2)=1' 'a foreign SIMD profile definition' $Context
|
||||
}
|
||||
'avx2' {
|
||||
Require-Flag $Command '(?i)UC_SIMD_PROFILE_AVX2=1' 'UC_SIMD_PROFILE_AVX2=1' $Context
|
||||
Reject-Flag $Command '(?i)(?:^|\s)/(?:d2Qvec-|Qvec-)(?=\s|$)' 'vectorizer-disable flag' $Context
|
||||
Reject-Flag $Command '(?i)(?:^|\s)/Oi-(?=\s|$)' '/Oi-' $Context
|
||||
Reject-Flag $Command '(?i)_USE_STD_VECTOR_ALGORITHMS=0|UC_SIMD_PROFILE_(?:SCALAR|BASELINE)=1' 'a foreign SIMD profile definition' $Context
|
||||
|
||||
$architectureFlags = @([regex]::Matches(
|
||||
$Command,
|
||||
'(?i)(?:^|\s)/arch:\S+'
|
||||
) | ForEach-Object { $_.Value.Trim() })
|
||||
if ($architectureFlags.Count -ne 1 -or $architectureFlags[0] -ine '/arch:AVX2') {
|
||||
$actual = if ($architectureFlags.Count -eq 0) {
|
||||
'none'
|
||||
} else {
|
||||
$architectureFlags -join ', '
|
||||
}
|
||||
throw "${Profile}/${Context}: expected exactly /arch:AVX2, found $actual"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$validatedCommands = 0
|
||||
foreach ($target in $targets) {
|
||||
$releaseDirectory = Join-Path $build "$target.dir\Release"
|
||||
if (-not (Test-Path -LiteralPath $releaseDirectory -PathType Container)) {
|
||||
throw "${Profile}/${target}: missing Release build directory $releaseDirectory"
|
||||
}
|
||||
|
||||
$logs = @(Get-ChildItem -LiteralPath $releaseDirectory -Recurse `
|
||||
-Filter 'CL.command.1.tlog' -File)
|
||||
if ($logs.Count -ne 1) {
|
||||
throw "${Profile}/${target}: expected one compiler command log, found $($logs.Count)"
|
||||
}
|
||||
|
||||
# MSBuild writes command tlogs as UTF-16LE without a reliable BOM.
|
||||
$commandLines = @(Get-Content -LiteralPath $logs[0].FullName -Encoding Unicode |
|
||||
Where-Object { $_ -and -not $_.StartsWith('^') })
|
||||
if ($commandLines.Count -eq 0) {
|
||||
throw "${Profile}/${target}: compiler command log contains no commands"
|
||||
}
|
||||
|
||||
for ($index = 0; $index -lt $commandLines.Count; ++$index) {
|
||||
$context = if ($commandLines.Count -eq 1) {
|
||||
$target
|
||||
} else {
|
||||
"$target command $($index + 1)"
|
||||
}
|
||||
Assert-ProfileCommand $commandLines[$index] $context
|
||||
++$validatedCommands
|
||||
}
|
||||
}
|
||||
|
||||
Write-Host "Build flag audit passed for ${Profile}: $validatedCommands Release compiler command(s) across $($targets.Count) targets."
|
||||
@@ -0,0 +1,15 @@
|
||||
$ErrorActionPreference = 'Stop'
|
||||
Add-Type -TypeDefinition @'
|
||||
using System.Runtime.InteropServices;
|
||||
public static class ProcessorFeatures {
|
||||
[DllImport("kernel32.dll")]
|
||||
public static extern bool IsProcessorFeaturePresent(uint feature);
|
||||
}
|
||||
'@
|
||||
|
||||
# PF_AVX2_INSTRUCTIONS_AVAILABLE from processthreadsapi.h / Windows SDK.
|
||||
if (-not [ProcessorFeatures]::IsProcessorFeaturePresent(40)) {
|
||||
Write-Warning 'AVX2 is not available; the AVX2 benchmark profile will be skipped.'
|
||||
exit 1
|
||||
}
|
||||
Write-Host 'AVX2 is available.'
|
||||
@@ -0,0 +1,140 @@
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$CompareCellMedianFile,
|
||||
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$AdaptCellMedianFile
|
||||
)
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
function Convert-ToDouble([string]$Value, [string]$Column, [string]$Path) {
|
||||
$number = 0.0
|
||||
$style = [Globalization.NumberStyles]::Float
|
||||
if ([double]::TryParse($Value, $style,
|
||||
[Globalization.CultureInfo]::InvariantCulture, [ref]$number)) {
|
||||
return $number
|
||||
}
|
||||
if ([double]::TryParse($Value, $style,
|
||||
[Globalization.CultureInfo]::CurrentCulture, [ref]$number)) {
|
||||
return $number
|
||||
}
|
||||
throw "Cannot parse '$Value' as $Column in $Path"
|
||||
}
|
||||
|
||||
function Test-SelectedDeferred([string]$Structure) {
|
||||
$base = ($Structure -split '@', 2)[0]
|
||||
return $base -eq 'adaptive_shape_deferred' `
|
||||
-or $base -eq 'adaptive_shape_default_deferred' `
|
||||
-or $base -eq 'adaptive_shape_moderate_deferred'
|
||||
}
|
||||
|
||||
function Test-CellFile([string]$Path, [string]$Suite) {
|
||||
$resolved = (Resolve-Path -LiteralPath $Path).Path
|
||||
$rows = @(Import-Csv -LiteralPath $resolved | Where-Object {
|
||||
Test-SelectedDeferred $_.structure
|
||||
})
|
||||
if ($rows.Count -eq 0) {
|
||||
throw "No selected deferred/default/moderate adaptive rows in $resolved"
|
||||
}
|
||||
$requiredWorkloads = if ($Suite -eq 'adapt') {
|
||||
@('short_burst_negative_control', 'phase_10_uniform_10_local_80_read')
|
||||
} else {
|
||||
@('bursty_edit', 'phase_uniform_local_read')
|
||||
}
|
||||
foreach ($workload in $requiredWorkloads) {
|
||||
if (-not @($rows | Where-Object { $_.workload -eq $workload }).Count) {
|
||||
throw "Missing selected $Suite workload '$workload' in $resolved"
|
||||
}
|
||||
}
|
||||
|
||||
$failures = @()
|
||||
foreach ($row in $rows) {
|
||||
$switches = Convert-ToDouble $row.median_switches 'median_switches' $resolved
|
||||
$leafRebuilds = Convert-ToDouble `
|
||||
$row.median_leaf_rebuilds 'median_leaf_rebuilds' $resolved
|
||||
$maxSwitches = 1.0
|
||||
$maxLeafRebuilds = 1.0
|
||||
$requireVector = $false
|
||||
$class = 'stationary'
|
||||
|
||||
if ($row.workload -eq 'short_burst_negative_control') {
|
||||
$maxSwitches = 0.0
|
||||
$maxLeafRebuilds = 0.0
|
||||
$requireVector = $true
|
||||
$class = 'short-negative'
|
||||
} elseif ($Suite -eq 'adapt' `
|
||||
-and $row.workload -eq 'phase_10_uniform_10_local_80_read') {
|
||||
$maxSwitches = 2.0
|
||||
# The trace deliberately contains two different edit phases. One
|
||||
# stable shape choice per phase is useful adaptation, not churn.
|
||||
$maxLeafRebuilds = 2.0
|
||||
# Returning to vector is optional: for wide/non-trivial values the
|
||||
# measured O(N) conversion can cost more than the remaining read
|
||||
# phase. The policy must avoid churn, not force an uneconomic
|
||||
# second transition.
|
||||
$class = 'adapt-exact-phase'
|
||||
} elseif ($Suite -eq 'compare' `
|
||||
-and $row.workload -eq 'phase_uniform_local_read') {
|
||||
$maxSwitches = 2.0
|
||||
$maxLeafRebuilds = 2.0
|
||||
# The shorter compare phase, especially at smoke scale, may not
|
||||
# provide enough read evidence to amortize a return to vector.
|
||||
$class = 'compare-phase'
|
||||
} elseif ($Suite -eq 'compare' -and $row.workload -eq 'bursty_edit') {
|
||||
$maxSwitches = 2.0
|
||||
$maxLeafRebuilds = 1.0
|
||||
# As above, final tiered is valid when return conversion does not
|
||||
# amortize within the observed read tail.
|
||||
$class = 'bursty'
|
||||
}
|
||||
|
||||
$modeFailure = $requireVector -and $row.final_mode -ne 'vector'
|
||||
if ($switches -gt $maxSwitches `
|
||||
-or $leafRebuilds -gt $maxLeafRebuilds `
|
||||
-or $modeFailure) {
|
||||
$failures += [pscustomobject]@{
|
||||
suite = $Suite
|
||||
class = $class
|
||||
profile = $row.profile
|
||||
type = $row.type
|
||||
workload = $row.workload
|
||||
n = $row.n
|
||||
structure = $row.structure
|
||||
switches = $switches
|
||||
switch_limit = $maxSwitches
|
||||
leaf_rebuilds = $leafRebuilds
|
||||
leaf_rebuild_limit = $maxLeafRebuilds
|
||||
final_mode = $row.final_mode
|
||||
required_mode = if ($requireVector) { 'vector' } else { '*' }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[pscustomobject]@{
|
||||
path = $resolved
|
||||
checked = $rows.Count
|
||||
failures = $failures
|
||||
}
|
||||
}
|
||||
|
||||
$compare = Test-CellFile $CompareCellMedianFile 'compare'
|
||||
$adapt = Test-CellFile $AdaptCellMedianFile 'adapt'
|
||||
$failures = @($compare.failures) + @($adapt.failures)
|
||||
|
||||
if ($failures.Count -ne 0) {
|
||||
foreach ($failure in $failures) {
|
||||
$message = ("FAIL {0}/{1}: {2} {3}/{4}/N={5}; " +
|
||||
"switches={6} (limit {7}), leaf_rebuilds={8} (limit {9}), " +
|
||||
"final_mode={10} (required {11})") -f
|
||||
$failure.suite, $failure.class, $failure.structure,
|
||||
$failure.type, $failure.workload, $failure.n,
|
||||
$failure.switches, $failure.switch_limit,
|
||||
$failure.leaf_rebuilds, $failure.leaf_rebuild_limit,
|
||||
$failure.final_mode, $failure.required_mode
|
||||
Write-Host $message
|
||||
}
|
||||
throw "Adaptation validation failed in $($failures.Count) selected cell(s)"
|
||||
}
|
||||
|
||||
Write-Host "Adaptation validation passed: $($compare.checked) compare and $($adapt.checked) adapt cells."
|
||||
@@ -0,0 +1,93 @@
|
||||
param(
|
||||
[Parameter(Mandatory = $true)] [string]$ResultDirectory,
|
||||
[Parameter(Mandatory = $true)] [string]$RunId,
|
||||
[Parameter(Mandatory = $true)] [string]$Scale,
|
||||
[Parameter(Mandatory = $true)] [string]$Profiles
|
||||
)
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$result = [IO.Path]::GetFullPath((Join-Path (Get-Location) $ResultDirectory))
|
||||
New-Item -ItemType Directory -Force -Path $result | Out-Null
|
||||
$cpu = (Get-ItemProperty 'HKLM:\HARDWARE\DESCRIPTION\System\CentralProcessor\0').ProcessorNameString
|
||||
$windows = Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion'
|
||||
$compilerFile = Get-ChildItem 'out\build\msvc-baseline\CMakeFiles' -Recurse `
|
||||
-Filter 'CMakeCXXCompiler.cmake' -File | Select-Object -First 1
|
||||
$compilerText = if ($compilerFile) { Get-Content -Raw $compilerFile.FullName } else { '' }
|
||||
$compilerVersion = if ($compilerText -match 'CMAKE_CXX_COMPILER_VERSION "([^"]+)"') {
|
||||
$Matches[1]
|
||||
} else { 'unknown' }
|
||||
$cache = Get-Content -Raw 'out\build\msvc-baseline\CMakeCache.txt'
|
||||
$cmakePath = if ($cache -match '(?m)^CMAKE_COMMAND:INTERNAL=(.+)$') { $Matches[1].Trim() } else { '' }
|
||||
$cmakeVersion = if ($cmakePath -and (Test-Path -LiteralPath $cmakePath)) {
|
||||
(& $cmakePath --version | Select-Object -First 1)
|
||||
} else { 'unknown' }
|
||||
$gitHead = try { (git rev-parse --verify HEAD 2>$null) } catch { '' }
|
||||
$gitDirty = try { @((git status --porcelain 2>$null)).Count } catch { -1 }
|
||||
$projectRoot = (Get-Location).Path
|
||||
$sourceManifestName = 'source-manifest.sha256'
|
||||
$binaryManifestName = 'binary-manifest.sha256'
|
||||
|
||||
$metadata = [ordered]@{
|
||||
run_id = $RunId
|
||||
timestamp = (Get-Date).ToString('o')
|
||||
scale = $Scale
|
||||
profiles = @($Profiles -split ' ' | Where-Object { $_ })
|
||||
cpu = $cpu.Trim()
|
||||
logical_processors = [Environment]::ProcessorCount
|
||||
windows = "$($windows.ProductName) $($windows.DisplayVersion) build $($windows.CurrentBuildNumber)"
|
||||
power_plan = ((powercfg /getactivescheme) -join ' ').Trim()
|
||||
compiler = "MSVC $compilerVersion x64"
|
||||
cmake = $cmakeVersion
|
||||
release_runtime = '/MT static'
|
||||
ipo_lto = 'off'
|
||||
scalar_flags = '/O2 /d2Qvec- /Qvec-report:1 /Oi-, no /arch:AVX*'
|
||||
baseline_flags = '/O2, no /Qvec disable, no /arch:AVX*'
|
||||
avx2_flags = '/O2 /arch:AVX2'
|
||||
git_head = $gitHead
|
||||
git_dirty_entries = $gitDirty
|
||||
source_manifest = $sourceManifestName
|
||||
binary_manifest = $binaryManifestName
|
||||
}
|
||||
|
||||
$metadata | ConvertTo-Json -Depth 4 | Set-Content -LiteralPath (Join-Path $result 'environment.json') -Encoding utf8
|
||||
$metadata.GetEnumerator() | ForEach-Object {
|
||||
$value = if ($_.Value -is [array]) { $_.Value -join ',' } else { $_.Value }
|
||||
"$($_.Key)=$value"
|
||||
} | Set-Content -LiteralPath (Join-Path $result 'environment.txt') -Encoding utf8
|
||||
|
||||
# A manifest makes an uncommitted experimental run identifiable even before the
|
||||
# repository has its first HEAD. Results/out are intentionally excluded from
|
||||
# the source set; generated executables receive their own manifest.
|
||||
$sourceRoots = @(
|
||||
'.gitignore', 'CMakeLists.txt', 'CMakePresets.json', 'README.md',
|
||||
'build.bat', 'benchmark.bat', 'include', 'src', 'tests', 'tools', 'docs'
|
||||
)
|
||||
$sourceFiles = @()
|
||||
foreach ($entry in $sourceRoots) {
|
||||
if (Test-Path -LiteralPath $entry -PathType Leaf) {
|
||||
$sourceFiles += Get-Item -LiteralPath $entry
|
||||
} elseif (Test-Path -LiteralPath $entry -PathType Container) {
|
||||
$sourceFiles += Get-ChildItem -LiteralPath $entry -Recurse -File
|
||||
}
|
||||
}
|
||||
$sourceLines = @($sourceFiles | Sort-Object FullName -Unique | ForEach-Object {
|
||||
$relative = $_.FullName.Substring($projectRoot.Length).TrimStart('\') -replace '\\', '/'
|
||||
$hash = (Get-FileHash -LiteralPath $_.FullName -Algorithm SHA256).Hash.ToLowerInvariant()
|
||||
"$hash $relative"
|
||||
})
|
||||
$sourceLines | Set-Content -LiteralPath (Join-Path $result $sourceManifestName) -Encoding ascii
|
||||
|
||||
$binaryFiles = @()
|
||||
foreach ($profile in @($Profiles -split ' ' | Where-Object { $_ })) {
|
||||
$profileDirectory = Join-Path $projectRoot "out\bin\$profile\Release"
|
||||
if (Test-Path -LiteralPath $profileDirectory) {
|
||||
$binaryFiles += Get-ChildItem -LiteralPath $profileDirectory -File
|
||||
}
|
||||
}
|
||||
$binaryLines = @($binaryFiles | Sort-Object FullName -Unique | ForEach-Object {
|
||||
$relative = $_.FullName.Substring($projectRoot.Length).TrimStart('\') -replace '\\', '/'
|
||||
$hash = (Get-FileHash -LiteralPath $_.FullName -Algorithm SHA256).Hash.ToLowerInvariant()
|
||||
"$hash $relative"
|
||||
})
|
||||
$binaryLines | Set-Content -LiteralPath (Join-Path $result $binaryManifestName) -Encoding ascii
|
||||
Write-Host "Wrote benchmark environment metadata to $result"
|
||||
Reference in New Issue
Block a user