Repository navigation
PGO build broken on Windows #111786
Copy link
Copy link
Closed
Labels
OS-windowsbuildThe build process and cross-buildThe build process and cross-buildtype-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error
Description
Bug report
Bug description:
Building with --pgo on Windows seems to be broken on main (853b4b5).
My hunch, since this is failing in ceval.c, is that the recent merge of the Tier 1 and Tier 2 interpreters may have made the interpreter loop function too large for MSVC to handle.
Build command:
PCbuild\build.bat --pgo -c Release
Error:
Merging C:\actions-runner\_work\benchmarking\benchmarking\cpython\PCbuild\amd64\python313!1.pgc
C:\actions-runner\_work\benchmarking\benchmarking\cpython\PCbuild\amd64\python313!1.pgc: Used 25.9% (15643384 / 60370944) of total space reserved. 0.0% of the counts were dropped due to overflow.
Reading PGD file 1: C:\actions-runner\_work\benchmarking\benchmarking\cpython\PCbuild\amd64\python313.pgd
Creating library C:\actions-runner\_work\benchmarking\benchmarking\cpython\PCbuild\amd64\python313.lib and object C:\actions-runner\_work\benchmarking\benchmarking\cpython\PCbuild\amd64\python313.exp
Generating code
0 of 0 ( 0.0%) original invalid call sites were matched.
0 new call sites were added.
257 of 14721 ( 1.75%) profiled functions will be compiled for speed, and the rest of the functions will be compiled for size
C:\actions-runner\_work\benchmarking\benchmarking\cpython\Python\ceval.c(669): fatal error C1001: Internal compiler error. [C:\actions-runner\_work\benchmarking\benchmarking\cpython\PCbuild\pythoncore.vcxproj]
(compiler file 'D:\a\_work\1\s\src\vctools\Compiler\Utc\src\p2\main.c', line 224)
To work around this problem, try simplifying or changing the program near the locations listed above.
If possible please provide a repro here: https://developercommunity.visualstudio.com/
Please choose the Technical Support command on the Visual C++
Help menu, or open the Technical Support help file for more information
link!InvokeCompilerPass()+0x10e636
link!InvokeCompilerPass()+0x10e636
link!InvokeCompilerPass()+0x10e373
link!InvokeCompilerPass()+0x10b310
link!InvokeCompilerPass()+0x10b215
link!InvokeCompilerPass()+0x102cea
Build FAILED.
We don't currently have a buildbot that builds with --pgo on Windows -- probably something to fix as well.
CPython versions tested on:
CPython main branch
Operating systems tested on:
Windows
Linked issue
Microsoft Developer Community MSVC bug report
Linked PRs
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
OS-windowsbuildThe build process and cross-buildThe build process and cross-buildtype-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error
Projects
- StatusShow more project fieldsDone
Activity
vstinner commentedon Nov 6, 2023
That sounds like a MSVC bug.
Can you try to report the bug to MSVC support?
zooba commentedon Nov 6, 2023
Yeah, we'll need a link repro for this, but it should be easy enough to get them to diagnose it. Reporting through dev community is still the best option, even if you work there 😉
Hopefully there's a resolution that doesn't require a specific compiler version. Our infrastructure needs a revamp to handle that situation...
mdboom commentedon Nov 6, 2023
My plan is to explore configuration/pragma options to see if anything can be worked around (and benchmark the effect of that so we know what the performance impact of not doing certain optimizations on certain functions is). Barring that we may need to revert or redesign #111428.
Reporting the bug to MSVC also makes sense, and I plan to do that. But I don't think we can count on it to solve our issue -- there will always be a need to compile on a range of versions for a variety of reasons, including what @zooba already said.
mdboom commentedon Nov 6, 2023
Reading the description of error C1001, it's clear this is an error in the optimizer. This lead me to a description of MSVC's optimization pragmas as a way to control the optimization of just this one very large function (rather than spending time on anything more global).
The two main optimization flags are size ("s") and time ("t"). If you disable them both, you create a new problem, which is that the stack frames get larger, and it exceeds the C stack space during some unit tests (
test_picklebeing one of them). If you disable only size, the internal compiler error remains. However, if you just disable time ("t"), it successfully compiles.It would seems that disabling "optimize for speed" would make things slower, however, running on our benchmarking machine, it is about 3% faster (by the HPT method, which is more stable between runs) or 6% faster by the geometric mean method.
So it seems like disabling the "t" optimization for just
_PyEval_EvalFrameDefaulthas no downside.I still plan to report this issue to MSVC. When there is a fix in MSVC, we can add an
#ifdefto remove this workaround for that newer version of MSVC, assuming it helps performance.pythongh-111786: Do pragmas only in MSC block
zooba commentedon Nov 6, 2023
Large functions are often faster when optimised for size, as the instruction cache pressure tends to outweigh anything else.
That said, I'm fairly sure that PGO ignores all of those options and uses its own data, and generally it improves upon the
/Ooptions by 10-20%, if not more. So it may not be a suitable choice.I've also found in the past that many of our optimiser crashes have been due to mishandled types between object file (e.g. a function argument is defined as
intin one butunsigned intin the other). Might be worth looking for anywhere new that kind of thing may have happened - the compiler should never crash, but it's potentially crashing because of an error in our code that we'd want to fix anyway.gvanrossum commentedon Nov 7, 2023
@zooba
How do you reconcile that with @mdboom's actual benchmark results? IIUC the benchmark suite is run with PGO. And I just tried
build.bat --pgowith and without the pragma from Mike's PR, and the compiler error disappears when I add the pragma. So it has some effect.10 remaining items
neonene commentedon Nov 17, 2023
Without this PGO workaround, 3.13's
EXTRA_CASES(version specific) seem to require theopcodevariable to be declared asuint8_t, rather than to be cast. C1001 error can be seen even before mixing tiers at 7e135a4, ifopcodeisinttype.The error went away when I tried making tier1 and tier2 have their own
opcodes (uint8_tandint). An alternative was to replaceEXTRA_CASESwithdefault:, leavingint opcodeas-is. However,#pragma optimize("t", off)was realistically needed to speed up them after all.mdboom commentedon Nov 17, 2023
Thanks, @neonene. I will take some of these ideas, measure them, and file a PR.
mdboom commentedon Nov 20, 2023
I measured this a few different ways and compared
_PyEval_EvalFrameDefaultsep-opcodeuses an uint8_t for Tier 1 opcodes and a separate uint16_t for Tier 2 opcodessep-opcode_pragma_offdoes the above, but also removes the#pragma(since it's no longer needed to prevent the compiler crash)Os-globalsets/Osfor all code during the PGInstrument phase as @neonene suggested as a workaroundSo it seems like the best thing for performance is to separate the opcode variables, leaving everything else the same.
pythongh-111786: Use separate opcode vars for Tier 1 and Tier 2
pythongh-111786: Use separate opcode vars for Tier 1 and Tier 2
pythongh-111786: Use separate opcode vars for Tier 1 and Tier 2
gh-111786: Use separate opcode vars for Tier 1 and Tier 2 (#112289)
zooba commentedon Nov 29, 2023
@mdboom Does your merged fix address this issue entirely? No point adding compatibility infrastructure if we've got a good workaround.
neonene commentedon Dec 4, 2023
It appears that Windows PGO builds in faster-cpython/benchmarking-public repo have slowed down with Tier 2 off, after generated cases were sorted (1619f43).
If the switch-cases cannot be arranged for working around, a similar performance issue closed with low priority in MSVC's bug tracker would be worth checking, where they suggested
ssa-pre-option forclorlink, to recover to some degree.The hidden option worked for me in
pythoncore.vcxprojwith MSVC v.1936 and v.1938, specified at least at PGUpdate phase. Andoptimize("t",off)pragma inceval.cseems to work almost independently.MSVC v.1929 (VS2019) is fine with/without this option.
pythongh-111786: Optimize for space for _PyEval_EvalFrameDefault on M…
pythongh-111786: Use separate opcode vars for Tier 1 and Tier 2 (pyth…
pythongh-111786: Optimize for space for _PyEval_EvalFrameDefault on M…
pythongh-111786: Use separate opcode vars for Tier 1 and Tier 2 (pyth…