1515#include "mem.h"
1616#include "oj.h"
1717#include "rxclass.h"
18+ #include "simd.h"
1819#include "val_stack.h"
1920
20- #ifdef OJ_USE_SSE4_2
21- #include <nmmintrin.h>
22- #endif
23-
2421// Workaround in case INFINITY is not defined in math.h or if the OS is CentOS
2522#define OJ_INFINITY (1.0 / 0.0)
2623
@@ -202,23 +199,143 @@ static inline const char *scan_string_noSIMD(const char *str, const char *end) {
202199 return str ;
203200}
204201
205- #ifdef OJ_USE_SSE4_2
206- static inline const char * scan_string_SIMD (const char * str , const char * end ) {
207- static const char chars [16 ] = "\x00\\\"" ;
208- const __m128i terminate = _mm_loadu_si128 ((const __m128i * )& chars [0 ]);
209- const char * _end = (const char * )(end - 16 );
202+ #ifdef HAVE_SIMD_SSE4_2
203+ // Optimized SIMD string scanner using SSE4.2 instructions
204+ // Uses prefetching and processes multiple chunks in parallel to reduce latency
205+ static inline const char * scan_string_SSE42 (const char * str , const char * end ) {
206+ static const char chars [16 ] = "\x00\\\"" ;
207+ const __m128i terminate = _mm_loadu_si128 ((const __m128i * )& chars [0 ]);
208+ const char * safe_end_64 = end - 64 ;
209+ const char * safe_end_16 = end - 16 ;
210+
211+ // Process 64 bytes at a time with parallel SIMD operations
212+ // This reduces pipeline stalls and improves instruction-level parallelism
213+ while (str <= safe_end_64 ) {
214+ // Prefetch next cache line for better memory throughput
215+ __builtin_prefetch (str + 64 , 0 , 0 );
216+
217+ // Load and compare 4 chunks in parallel
218+ const __m128i chunk0 = _mm_loadu_si128 ((const __m128i * )(str ));
219+ const __m128i chunk1 = _mm_loadu_si128 ((const __m128i * )(str + 16 ));
220+ const __m128i chunk2 = _mm_loadu_si128 ((const __m128i * )(str + 32 ));
221+ const __m128i chunk3 = _mm_loadu_si128 ((const __m128i * )(str + 48 ));
222+
223+ const int r0 = _mm_cmpestri (terminate ,
224+ 3 ,
225+ chunk0 ,
226+ 16 ,
227+ _SIDD_UBYTE_OPS | _SIDD_CMP_EQUAL_ANY | _SIDD_LEAST_SIGNIFICANT );
228+ if (__builtin_expect (r0 != 16 , 0 ))
229+ return str + r0 ;
230+
231+ const int r1 = _mm_cmpestri (terminate ,
232+ 3 ,
233+ chunk1 ,
234+ 16 ,
235+ _SIDD_UBYTE_OPS | _SIDD_CMP_EQUAL_ANY | _SIDD_LEAST_SIGNIFICANT );
236+ if (__builtin_expect (r1 != 16 , 0 ))
237+ return str + 16 + r1 ;
238+
239+ const int r2 = _mm_cmpestri (terminate ,
240+ 3 ,
241+ chunk2 ,
242+ 16 ,
243+ _SIDD_UBYTE_OPS | _SIDD_CMP_EQUAL_ANY | _SIDD_LEAST_SIGNIFICANT );
244+ if (__builtin_expect (r2 != 16 , 0 ))
245+ return str + 32 + r2 ;
246+
247+ const int r3 = _mm_cmpestri (terminate ,
248+ 3 ,
249+ chunk3 ,
250+ 16 ,
251+ _SIDD_UBYTE_OPS | _SIDD_CMP_EQUAL_ANY | _SIDD_LEAST_SIGNIFICANT );
252+ if (__builtin_expect (r3 != 16 , 0 ))
253+ return str + 48 + r3 ;
254+
255+ str += 64 ;
256+ }
210257
211- for (; str <= _end ; str += 16 ) {
258+ // Handle remaining 16-byte chunks
259+ for (; str <= safe_end_16 ; str += 16 ) {
212260 const __m128i string = _mm_loadu_si128 ((const __m128i * )str );
213261 const int r = _mm_cmpestri (terminate ,
214262 3 ,
215263 string ,
216264 16 ,
217265 _SIDD_UBYTE_OPS | _SIDD_CMP_EQUAL_ANY | _SIDD_LEAST_SIGNIFICANT );
218- if (r != 16 ) {
219- str = (char * )(str + r );
220- return str ;
221- }
266+ if (r != 16 )
267+ return str + r ;
268+ }
269+
270+ return scan_string_noSIMD (str , end );
271+ }
272+ #endif
273+
274+ #ifdef HAVE_SIMD_SSE2
275+ // Optimized SSE2 string scanner (fallback for older x86_64 CPUs)
276+ // Uses SSE2 instructions with prefetching and parallel processing
277+ static inline const char * scan_string_SSE2 (const char * str , const char * end ) {
278+ const char * safe_end_64 = end - 64 ;
279+ const char * safe_end_16 = end - 16 ;
280+
281+ // Create comparison vectors for our three special characters
282+ const __m128i null_char = _mm_setzero_si128 ();
283+ const __m128i backslash = _mm_set1_epi8 ('\\' );
284+ const __m128i quote = _mm_set1_epi8 ('"' );
285+
286+ // Process 64 bytes at a time for better throughput
287+ while (str <= safe_end_64 ) {
288+ __builtin_prefetch (str + 64 , 0 , 0 );
289+
290+ // Load 4 chunks
291+ const __m128i chunk0 = _mm_loadu_si128 ((const __m128i * )(str ));
292+ const __m128i chunk1 = _mm_loadu_si128 ((const __m128i * )(str + 16 ));
293+ const __m128i chunk2 = _mm_loadu_si128 ((const __m128i * )(str + 32 ));
294+ const __m128i chunk3 = _mm_loadu_si128 ((const __m128i * )(str + 48 ));
295+
296+ // Compare all chunks (allows CPU to parallelize)
297+ const __m128i cmp0 = _mm_or_si128 (
298+ _mm_or_si128 (_mm_cmpeq_epi8 (chunk0 , null_char ), _mm_cmpeq_epi8 (chunk0 , backslash )),
299+ _mm_cmpeq_epi8 (chunk0 , quote ));
300+ const __m128i cmp1 = _mm_or_si128 (
301+ _mm_or_si128 (_mm_cmpeq_epi8 (chunk1 , null_char ), _mm_cmpeq_epi8 (chunk1 , backslash )),
302+ _mm_cmpeq_epi8 (chunk1 , quote ));
303+ const __m128i cmp2 = _mm_or_si128 (
304+ _mm_or_si128 (_mm_cmpeq_epi8 (chunk2 , null_char ), _mm_cmpeq_epi8 (chunk2 , backslash )),
305+ _mm_cmpeq_epi8 (chunk2 , quote ));
306+ const __m128i cmp3 = _mm_or_si128 (
307+ _mm_or_si128 (_mm_cmpeq_epi8 (chunk3 , null_char ), _mm_cmpeq_epi8 (chunk3 , backslash )),
308+ _mm_cmpeq_epi8 (chunk3 , quote ));
309+
310+ // Convert to masks
311+ int mask0 = _mm_movemask_epi8 (cmp0 );
312+ if (__builtin_expect (mask0 != 0 , 0 ))
313+ return str + __builtin_ctz (mask0 );
314+
315+ int mask1 = _mm_movemask_epi8 (cmp1 );
316+ if (__builtin_expect (mask1 != 0 , 0 ))
317+ return str + 16 + __builtin_ctz (mask1 );
318+
319+ int mask2 = _mm_movemask_epi8 (cmp2 );
320+ if (__builtin_expect (mask2 != 0 , 0 ))
321+ return str + 32 + __builtin_ctz (mask2 );
322+
323+ int mask3 = _mm_movemask_epi8 (cmp3 );
324+ if (__builtin_expect (mask3 != 0 , 0 ))
325+ return str + 48 + __builtin_ctz (mask3 );
326+
327+ str += 64 ;
328+ }
329+
330+ // Handle remaining 16-byte chunks
331+ for (; str <= safe_end_16 ; str += 16 ) {
332+ const __m128i chunk = _mm_loadu_si128 ((const __m128i * )str );
333+ const __m128i matches = _mm_or_si128 (
334+ _mm_or_si128 (_mm_cmpeq_epi8 (chunk , null_char ), _mm_cmpeq_epi8 (chunk , backslash )),
335+ _mm_cmpeq_epi8 (chunk , quote ));
336+ int mask = _mm_movemask_epi8 (matches );
337+ if (mask != 0 )
338+ return str + __builtin_ctz (mask );
222339 }
223340
224341 return scan_string_noSIMD (str , end );
@@ -228,9 +345,12 @@ static inline const char *scan_string_SIMD(const char *str, const char *end) {
228345static const char * (* scan_func )(const char * str , const char * end ) = scan_string_noSIMD ;
229346
230347void oj_scanner_init (void ) {
231- #ifdef OJ_USE_SSE4_2
232- scan_func = scan_string_SIMD ;
348+ #ifdef HAVE_SIMD_SSE4_2
349+ scan_func = scan_string_SSE42 ;
350+ #elif defined(HAVE_SIMD_SSE2 )
351+ scan_func = scan_string_SSE2 ;
233352#endif
353+ // Note: ARM NEON string scanning would be added here if needed
234354}
235355
236356// entered at /
0 commit comments