From 84f08f9de9b6ad0151098a569b78d1293457585c Mon Sep 17 00:00:00 2001 From: koishi <1580412105@qq.com> Date: Tue, 18 Aug 2026 16:29:04 +0800 Subject: [PATCH 1/4] `<__msvc_int128.hpp>`: optimize `_Signed128::operator*` with a multiply without sign-normalizes --- benchmarks/CMakeLists.txt | 1 + benchmarks/src/signed128_mul.cpp | 72 ++++++++++++++++++++++++++++++++ stl/inc/__msvc_int128.hpp | 13 +++--- 3 files changed, 79 insertions(+), 7 deletions(-) create mode 100644 benchmarks/src/signed128_mul.cpp diff --git a/benchmarks/CMakeLists.txt b/benchmarks/CMakeLists.txt index dcbe625d816..3b590df78e9 100644 --- a/benchmarks/CMakeLists.txt +++ b/benchmarks/CMakeLists.txt @@ -137,6 +137,7 @@ add_benchmark(sample src/sample.cpp) add_benchmark(search src/search.cpp) add_benchmark(search_n src/search_n.cpp) add_benchmark(shuffle src/shuffle.cpp) +add_benchmark(signed128_mul src/signed128_mul.cpp) add_benchmark(std_copy src/std_copy.cpp) add_benchmark(sv_equal src/sv_equal.cpp) add_benchmark(swap_ranges src/swap_ranges.cpp) diff --git a/benchmarks/src/signed128_mul.cpp b/benchmarks/src/signed128_mul.cpp new file mode 100644 index 00000000000..ccb9974678c --- /dev/null +++ b/benchmarks/src/signed128_mul.cpp @@ -0,0 +1,72 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include + +#include <__msvc_int128.hpp> +#include +#include +#include +#include + +using namespace std; + +_NODISCARD constexpr _Signed128 _old_mul128(_Signed128 _Left, _Signed128 _Right) { + bool _negative = false; + _Left._Strip_negative(_negative); + _Right._Strip_negative(_negative); + _Signed128 _Result{_Base128::_Multiply(_Left, _Right)}; + if (_negative) { + _Result = -_Result; + } + return _Result; +} + +vector> init_test_set() { + vector> vec(1'000'000); + mt19937_64 mt64; + for (auto& [val1, val2] : vec) { + val1._Word[0] = mt64(); + val1._Word[1] = mt64(); + val2._Word[0] = mt64(); + val2._Word[1] = mt64(); + } + return vec; +} + +void bm_signed128_mul(benchmark::State& state) { + const auto vec = init_test_set(); + + auto it = vec.begin(); + _Signed128 res = 0; + + for (auto _ : state) { + res = it->first * it->second; + + benchmark::DoNotOptimize(res); + if (++it == vec.end()) { + it = vec.begin(); + } + } +} + +void bm_signed128_oldmul(benchmark::State& state) { + const auto vec = init_test_set(); + + auto it = vec.begin(); + _Signed128 res = 0; + + for (auto _ : state) { + res = _old_mul128(it->first, it->second); + + benchmark::DoNotOptimize(res); + if (++it == vec.end()) { + it = vec.begin(); + } + } +} + +BENCHMARK(bm_signed128_mul); +BENCHMARK(bm_signed128_oldmul); + +BENCHMARK_MAIN(); diff --git a/stl/inc/__msvc_int128.hpp b/stl/inc/__msvc_int128.hpp index e2f2f8f7f7b..3c4e50e3a05 100644 --- a/stl/inc/__msvc_int128.hpp +++ b/stl/inc/__msvc_int128.hpp @@ -1370,13 +1370,12 @@ struct _Signed128 : _Base128 { } _NODISCARD friend constexpr _Signed128 operator*(_Signed128 _Left, _Signed128 _Right) noexcept { - bool _Negative = false; - _Left._Strip_negative(_Negative); - _Right._Strip_negative(_Negative); - _Signed128 _Result{_Base128::_Multiply(_Left, _Right)}; - if (_Negative) { - _Result = -_Result; - } + // The low 128 bits of a two's-complement product equal the unsigned product, so the sign normalization in the old + // implementation was unnecessary. + _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]; return _Result; } From 4e8d2adf9de0066b97e7bc992fb96a67abc8f35c Mon Sep 17 00:00:00 2001 From: koishi <1580412105@qq.com> Date: Fri, 21 Aug 2026 17:22:51 +0800 Subject: [PATCH 2/4] remove unnecessary comment and add `#include ` to benchmark --- benchmarks/src/signed128_mul.cpp | 1 + stl/inc/__msvc_int128.hpp | 2 -- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/benchmarks/src/signed128_mul.cpp b/benchmarks/src/signed128_mul.cpp index ccb9974678c..196da1e18f6 100644 --- a/benchmarks/src/signed128_mul.cpp +++ b/benchmarks/src/signed128_mul.cpp @@ -8,6 +8,7 @@ #include #include #include +#include using namespace std; diff --git a/stl/inc/__msvc_int128.hpp b/stl/inc/__msvc_int128.hpp index 3c4e50e3a05..78b19d44364 100644 --- a/stl/inc/__msvc_int128.hpp +++ b/stl/inc/__msvc_int128.hpp @@ -1370,8 +1370,6 @@ struct _Signed128 : _Base128 { } _NODISCARD friend constexpr _Signed128 operator*(_Signed128 _Left, _Signed128 _Right) noexcept { - // The low 128 bits of a two's-complement product equal the unsigned product, so the sign normalization in the old - // implementation was unnecessary. _Signed128 _Result; _Result._Word[0] = _UMul128(_Left._Word[0], _Right._Word[0], _Result._Word[1]); _Result._Word[1] += _Left._Word[1] * _Right._Word[0]; From 177d05abf0522fc726e967a15dbd23f4af1d063d Mon Sep 17 00:00:00 2001 From: KKoishi_ <88583686+Koishi-Satori@users.noreply.github.com> Date: Sun, 23 Aug 2026 09:26:37 +0800 Subject: [PATCH 3/4] `__msvc_int128.hpp`: simplify opeartor multiply to a single statement. Co-authored-by: statementreply --- stl/inc/__msvc_int128.hpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/stl/inc/__msvc_int128.hpp b/stl/inc/__msvc_int128.hpp index 78b19d44364..afe5e4b4ac5 100644 --- a/stl/inc/__msvc_int128.hpp +++ b/stl/inc/__msvc_int128.hpp @@ -1370,11 +1370,7 @@ struct _Signed128 : _Base128 { } _NODISCARD friend constexpr _Signed128 operator*(_Signed128 _Left, _Signed128 _Right) noexcept { - _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]; - return _Result; + return _Signed128{_Base128::_Multiply(_Left, _Right)}; } _TEMPLATE_CLASS_INTEGRAL(_Ty) From a51a1ba6c0d7d3d87eb04ad07f70566e196f277f Mon Sep 17 00:00:00 2001 From: koishi <1580412105@qq.com> Date: Mon, 24 Aug 2026 23:43:35 +0800 Subject: [PATCH 4/4] reformat _Signed128 multiply benchmark --- benchmarks/src/signed128_mul.cpp | 148 ++++++++++++++++--------------- 1 file changed, 75 insertions(+), 73 deletions(-) diff --git a/benchmarks/src/signed128_mul.cpp b/benchmarks/src/signed128_mul.cpp index 196da1e18f6..01d3822c369 100644 --- a/benchmarks/src/signed128_mul.cpp +++ b/benchmarks/src/signed128_mul.cpp @@ -1,73 +1,75 @@ -// Copyright (c) Microsoft Corporation. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - -#include - -#include <__msvc_int128.hpp> -#include -#include -#include -#include -#include - -using namespace std; - -_NODISCARD constexpr _Signed128 _old_mul128(_Signed128 _Left, _Signed128 _Right) { - bool _negative = false; - _Left._Strip_negative(_negative); - _Right._Strip_negative(_negative); - _Signed128 _Result{_Base128::_Multiply(_Left, _Right)}; - if (_negative) { - _Result = -_Result; - } - return _Result; -} - -vector> init_test_set() { - vector> vec(1'000'000); - mt19937_64 mt64; - for (auto& [val1, val2] : vec) { - val1._Word[0] = mt64(); - val1._Word[1] = mt64(); - val2._Word[0] = mt64(); - val2._Word[1] = mt64(); - } - return vec; -} - -void bm_signed128_mul(benchmark::State& state) { - const auto vec = init_test_set(); - - auto it = vec.begin(); - _Signed128 res = 0; - - for (auto _ : state) { - res = it->first * it->second; - - benchmark::DoNotOptimize(res); - if (++it == vec.end()) { - it = vec.begin(); - } - } -} - -void bm_signed128_oldmul(benchmark::State& state) { - const auto vec = init_test_set(); - - auto it = vec.begin(); - _Signed128 res = 0; - - for (auto _ : state) { - res = _old_mul128(it->first, it->second); - - benchmark::DoNotOptimize(res); - if (++it == vec.end()) { - it = vec.begin(); - } - } -} - -BENCHMARK(bm_signed128_mul); -BENCHMARK(bm_signed128_oldmul); - -BENCHMARK_MAIN(); +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include + +#include <__msvc_int128.hpp> +#include +#include +#include +#include +#include + +using namespace std; + +_NODISCARD constexpr _Signed128 old_mul128(_Signed128 _Left, _Signed128 _Right) { + bool _negative = false; + _Left._Strip_negative(_negative); + _Right._Strip_negative(_negative); + _Signed128 _Result{_Base128::_Multiply(_Left, _Right)}; + if (_negative) { + _Result = -_Result; + } + return _Result; +} + +vector> init_test_set() { + vector> vec(1'000'000); + mt19937_64 mt64{}; + for (auto& [val1, val2] : vec) { + val1._Word[0] = mt64(); + val1._Word[1] = mt64(); + val2._Word[0] = mt64(); + val2._Word[1] = mt64(); + } + return vec; +} + +void bm_signed128_mul(benchmark::State& state) { + const auto vec = init_test_set(); + + auto it = vec.begin(); + _Signed128 res = 0; + + for (auto _ : state) { + res = it->first * it->second; + + benchmark::DoNotOptimize(res); + ++it; + if (it == vec.end()) { + it = vec.begin(); + } + } +} + +void bm_signed128_oldmul(benchmark::State& state) { + const auto vec = init_test_set(); + + auto it = vec.begin(); + _Signed128 res = 0; + + for (auto _ : state) { + res = old_mul128(it->first, it->second); + + benchmark::DoNotOptimize(res); + ++it; + if (it == vec.end()) { + it = vec.begin(); + } + } +} + +BENCHMARK(bm_signed128_mul); +BENCHMARK(bm_signed128_oldmul); + +BENCHMARK_MAIN();