Skip to content

Commit eae4779

Browse files
authored
crc64ecma implementation for x86_64/AArch64 using SIMD (SSE/NEON) (alibaba#697)
1 parent f34745b commit eae4779

8 files changed

Lines changed: 9684 additions & 82 deletions

File tree

CMakeLists.txt

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -86,9 +86,9 @@ if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
8686
endif()
8787

8888
if (${ARCH} STREQUAL x86_64)
89-
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -msse4.2")
89+
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -msse4.2 -mpclmul")
9090
elseif (${ARCH} STREQUAL aarch64)
91-
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mcpu=generic+crc -fsigned-char -fno-stack-protector -fomit-frame-pointer")
91+
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mcpu=native -fsigned-char -fno-stack-protector -fomit-frame-pointer")
9292
endif ()
9393

9494
if (${ARCH} STREQUAL x86_64)
@@ -203,12 +203,6 @@ file(GLOB PHOTON_SRC RELATIVE "${PROJECT_SOURCE_DIR}"
203203
rpc/*.cpp
204204
thread/*.cpp
205205
)
206-
if (${ARCH} STREQUAL x86_64)
207-
enable_language(ASM_NASM)
208-
list(APPEND PHOTON_SRC ${PROJECT_SOURCE_DIR}/common/checksum/crc64_ecma_refl_by8.asm)
209-
else ()
210-
list(APPEND PHOTON_SRC ${PROJECT_SOURCE_DIR}/common/checksum/crc64_ecma_refl_pmull.S)
211-
endif ()
212206

213207
if (APPLE)
214208
list(APPEND PHOTON_SRC io/kqueue.cpp)

common/checksum/crc32c.cpp

Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -597,6 +597,177 @@ uint32_t crc32c_hw(const uint8_t *data, size_t nbytes, uint32_t crc) {
597597
return crc32c_hw_portable(data, nbytes, crc);
598598
}
599599

600+
// rk1 ~ rk20
601+
__attribute__((aligned(16), used))
602+
const static uint64_t rk[20] = {
603+
0xdabe95afc7875f40,
604+
0xe05dd497ca393ae4,
605+
0xd7d86b2af73de740,
606+
0x8757d71d4fcc1000,
607+
0xdabe95afc7875f40,
608+
0x0000000000000000,
609+
0x9c3e466c172963d5,
610+
0x92d8af2baf0e1e84,
611+
0x947874de595052cb,
612+
0x9e735cb59b4724da,
613+
0xe4ce2cd55fea0037,
614+
0x2fe3fd2920ce82ec,
615+
0x0e31d519421a63a5,
616+
0x2e30203212cac325,
617+
0x081f6054a7842df4,
618+
0x6ae3efbb9dd441f3,
619+
0x69a35d91c3730254,
620+
0xb5ea1af9c013aca4,
621+
0x3be653a30fe1af51,
622+
0x60095b008a9efa44,
623+
};
624+
625+
#define RK(i) &rk[i-1]
626+
627+
__attribute__((aligned(16), used))
628+
const static uint64_t mask[6] = {
629+
0xFFFFFFFFFFFFFFFF, 0x0000000000000000,
630+
0xFFFFFFFF00000000, 0xFFFFFFFFFFFFFFFF,
631+
0x8080808080808080, 0x8080808080808080,
632+
};
633+
634+
#define MASK(i) ({auto p = &mask[((i)-1)*2]; *(v128*)p;})
635+
636+
const static uint64_t pshufb_shf_table[4] = {
637+
0x8786858483828100, 0x8f8e8d8c8b8a8988,
638+
0x0706050403020100, 0x000e0d0c0b0a0908};
639+
640+
inline void* get_shf_table(size_t i) {
641+
return (char*)pshufb_shf_table + i;
642+
}
643+
644+
#pragma GCC diagnostic ignored "-Wstrict-aliasing"
645+
646+
inline uint64_t mm_load_tail_tiny(const void* data, size_t n) {
647+
uint64_t x = 0;
648+
(char*&)data += n;
649+
if (n & 4) x = *--(const uint32_t*&)data;
650+
if (n & 2) x = (x<<16) | *--(const uint16_t*&)data ;
651+
if (n & 1) x = (x <<8) | *--(const uint8_t *&)data ;
652+
return x;
653+
}
654+
655+
#ifdef __x86_64__
656+
#include <immintrin.h>
657+
#elif defined(__aarch64__)
658+
#if !defined(__clang__) && defined(__GNUC__)
659+
#undef __GNUC__
660+
#define __GNUC__ 10
661+
#endif
662+
#define SSE2NEON_SUPPRESS_WARNINGS
663+
#include "sse2neon.h"
664+
#else
665+
#error "Unsupported architecture"
666+
#endif
667+
668+
struct SSE {
669+
public:
670+
typedef __m128i v128;
671+
static v128 loadu(const void* ptr) {
672+
return _mm_loadu_si128((v128*)ptr);
673+
}
674+
static v128 pshufb(v128& x, const v128& y) {
675+
return (v128)_mm_shuffle_epi8((__m128i&)x, (const __m128i&)y);
676+
}
677+
static v128 pblendvb(v128& x, v128& y, v128& z) {
678+
return (v128)_mm_blendv_epi8((__m128i&)x, (__m128i&)y, (__m128i&)z);
679+
}
680+
template<uint8_t imm>
681+
static v128 pclmulqdq(v128& x, const uint64_t* rk) {
682+
return _mm_clmulepi64_si128(x, *(const v128*)rk, imm);
683+
}
684+
static v128 op(v128& x, const uint64_t* rk) {
685+
return pclmulqdq<0x10>(x, rk) ^ pclmulqdq<0x01>(x, rk);
686+
}
687+
static v128 load_small(const void* data, size_t n) {
688+
assert(n < 16);
689+
long x = mm_load_tail_tiny(data, n);
690+
return (n & 8) ? v128{*(long*)data, x} : v128{x, 0};
691+
}
692+
static v128 bsl8(v128 x) {
693+
return _mm_bslli_si128(x, 8);
694+
}
695+
static v128 bsr8(v128 x) {
696+
return _mm_bsrli_si128(x, 8);
697+
}
698+
};
699+
700+
701+
inline __attribute__((always_inline))
702+
uint64_t crc64ecma_hw_portable(const uint8_t *data, size_t nbytes, uint64_t crc) {
703+
if (unlikely(!nbytes || !data)) return crc;
704+
using SIMD = SSE;
705+
using v128 = typename SIMD::v128;
706+
v128 xmm7 = {(long)~crc};
707+
auto& ptr = (const v128*&)data;
708+
if (nbytes >= 256) {
709+
v128 xmm[8];
710+
assert(nbytes >= 256);
711+
static_loop<0, 7, 1>(BODY(i){ xmm[i] = SIMD::loadu(ptr+i); });
712+
xmm[0] ^= xmm7; ptr += 8; nbytes -= 128;
713+
do {
714+
static_loop<0, 7, 1>(BODY(i) {
715+
xmm[i] = SIMD::op(xmm[i], RK(3)) ^ SIMD::loadu(ptr+i);
716+
});
717+
ptr += 8; nbytes -= 128;
718+
} while (nbytes >= 128);
719+
static_loop<0, 6, 1>(BODY(i) {
720+
auto I = (i == 6) ? 1 : (9 + i * 2);
721+
xmm[7] ^= SIMD::op(xmm[i], RK(I));
722+
});
723+
xmm7 = xmm[7];
724+
} else if (nbytes >= 16) {
725+
xmm7 ^= SIMD::loadu(ptr++);
726+
nbytes -= 16;
727+
} else /* 0 < nbytes < 16*/ {
728+
xmm7 ^= SIMD::load_small(data, nbytes);
729+
if (nbytes >= 8) {
730+
auto shf = SIMD::loadu(get_shf_table(nbytes));
731+
xmm7 = SIMD::pshufb(xmm7, shf);
732+
goto _128_done;
733+
} else {
734+
auto shf = SIMD::loadu(get_shf_table(nbytes + 8));
735+
xmm7 = SIMD::pshufb(xmm7, shf);
736+
goto _barrett;
737+
}
738+
}
739+
740+
while (nbytes >= 16) {
741+
xmm7 = SIMD::op(xmm7, RK(1)) ^ SIMD::loadu(ptr++);
742+
nbytes -= 16;
743+
}
744+
745+
if (nbytes) {
746+
auto p = data + nbytes - 16;
747+
auto remainder = SIMD::loadu((v128*)p);
748+
auto xmm0 = SIMD::loadu(get_shf_table(nbytes));
749+
auto xmm2 = xmm7;
750+
xmm7 = SIMD::pshufb(xmm7, xmm0);
751+
xmm0 ^= MASK(3);
752+
xmm2 = SIMD::pshufb(xmm2, xmm0);
753+
xmm2 = SIMD::pblendvb(xmm2, remainder, xmm0);
754+
xmm7 = xmm2 ^ SIMD::op(xmm7, RK(1));
755+
}
756+
_128_done:
757+
xmm7 = SIMD::pclmulqdq<0>(xmm7, RK(5)) ^ SIMD::bsr8(xmm7);
758+
_barrett:
759+
auto t = SIMD::pclmulqdq<0>(xmm7, RK(7));
760+
xmm7 ^= SIMD::pclmulqdq<0x10>(t, RK(7)) ^ SIMD::bsl8(t);
761+
auto p = (uint64_t*)&xmm7;
762+
crc = ~p[1];
763+
return crc;
764+
}
765+
766+
uint64_t crc64ecma_hw(const uint8_t *buf, size_t len, uint64_t crc) {
767+
return crc64ecma_hw_portable(buf, len, crc);
768+
}
769+
770+
600771
/*
601772
* Copyright (c) 2004-2006 Intel Corporation - All Rights Reserved
602773
*

common/checksum/crc64ecma.cpp

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -219,18 +219,3 @@ uint64_t crc64ecma_sw(const uint8_t *buf, size_t len, uint64_t crc) {
219219
crc64_big(crc, buf, len);
220220
}
221221

222-
extern "C" uint64_t crc64_ecma_refl_pmull(uint64_t seed, const uint8_t *buf, uint64_t len);
223-
#if !defined(__APPLE__) || !defined(__x86_64__)
224-
extern "C" uint64_t crc64_ecma_refl_by8 (uint64_t seed, const uint8_t *buf, uint64_t len);
225-
#else
226-
extern "C" uint64_t crc64_ecma_refl_by8 (uint64_t seed, const uint8_t *buf, uint64_t len)
227-
asm("crc64_ecma_refl_by8");
228-
#endif
229-
230-
uint64_t crc64ecma_hw(const uint8_t *buf, size_t len, uint64_t crc) {
231-
#ifdef __aarch64__
232-
return crc64_ecma_refl_pmull(crc, buf, len);
233-
#else
234-
return crc64_ecma_refl_by8(crc, buf, len);
235-
#endif
236-
}

0 commit comments

Comments
 (0)