Improve dump_integer performance by implementing a more efficient int2ascii
This commits implements a faster int2ascii inspired by "Fastware" talk given by Andrei Alexandrescu. See: https://www.youtube.com/watch?v=o4-CwDo2zpg
This commit is contained in:
parent
676c847c55
commit
6503e83e74
2 changed files with 190 additions and 30 deletions
|
@ -527,6 +527,40 @@ class serializer
|
|||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief count digits
|
||||
|
||||
Count the number of decimal (base 10) digits for an input unsigned integer.
|
||||
|
||||
@param[in] x unsigned integer number to count its digits
|
||||
@return number of decimal digits
|
||||
*/
|
||||
inline unsigned int count_digits(number_unsigned_t x) noexcept
|
||||
{
|
||||
unsigned int n_digits = 1;
|
||||
for (;;)
|
||||
{
|
||||
if (x < 10)
|
||||
{
|
||||
return n_digits;
|
||||
}
|
||||
if (x < 100)
|
||||
{
|
||||
return n_digits + 1;
|
||||
}
|
||||
if (x < 1000)
|
||||
{
|
||||
return n_digits + 2;
|
||||
}
|
||||
if (x < 10000)
|
||||
{
|
||||
return n_digits + 3;
|
||||
}
|
||||
x = x / 10000u;
|
||||
n_digits += 4;
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief dump an integer
|
||||
|
||||
|
@ -542,6 +576,22 @@ class serializer
|
|||
int> = 0>
|
||||
void dump_integer(NumberType x)
|
||||
{
|
||||
static constexpr std::array<std::array<char, 2>, 100> digits_to_99
|
||||
{
|
||||
{
|
||||
{'0', '0'}, {'0', '1'}, {'0', '2'}, {'0', '3'}, {'0', '4'}, {'0', '5'}, {'0', '6'}, {'0', '7'}, {'0', '8'}, {'0', '9'},
|
||||
{'1', '0'}, {'1', '1'}, {'1', '2'}, {'1', '3'}, {'1', '4'}, {'1', '5'}, {'1', '6'}, {'1', '7'}, {'1', '8'}, {'1', '9'},
|
||||
{'2', '0'}, {'2', '1'}, {'2', '2'}, {'2', '3'}, {'2', '4'}, {'2', '5'}, {'2', '6'}, {'2', '7'}, {'2', '8'}, {'2', '9'},
|
||||
{'3', '0'}, {'3', '1'}, {'3', '2'}, {'3', '3'}, {'3', '4'}, {'3', '5'}, {'3', '6'}, {'3', '7'}, {'3', '8'}, {'3', '9'},
|
||||
{'4', '0'}, {'4', '1'}, {'4', '2'}, {'4', '3'}, {'4', '4'}, {'4', '5'}, {'4', '6'}, {'4', '7'}, {'4', '8'}, {'4', '9'},
|
||||
{'5', '0'}, {'5', '1'}, {'5', '2'}, {'5', '3'}, {'5', '4'}, {'5', '5'}, {'5', '6'}, {'5', '7'}, {'5', '8'}, {'5', '9'},
|
||||
{'6', '0'}, {'6', '1'}, {'6', '2'}, {'6', '3'}, {'6', '4'}, {'6', '5'}, {'6', '6'}, {'6', '7'}, {'6', '8'}, {'6', '9'},
|
||||
{'7', '0'}, {'7', '1'}, {'7', '2'}, {'7', '3'}, {'7', '4'}, {'7', '5'}, {'7', '6'}, {'7', '7'}, {'7', '8'}, {'7', '9'},
|
||||
{'8', '0'}, {'8', '1'}, {'8', '2'}, {'8', '3'}, {'8', '4'}, {'8', '5'}, {'8', '6'}, {'8', '7'}, {'8', '8'}, {'8', '9'},
|
||||
{'9', '0'}, {'9', '1'}, {'9', '2'}, {'9', '3'}, {'9', '4'}, {'9', '5'}, {'9', '6'}, {'9', '7'}, {'9', '8'}, {'9', '9'},
|
||||
}
|
||||
};
|
||||
|
||||
// special case for "0"
|
||||
if (x == 0)
|
||||
{
|
||||
|
@ -549,28 +599,58 @@ class serializer
|
|||
return;
|
||||
}
|
||||
|
||||
// use a pointer to fill the buffer
|
||||
auto buffer_ptr = begin(number_buffer);
|
||||
|
||||
const bool is_negative = std::is_same<NumberType, number_integer_t>::value and not(x >= 0); // see issue #755
|
||||
std::size_t i = 0;
|
||||
number_unsigned_t abs_value;
|
||||
|
||||
while (x != 0)
|
||||
{
|
||||
// spare 1 byte for '\0'
|
||||
assert(i < number_buffer.size() - 1);
|
||||
|
||||
const auto digit = std::labs(static_cast<long>(x % 10));
|
||||
number_buffer[i++] = static_cast<char>('0' + digit);
|
||||
x /= 10;
|
||||
}
|
||||
unsigned int n_chars;
|
||||
|
||||
if (is_negative)
|
||||
{
|
||||
// make sure there is capacity for the '-'
|
||||
assert(i < number_buffer.size() - 2);
|
||||
number_buffer[i++] = '-';
|
||||
*buffer_ptr = '-';
|
||||
abs_value = static_cast<number_unsigned_t>(0 - x);
|
||||
|
||||
// account one more byte for the minus sign
|
||||
n_chars = 1 + count_digits(abs_value);
|
||||
}
|
||||
else
|
||||
{
|
||||
abs_value = static_cast<number_unsigned_t>(x);
|
||||
n_chars = count_digits(abs_value);
|
||||
}
|
||||
|
||||
std::reverse(number_buffer.begin(), number_buffer.begin() + i);
|
||||
o->write_characters(number_buffer.data(), i);
|
||||
// spare 1 byte for '\0'
|
||||
assert(n_chars < number_buffer.size() - 1);
|
||||
|
||||
// jump to the end to generate the string from backward
|
||||
// so we later avoid reversing the result
|
||||
buffer_ptr += n_chars;
|
||||
|
||||
// Fast int2ascii implementation inspired by "Fastware" talk by Andrei Alexandrescu
|
||||
// See: https://www.youtube.com/watch?v=o4-CwDo2zpg
|
||||
const auto buffer_end = buffer_ptr;
|
||||
while (abs_value >= 100)
|
||||
{
|
||||
const auto digits_index = static_cast<unsigned>((abs_value % 100));
|
||||
abs_value /= 100;
|
||||
*(--buffer_ptr) = digits_to_99[digits_index][1];
|
||||
*(--buffer_ptr) = digits_to_99[digits_index][0];
|
||||
}
|
||||
|
||||
if (abs_value >= 10)
|
||||
{
|
||||
const auto digits_index = static_cast<unsigned>(abs_value);
|
||||
*(--buffer_ptr) = digits_to_99[digits_index][1];
|
||||
*(--buffer_ptr) = digits_to_99[digits_index][0];
|
||||
}
|
||||
else
|
||||
{
|
||||
*(--buffer_ptr) = static_cast<char>('0' + abs_value);
|
||||
}
|
||||
|
||||
o->write_characters(number_buffer.data(), n_chars);
|
||||
}
|
||||
|
||||
/*!
|
||||
|
|
|
@ -11410,6 +11410,40 @@ class serializer
|
|||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief count digits
|
||||
|
||||
Count the number of decimal (base 10) digits for an input unsigned integer.
|
||||
|
||||
@param[in] x unsigned integer number to count its digits
|
||||
@return number of decimal digits
|
||||
*/
|
||||
inline unsigned int count_digits(number_unsigned_t x) noexcept
|
||||
{
|
||||
unsigned int n_digits = 1;
|
||||
for (;;)
|
||||
{
|
||||
if (x < 10)
|
||||
{
|
||||
return n_digits;
|
||||
}
|
||||
if (x < 100)
|
||||
{
|
||||
return n_digits + 1;
|
||||
}
|
||||
if (x < 1000)
|
||||
{
|
||||
return n_digits + 2;
|
||||
}
|
||||
if (x < 10000)
|
||||
{
|
||||
return n_digits + 3;
|
||||
}
|
||||
x = x / 10000u;
|
||||
n_digits += 4;
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief dump an integer
|
||||
|
||||
|
@ -11425,6 +11459,22 @@ class serializer
|
|||
int> = 0>
|
||||
void dump_integer(NumberType x)
|
||||
{
|
||||
static constexpr std::array<std::array<char, 2>, 100> digits_to_99
|
||||
{
|
||||
{
|
||||
{'0', '0'}, {'0', '1'}, {'0', '2'}, {'0', '3'}, {'0', '4'}, {'0', '5'}, {'0', '6'}, {'0', '7'}, {'0', '8'}, {'0', '9'},
|
||||
{'1', '0'}, {'1', '1'}, {'1', '2'}, {'1', '3'}, {'1', '4'}, {'1', '5'}, {'1', '6'}, {'1', '7'}, {'1', '8'}, {'1', '9'},
|
||||
{'2', '0'}, {'2', '1'}, {'2', '2'}, {'2', '3'}, {'2', '4'}, {'2', '5'}, {'2', '6'}, {'2', '7'}, {'2', '8'}, {'2', '9'},
|
||||
{'3', '0'}, {'3', '1'}, {'3', '2'}, {'3', '3'}, {'3', '4'}, {'3', '5'}, {'3', '6'}, {'3', '7'}, {'3', '8'}, {'3', '9'},
|
||||
{'4', '0'}, {'4', '1'}, {'4', '2'}, {'4', '3'}, {'4', '4'}, {'4', '5'}, {'4', '6'}, {'4', '7'}, {'4', '8'}, {'4', '9'},
|
||||
{'5', '0'}, {'5', '1'}, {'5', '2'}, {'5', '3'}, {'5', '4'}, {'5', '5'}, {'5', '6'}, {'5', '7'}, {'5', '8'}, {'5', '9'},
|
||||
{'6', '0'}, {'6', '1'}, {'6', '2'}, {'6', '3'}, {'6', '4'}, {'6', '5'}, {'6', '6'}, {'6', '7'}, {'6', '8'}, {'6', '9'},
|
||||
{'7', '0'}, {'7', '1'}, {'7', '2'}, {'7', '3'}, {'7', '4'}, {'7', '5'}, {'7', '6'}, {'7', '7'}, {'7', '8'}, {'7', '9'},
|
||||
{'8', '0'}, {'8', '1'}, {'8', '2'}, {'8', '3'}, {'8', '4'}, {'8', '5'}, {'8', '6'}, {'8', '7'}, {'8', '8'}, {'8', '9'},
|
||||
{'9', '0'}, {'9', '1'}, {'9', '2'}, {'9', '3'}, {'9', '4'}, {'9', '5'}, {'9', '6'}, {'9', '7'}, {'9', '8'}, {'9', '9'},
|
||||
}
|
||||
};
|
||||
|
||||
// special case for "0"
|
||||
if (x == 0)
|
||||
{
|
||||
|
@ -11432,28 +11482,58 @@ class serializer
|
|||
return;
|
||||
}
|
||||
|
||||
// use a pointer to fill the buffer
|
||||
auto buffer_ptr = begin(number_buffer);
|
||||
|
||||
const bool is_negative = std::is_same<NumberType, number_integer_t>::value and not(x >= 0); // see issue #755
|
||||
std::size_t i = 0;
|
||||
number_unsigned_t abs_value;
|
||||
|
||||
while (x != 0)
|
||||
{
|
||||
// spare 1 byte for '\0'
|
||||
assert(i < number_buffer.size() - 1);
|
||||
|
||||
const auto digit = std::labs(static_cast<long>(x % 10));
|
||||
number_buffer[i++] = static_cast<char>('0' + digit);
|
||||
x /= 10;
|
||||
}
|
||||
unsigned int n_chars;
|
||||
|
||||
if (is_negative)
|
||||
{
|
||||
// make sure there is capacity for the '-'
|
||||
assert(i < number_buffer.size() - 2);
|
||||
number_buffer[i++] = '-';
|
||||
*buffer_ptr = '-';
|
||||
abs_value = static_cast<number_unsigned_t>(0 - x);
|
||||
|
||||
// account one more byte for the minus sign
|
||||
n_chars = 1 + count_digits(abs_value);
|
||||
}
|
||||
else
|
||||
{
|
||||
abs_value = static_cast<number_unsigned_t>(x);
|
||||
n_chars = count_digits(abs_value);
|
||||
}
|
||||
|
||||
std::reverse(number_buffer.begin(), number_buffer.begin() + i);
|
||||
o->write_characters(number_buffer.data(), i);
|
||||
// spare 1 byte for '\0'
|
||||
assert(n_chars < number_buffer.size() - 1);
|
||||
|
||||
// jump to the end to generate the string from backward
|
||||
// so we later avoid reversing the result
|
||||
buffer_ptr += n_chars;
|
||||
|
||||
// Fast int2ascii implementation inspired by "Fastware" talk by Andrei Alexandrescu
|
||||
// See: https://www.youtube.com/watch?v=o4-CwDo2zpg
|
||||
const auto buffer_end = buffer_ptr;
|
||||
while (abs_value >= 100)
|
||||
{
|
||||
const auto digits_index = static_cast<unsigned>((abs_value % 100));
|
||||
abs_value /= 100;
|
||||
*(--buffer_ptr) = digits_to_99[digits_index][1];
|
||||
*(--buffer_ptr) = digits_to_99[digits_index][0];
|
||||
}
|
||||
|
||||
if (abs_value >= 10)
|
||||
{
|
||||
const auto digits_index = static_cast<unsigned>(abs_value);
|
||||
*(--buffer_ptr) = digits_to_99[digits_index][1];
|
||||
*(--buffer_ptr) = digits_to_99[digits_index][0];
|
||||
}
|
||||
else
|
||||
{
|
||||
*(--buffer_ptr) = static_cast<char>('0' + abs_value);
|
||||
}
|
||||
|
||||
o->write_characters(number_buffer.data(), n_chars);
|
||||
}
|
||||
|
||||
/*!
|
||||
|
|
Loading…
Reference in a new issue