<__msvc_int128.hpp>: optimize _Signed128::operator* with a branchless multiply - #6406
<__msvc_int128.hpp>: optimize _Signed128::operator* with a branchless multiply#6406KKoishi_ (Koishi-Satori) wants to merge 4 commits into
<__msvc_int128.hpp>: optimize _Signed128::operator* with a branchless multiply#6406Conversation
…ly without sign-normalizes
|
Azure Pipelines: There may be pipelines that require an authorized user to comment /azp run to run. |
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
Optimizes _Signed128 multiplication by avoiding sign normalization and adds a microbenchmark to compare new vs. old behavior/performance.
Changes:
- Replaced
_Signed128::operator*implementation with a direct low-128-bit product using_UMul128+ cross terms. - Added a new Google Benchmark program to compare the new multiplication vs. the previous implementation.
- Registered the new benchmark target in the benchmarks CMake list.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 5 comments.
| File | Description |
|---|---|
| stl/inc/__msvc_int128.hpp | Reworks _Signed128 multiplication implementation to compute the low 128 bits directly. |
| benchmarks/src/signed128_mul.cpp | Adds a benchmark comparing new vs. old signed-128 multiplication. |
| benchmarks/CMakeLists.txt | Adds the new signed128_mul benchmark target. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
|
@microsoft-github-policy-service agree |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 3 out of 3 changed files in this pull request and generated no new comments.
Suppressed comments (1)
stl/inc/__msvc_int128.hpp:1377
- Optional: this reimplements
_Base128::_Multiplyverbatim (defined at lines 422–427), creating a second multiplication core that can drift when the shared implementation changes. The unsigned operator already delegates to that helper at lines 1025–1026; returning its result here removes the sign-normalization branches while preserving a single implementation.
_Signed128 _Result;
_Result._Word[0] = _UMul128(_Left._Word[0], _Right._Word[0], _Result._Word[1]);
_Result._Word[1] += _Left._Word[1] * _Right._Word[0];
_Result._Word[1] += _Left._Word[0] * _Right._Word[1];
Co-authored-by: statementreply <statementreply@gmail.com>
Alex Guteniev (AlexGuteniev)
left a comment
There was a problem hiding this comment.
Please provide your benchmark numbers.
See Benchmarking the STL on the Wiki or follow other PR precedents.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
I've added benchmark results in PR description. |
|
/azp run STL-CI |
|
Azure Pipelines: Successfully started running 1 pipeline(s). |
Resolves issue #6405.
std::_Signed128::operator*in<__msvc_int128.hpp>perform sign-normalizes before multiplying, but it is unnecessary and will cause performance issue.The low 128 bits of a two's-complement product equal the unsigned product, so the sign handling is unnecessary. I replaced it with the same computation
_Base128::_Multiplyalready performs.I've tried to implement a version without sign handling, the new version has same behavior and will not break any ABI. The new version without sign-normalizing is more faster than current version in
<__msvc_int128.hpp>.This PR also adds a signed128_mul benchmark comparing the new implementation against a faithful replica of the old one.
Benchmark
On MSVC x64, Release (
/O2), measured on a GitHub Actions hosted runner (shared 4-vCPU VM; absolute numbers vary between runs):bm_signed128_mul(independent multiplies)bm_signed128_mul_horner(5 dependent multiplies)The first case measures multiplication throughput. The second evaluates a degree-5 polynomial with a Horner chain, where every multiply depends on the previous result; this is the same computation shape used by fixed-point math functions such as
log2andsin. Removing the sign normalization shortens the serial dependency chain of each multiply (and eliminates its branches), so latency-bound code benefits far more than the throughput microbenchmarksuggests.