diff --git a/benchmarks/CMakeLists.txt b/benchmarks/CMakeLists.txt index c5aa8ba677..9b8b3fe324 100644 --- a/benchmarks/CMakeLists.txt +++ b/benchmarks/CMakeLists.txt @@ -113,6 +113,7 @@ add_benchmark(find_and_count src/find_and_count.cpp) add_benchmark(find_first_of src/find_first_of.cpp) add_benchmark(has_single_bit src/has_single_bit.cpp) add_benchmark(includes src/includes.cpp) +add_benchmark(integer_to_string src/integer_to_string.cpp) add_benchmark(iota src/iota.cpp) add_benchmark(is_sorted_until src/is_sorted_until.cpp) add_benchmark(locale_classic src/locale_classic.cpp) diff --git a/benchmarks/src/integer_to_string.cpp b/benchmarks/src/integer_to_string.cpp new file mode 100644 index 0000000000..569dada191 --- /dev/null +++ b/benchmarks/src/integer_to_string.cpp @@ -0,0 +1,84 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +template +auto generate_array() { + array a; + + mt19937 gen; + lognormal_distribution dis(M, S); + ranges::generate(a, [&] { return static_cast(dis(gen)); }); + + if constexpr (is_signed_v) { + bernoulli_distribution b(0.5); + ranges::for_each(a, [&](T& v) { v *= (b(gen) ? -1 : 1); }); + } + + return a; +} + +template +void internal_integer_to_buff(benchmark::State& state) { + auto a = generate_array(); + + char buff[24]; + + auto it = a.begin(); + for (auto _ : state) { + auto i = *it; + benchmark::DoNotOptimize(i); + auto s = std::_UIntegral_to_buff(buff, i); + benchmark::DoNotOptimize(s); + + ++it; + if (it == a.end()) { + it = a.begin(); + } + } +} + +template +void integer_to_string(benchmark::State& state) { + auto a = generate_array(); + + auto it = a.begin(); + for (auto _ : state) { + auto i = *it; + benchmark::DoNotOptimize(i); + auto s = to_string(i); + benchmark::DoNotOptimize(s); + + ++it; + if (it == a.end()) { + it = a.begin(); + } + } +} + +BENCHMARK(internal_integer_to_buff); +BENCHMARK(internal_integer_to_buff); +BENCHMARK(internal_integer_to_buff); +BENCHMARK(internal_integer_to_buff); + +BENCHMARK(integer_to_string); +BENCHMARK(integer_to_string); +BENCHMARK(integer_to_string); +BENCHMARK(integer_to_string); + +BENCHMARK(integer_to_string); +BENCHMARK(integer_to_string); +BENCHMARK(integer_to_string); +BENCHMARK(integer_to_string); + +BENCHMARK_MAIN(); diff --git a/stl/inc/xmemory b/stl/inc/xmemory index 138e2a62d1..12c4e5c096 100644 --- a/stl/inc/xmemory +++ b/stl/inc/xmemory @@ -2762,10 +2762,25 @@ namespace ranges { } // namespace ranges #endif // _HAS_CXX23 +template +constexpr auto _UIntegral_to_buff_digits() { + struct { + _Elem _Data[200]; + } _Result = {}; + + for (int _Idx = 0; _Idx != 100; ++_Idx) { + _Result._Data[_Idx * 2 + 0] = static_cast<_Elem>(_Idx / 10 + '0'); + _Result._Data[_Idx * 2 + 1] = static_cast<_Elem>(_Idx % 10 + '0'); + } + + return _Result; +} + template _NODISCARD _Elem* _UIntegral_to_buff(_Elem* _RNext, _UTy _UVal) { // used by both to_string and thread::id output // format _UVal into buffer *ending at* _RNext static_assert(is_unsigned_v<_UTy>, "_UTy must be unsigned"); + static constexpr auto _Table = _STD _UIntegral_to_buff_digits<_Elem>(); #ifdef _WIN64 auto _UVal_trunc = _UVal; @@ -2776,20 +2791,34 @@ _NODISCARD _Elem* _UIntegral_to_buff(_Elem* _RNext, _UTy _UVal) { // used by bot auto _UVal_chunk = static_cast(_UVal % 1000000000); _UVal /= 1000000000; - for (int _Idx = 0; _Idx != 9; ++_Idx) { - *--_RNext = static_cast<_Elem>('0' + _UVal_chunk % 10); - _UVal_chunk /= 10; + for (int _Idx = 0; _Idx != 4; ++_Idx) { + const unsigned long _UVal_chunk_part = _UVal_chunk % 100; + _UVal_chunk /= 100; + _RNext -= 2; + _CSTD memcpy(_RNext, _Table._Data + _UVal_chunk_part * 2, 2 * sizeof(_Elem)); } + + *--_RNext = static_cast<_Elem>('0' + _UVal_chunk); } } auto _UVal_trunc = static_cast(_UVal); #endif // ^^^ !defined(_WIN64) ^^^ - do { - *--_RNext = static_cast<_Elem>('0' + _UVal_trunc % 10); - _UVal_trunc /= 10; - } while (_UVal_trunc != 0); + if (_UVal_trunc >= 10) { + do { + const unsigned long _UVal_trunc_part = _UVal_trunc % 100; + _UVal_trunc /= 100; + _RNext -= 2; + _CSTD memcpy(_RNext, _Table._Data + _UVal_trunc_part * 2, 2 * sizeof(_Elem)); + } while (_UVal_trunc >= 10); + + if (_UVal_trunc == 0) { + return _RNext; + } + } + + *--_RNext = static_cast<_Elem>('0' + _UVal_trunc); return _RNext; } _STD_END