Skip to content

Commit 1a3d870

Browse files
ext/bcmath: Use SIMD for trailing zero counts during conversion (#14166)
Changed to count trailing zeros using SIMD when converting a string to a bc_num structure if possible. Removed unnecessary pointer resetting. Added UNEXPECTED to some branches.
1 parent 6e7adb3 commit 1a3d870

File tree

1 file changed

+34
-9
lines changed

1 file changed

+34
-9
lines changed

ext/bcmath/libbcmath/src/str2num.c

Lines changed: 34 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,35 @@ static const char *bc_count_digits(const char *str, const char *end)
7676
return str;
7777
}
7878

79+
static inline const char *bc_skip_zero_reverse(const char *scanner, const char *stop)
80+
{
81+
/* Check in bulk */
82+
#ifdef __SSE2__
83+
const __m128i c_zero_repeat = _mm_set1_epi8('0');
84+
while (scanner - sizeof(__m128i) >= stop) {
85+
scanner -= sizeof(__m128i);
86+
__m128i bytes = _mm_loadu_si128((const __m128i *) scanner);
87+
/* Checks if all numeric strings are equal to '0'. */
88+
bytes = _mm_cmpeq_epi8(bytes, c_zero_repeat);
89+
90+
int mask = _mm_movemask_epi8(bytes);
91+
/* The probability of having 16 trailing 0s in a row is very low, so we use EXPECTED. */
92+
if (EXPECTED(mask != 0xffff)) {
93+
/* Move the pointer back and check each character in loop. */
94+
scanner += sizeof(__m128i);
95+
break;
96+
}
97+
}
98+
#endif
99+
100+
/* Exclude trailing zeros. */
101+
while (scanner - 1 >= stop && scanner[-1] == '0') {
102+
scanner--;
103+
}
104+
105+
return scanner;
106+
}
107+
79108
/* Assumes `num` points to NULL, i.e. does yet not hold a number. */
80109
bool bc_str2num(bc_num *num, const char *str, const char *end, size_t scale, bool auto_scale)
81110
{
@@ -104,32 +133,28 @@ bool bc_str2num(bc_num *num, const char *str, const char *end, size_t scale, boo
104133
const char *decimal_point = (*ptr == '.') ? ptr : NULL;
105134

106135
/* If a non-digit and non-decimal-point indicator is in the string, i.e. an invalid character */
107-
if (!decimal_point && *ptr != '\0') {
136+
if (UNEXPECTED(!decimal_point && *ptr != '\0')) {
108137
goto fail;
109138
}
110139

111140
/* search and validate fractional end if exists */
112141
if (decimal_point) {
113142
/* search */
114143
fractional_ptr = fractional_end = decimal_point + 1;
115-
if (*fractional_ptr == '\0') {
144+
/* For strings that end with a decimal point, such as "012." */
145+
if (UNEXPECTED(*fractional_ptr == '\0')) {
116146
goto after_fractional;
117147
}
118148

119149
/* validate */
120150
fractional_end = bc_count_digits(fractional_ptr, end);
121-
if (*fractional_end != '\0') {
151+
if (UNEXPECTED(*fractional_end != '\0')) {
122152
/* invalid num */
123153
goto fail;
124154
}
125155

126156
/* Exclude trailing zeros. */
127-
while (fractional_end - 1 > decimal_point && fractional_end[-1] == '0') {
128-
fractional_end--;
129-
}
130-
131-
/* Move the pointer to the beginning of the fraction. */
132-
fractional_ptr = decimal_point + 1;
157+
fractional_end = bc_skip_zero_reverse(fractional_end, fractional_ptr);
133158

134159
/* Calculate the length of the fraction excluding trailing zero. */
135160
str_scale = fractional_end - fractional_ptr;

0 commit comments

Comments
 (0)