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; | ||||
|         } | ||||
| 
 | ||||
|         const bool is_negative = std::is_same<NumberType, number_integer_t>::value and not (x >= 0);  // see issue #755
 | ||||
|         std::size_t i = 0; | ||||
|         // use a pointer to fill the buffer
 | ||||
|         auto buffer_ptr = begin(number_buffer); | ||||
| 
 | ||||
|         while (x != 0) | ||||
|         { | ||||
|             // spare 1 byte for '\0'
 | ||||
|             assert(i < number_buffer.size() - 1); | ||||
|         const bool is_negative = std::is_same<NumberType, number_integer_t>::value and not(x >= 0); // see issue #755
 | ||||
|         number_unsigned_t abs_value; | ||||
| 
 | ||||
|             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…
	
	Add table
		Add a link
		
	
		Reference in a new issue