mirror of
https://github.com/RetroPortingToolKit/psxrecomp
synced 2026-10-25 18:05:12 -04:00
tools: compute the affinity mask by shifting, not by Pow on a double
[math]::Pow(2, $n) - 1 builds the processor-affinity mask in DOUBLE precision, and a double carries only a 53-bit mantissa. From n = 54 the subtraction is lost to rounding: 2^n - 1 rounds straight back up to 2^n, so the mask comes out with exactly ONE bit set. Measured across the range, old math vs shifting: n = 50..53 both select n CPUs ok n = 54..62 old selects 1 CPU of n SILENTLY WRONG n = 63..64 old throws converting to Int64 visible failure The silent band is by far the worse half, and it is worst precisely in this tool. On a 54-to-62-processor host, -Off would pin the title to a single logical CPU while printing "affinity restored to all N processors", and -Cores N would pin it to one CPU while reporting N. A throttling tool that reports the opposite of what it did does not just fail, it invalidates every measurement taken through it -- which is the exact failure this tool exists to prevent. Get-AffinityMask shifts a uint64 instead, and special-cases a full 64 because .NET masks a 64-bit shift count to 63 (shifting by 64 quietly yields 1, not 0). Verified equivalent to the old expression for every n where the old expression was correct (1..53), correct by popcount for 1..64, and saturating above 64. ProcessorAffinity addresses a single processor group, so a host with more than 64 processors is inherently limited to the group the process already runs in; the mask now saturates there rather than throwing. No behaviour change on any host where the tool previously worked -- this box has 16 processors, where old and new masks are byte-identical (0xFFFF). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
+13
-2
@@ -78,6 +78,17 @@ $proc = Get-Process -Id $ProcessId -ErrorAction SilentlyContinue
|
||||
if (-not $proc) { throw "no process with PID $ProcessId" }
|
||||
$nCpu = [Environment]::ProcessorCount
|
||||
|
||||
function Get-AffinityMask([int]$n) {
|
||||
# [math]::Pow(2,$n)-1 overflows Int64 at $n >= 63 and THROWS, so on a host
|
||||
# with 64 logical processors -Off and -Cores both died instead of doing
|
||||
# anything. Shift instead, and special-case a full 64 because .NET masks a
|
||||
# 64-bit shift count to 63 (shifting by 64 quietly yields 1, not 0).
|
||||
# Note ProcessorAffinity covers a single processor group, so a >64-CPU host
|
||||
# is inherently limited to the group the process is already in.
|
||||
if ($n -ge 64) { return [int64]-1 }
|
||||
return [int64](([uint64]1 -shl $n) - 1)
|
||||
}
|
||||
|
||||
function Measure-Draw([System.Diagnostics.Process]$p, [int]$secs) {
|
||||
$t1 = $p.TotalProcessorTime
|
||||
Start-Sleep -Seconds $secs
|
||||
@@ -139,7 +150,7 @@ if ($Off) {
|
||||
if (-not [JobCpu]::SetInformationJobObject($job, [JobCpu]::InfoClass, [ref]$info, 8)) {
|
||||
throw "SetInformationJobObject(off) failed: $([ComponentModel.Win32Exception]::new([Runtime.InteropServices.Marshal]::GetLastWin32Error()).Message)"
|
||||
}
|
||||
$proc.ProcessorAffinity = [IntPtr]([int64]([math]::Pow(2, $nCpu) - 1))
|
||||
$proc.ProcessorAffinity = [IntPtr](Get-AffinityMask $nCpu)
|
||||
Write-Host "cap : REMOVED (affinity restored to all $nCpu processors)"
|
||||
} elseif ($CoresWorth -gt 0) {
|
||||
# CpuRate is in 1/100 of a percent of TOTAL machine capacity, so a cores'
|
||||
@@ -158,7 +169,7 @@ if ($Off) {
|
||||
|
||||
if ($Cores -gt 0) {
|
||||
if ($Cores -gt $nCpu) { $Cores = $nCpu }
|
||||
$mask = [int64]([math]::Pow(2, $Cores) - 1)
|
||||
$mask = Get-AffinityMask $Cores
|
||||
$proc.ProcessorAffinity = [IntPtr]$mask
|
||||
Write-Host "affinity: $Cores of $nCpu logical processors (mask 0x$('{0:X}' -f $mask))"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user