Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions benchmarks/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
84 changes: 84 additions & 0 deletions benchmarks/src/integer_to_string.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// Copyright (c) Microsoft Corporation.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

#include <algorithm>
#include <array>
#include <benchmark/benchmark.h>
#include <cstddef>
#include <cstdint>
#include <cstdlib>
#include <random>
#include <type_traits>

using namespace std;

template <class T, double M, double S>
auto generate_array() {
array<T, 2000> a;

mt19937 gen;
lognormal_distribution<double> dis(M, S);
ranges::generate(a, [&] { return static_cast<T>(dis(gen)); });

if constexpr (is_signed_v<T>) {
bernoulli_distribution b(0.5);
ranges::for_each(a, [&](T& v) { v *= (b(gen) ? -1 : 1); });
}

return a;
}

template <class T, double M, double S>
void internal_integer_to_buff(benchmark::State& state) {
auto a = generate_array<T, M, S>();

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 <class T, double M, double S>
void integer_to_string(benchmark::State& state) {
auto a = generate_array<T, M, S>();

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<uint8_t, 2.5, 1.5>);
BENCHMARK(internal_integer_to_buff<uint16_t, 5.0, 3.0>);
BENCHMARK(internal_integer_to_buff<uint32_t, 10.0, 6.0>);
BENCHMARK(internal_integer_to_buff<uint64_t, 20.0, 12.0>);

BENCHMARK(integer_to_string<uint8_t, 2.5, 1.5>);
BENCHMARK(integer_to_string<uint16_t, 5.0, 3.0>);
BENCHMARK(integer_to_string<uint32_t, 10.0, 6.0>);
BENCHMARK(integer_to_string<uint64_t, 20.0, 12.0>);

BENCHMARK(integer_to_string<int8_t, 2.5, 1.5>);
BENCHMARK(integer_to_string<int16_t, 5.0, 3.0>);
BENCHMARK(integer_to_string<int32_t, 10.0, 6.0>);
BENCHMARK(integer_to_string<int64_t, 20.0, 12.0>);

BENCHMARK_MAIN();
43 changes: 36 additions & 7 deletions stl/inc/xmemory
Original file line number Diff line number Diff line change
Expand Up @@ -2762,10 +2762,25 @@ namespace ranges {
} // namespace ranges
#endif // _HAS_CXX23

template <class _Elem>
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 <class _Elem, class _UTy>
_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;
Expand All @@ -2776,20 +2791,34 @@ _NODISCARD _Elem* _UIntegral_to_buff(_Elem* _RNext, _UTy _UVal) { // used by bot
auto _UVal_chunk = static_cast<unsigned long>(_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<unsigned long>(_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
Expand Down