Realisation v1

This commit is contained in:
Efim Beshmenev
2026-08-11 22:19:34 +03:00
commit aea6e330e3
194 changed files with 62580 additions and 0 deletions
+186
View File
@@ -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
+127
View File
@@ -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."
+15
View File
@@ -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.'
+140
View File
@@ -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."
+93
View File
@@ -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"