Skip to content

Commit 13c93b6

Browse files
authored
crc64_combine_hw() (alibaba#1111)
1 parent 892bacf commit 13c93b6

3 files changed

Lines changed: 133 additions & 36 deletions

File tree

common/checksum/crc.cpp

Lines changed: 84 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,6 @@ inline T do_crc(const uint8_t *data, size_t nbytes, T crc, F1 f1, F8 f8) {
4444
crc = f8(crc, *(uint64_t*)(data + offset));
4545
offset += sizeof(uint64_t);
4646
}
47-
4847
// Process any bytes remaining after the last aligned 8-byte block.
4948
while (offset < nbytes) {
5049
crc = f1(crc, data[offset]);
@@ -515,8 +514,8 @@ uint32_t crc32c_hw(const uint8_t *data, size_t nbytes, uint32_t crc) {
515514
return crc32c_hw_portable(data, nbytes, crc);
516515
}
517516

518-
// for 2nd size of 16, 32, ..., 2G bytes
519-
const static uint32_t crc32c_combine_table[] = {
517+
const static uint32_t crc32c_lshift_table[] = {
518+
// by length of 16, 32, ..., 2G bytes
520519
0x493c7d27, 0xba4fc28e, 0x9e4addf8, 0x0d3b6092,
521520
0xb9e02b86, 0xdd7e3b0c, 0x170076fa, 0xa51b6135,
522521
0x82f89c77, 0x54a86326, 0x1dc403cc, 0x5ae703ab,
@@ -526,16 +525,20 @@ const static uint32_t crc32c_combine_table[] = {
526525
0xd610d67e, 0x6b086b3f, 0xd94f3c0b, 0xbf818109,
527526
};
528527

529-
static uint32_t crc32c_shift(uint32_t crc1, uint32_t len2,
528+
// virtually pad `len2` bytes of 0 to source
529+
// data, and return resulting crc value
530+
// `len2` must be >= 16
531+
static uint32_t crc32c_lshift_big(uint32_t crc1, uint32_t len2,
530532
uint32_t (*shift)(uint32_t crc1, uint32_t x)) {
531533
for (len2 >>= 4; len2; len2 &= len2 - 1) {
532-
auto x = crc32c_combine_table[__builtin_ctz(len2)];
534+
auto x = crc32c_lshift_table[__builtin_ctz(len2)];
533535
crc1 = shift(crc1, x);
534536
}
535537
return crc1;
536538
}
537539

538-
static uint32_t crc32c_shift_hw(uint32_t crc1, uint32_t x) {
540+
// do lshift with SIMD instructions
541+
static uint32_t do_crc32c_lshift_hw(uint32_t crc1, uint32_t x) {
539542
uint64_t dat64;
540543
__m128i crc1x, cnstx;
541544
crc1x = _mm_setr_epi32(crc1, 0, 0, 0);
@@ -546,15 +549,15 @@ static uint32_t crc32c_shift_hw(uint32_t crc1, uint32_t x) {
546549
}
547550

548551
uint32_t crc32c_combine_hw(uint32_t crc1, uint32_t crc2, uint32_t len2) {
549-
crc1 = crc32c_shift(crc1, len2, crc32c_shift_hw);
550-
if (unlikely(len2 &= 16 - 1)) {
551-
// I don't know why this doesn't work, while it works for crc32c_combine_sw()
552-
LOG_ERROR_RETURN(EINVAL, -1, "len2 must be a multiple of 16");
552+
if (unlikely(!crc1)) return crc2;
553+
if (unlikely(!len2)) return crc1;
554+
if (unlikely(len2 & 15)) {
553555
if (unlikely(len2 & 8)) crc1 = crc32c(crc1, (uint64_t)0);
554556
if (unlikely(len2 & 4)) crc1 = crc32c(crc1, (uint32_t)0);
555557
if (unlikely(len2 & 2)) crc1 = crc32c(crc1, (uint16_t)0);
556558
if (unlikely(len2 & 1)) crc1 = crc32c(crc1, (uint8_t)0);
557559
}
560+
crc1 = crc32c_lshift_big(crc1, len2, do_crc32c_lshift_hw);
558561
return crc1 ^ crc2;
559562
}
560563

@@ -575,26 +578,28 @@ static uint64_t bit_reverse32_64(uint32_t x) {
575578
return x64 << 32;
576579
}
577580

578-
static uint32_t crc32c_shift_sw(uint32_t crc1, uint32_t x) {
581+
// do lshift with traditional scalar instructions
582+
static uint32_t do_crc32c_lshift_sw(uint32_t crc1, uint32_t x) {
579583
uint64_t q = 0, xrev = bit_reverse32_64(x);
580584
for (int i = 0; i < 64; i++, xrev >>= 1)
581585
q = (q << 1) | __builtin_parity(crc1 & xrev);
582586
return crc32c_sw((uint8_t*)&q, 8, 0);
583587
}
584588

589+
const static unsigned char zeros[32] = {0};
585590
uint32_t crc32c_combine_sw(uint32_t crc1, uint32_t crc2, uint32_t len2) {
586-
crc1 = crc32c_shift(crc1, len2, crc32c_shift_sw);
587-
if (unlikely(len2 &= 16 - 1)) {
588-
const static unsigned char zero[8] = {0};
589-
if (unlikely(len2 & 8)) crc1 = crc32c_sw(zero, 8, crc1);
590-
if (unlikely(len2 & 4)) crc1 = crc32c_sw(zero, 4, crc1);
591-
if (unlikely(len2 & 2)) crc1 = crc32c_sw(zero, 2, crc1);
592-
if (unlikely(len2 & 1)) crc1 = crc32c_sw(zero, 1, crc1);
591+
if (unlikely(!crc1)) return crc2;
592+
if (unlikely(!len2)) return crc1;
593+
if (unlikely(len2 & 15)) {
594+
crc1 = crc32c_sw(zeros, len2 & 15, crc1);
593595
}
596+
crc1 = crc32c_lshift_big(crc1, len2, do_crc32c_lshift_sw);
594597
return crc1 ^ crc2;
595598
}
596599

597-
static uint32_t crc32_remove0(uint32_t crc1, size_t len2) {
600+
// virtually remove `len2` bytes of trailing 0 from
601+
// source data, and return resulting crc value
602+
static uint32_t crc32c_rshift_sw(uint32_t crc1, size_t len2) {
598603
// remove the effect of the zeros added to crc1
599604
// If crc1 = crcA * x^len2 mod p(x), then crcA = crc1 * (x^len2)^(-1) mod p(x)
600605
// Based on crc32_combine_return_crcA0, but uses inverse matrix to remove zeros.
@@ -679,9 +684,9 @@ uint32_t crc32c_trim_sw(CRC32C_Component all, CRC32C_Component prefix, CRC32C_Co
679684
LOG_ERRNO_RETURN(EINVAL, 0, "total size (`) is shorter than summed sizes of prefix (`) + suffix (`)", all.size, prefix.size, suffix.size);
680685
auto crc = all.crc;
681686
if (prefix.size)
682-
crc ^= crc32c_shift(prefix.crc, all.size - prefix.size, crc32c_shift_hw);
687+
crc = crc32c_combine_sw(prefix.crc, crc, all.size - prefix.size);
683688
if (suffix.size)
684-
crc = crc32_remove0(crc ^ suffix.crc, suffix.size);
689+
crc = crc32c_rshift_sw(crc ^ suffix.crc, suffix.size);
685690
return crc;
686691
}
687692

@@ -850,6 +855,64 @@ uint64_t crc64ecma_hw_sse128(const uint8_t *buf, size_t len, uint64_t crc) {
850855
return crc64ecma_hw_portable(buf, len, crc, crc64ecma_hw_big_sse);
851856
}
852857

858+
__attribute__((aligned(16), used))
859+
const static uint64_t crc64ecma_lshift_table[] = {
860+
// by length of 32, 64, ..., 4G bytes
861+
0xe05dd497ca393ae4, 0xb5ea1af9c013aca4, 0x9e735cb59b4724da, 0x2ecbc6dd0447c685,
862+
0x15d325270d465dfe, 0x3f663335b446e329, 0x68a971ffad4f1766, 0xb7fd3098b8293475,
863+
0x1b225daef15ff37d, 0xea0ddceb7273a052, 0x620648e8f01b0134, 0xfba68785783fd770,
864+
0x4c0976fee55cc2f6, 0x2b4d20a2ca417feb, 0xf8a6eaabee9c15b0, 0x2068c6b926839fed,
865+
0xc8fef34a6a17f35a, 0x0de5614bc4140f08, 0x724fd734d6b83fac, 0x710def1593c89dfa,
866+
0x337733943ec752b9, 0x2a46a23b7286e04a, 0x5e7857ffbea8390a, 0xa2988a6572c32450,
867+
0xdb67937ee00fcea3, 0xb427cfd9f16baa88, 0x465d86031d8426b3, 0x56ebf2f895082092,
868+
};
869+
870+
// virtually pad `len2` bytes of 0 to source
871+
// data, and return resulting crc value
872+
// `len2` must be >= 32
873+
static uint64_t crc64ecma_lshift_big(uint64_t crc1, uint32_t len2,
874+
uint64_t (*shift)(uint64_t crc1, uint64_t x)) {
875+
for (len2 >>= 5; len2; len2 &= len2 - 1) {
876+
auto x = crc64ecma_lshift_table[__builtin_ctz(len2)];
877+
crc1 = shift(crc1, x);
878+
}
879+
return crc1;
880+
}
881+
882+
static uint64_t do_crc64ecma_lshift_hw(uint64_t crc, uint64_t x) {
883+
__m128i crc1x, crc2x, crc3x, constx;
884+
const __m128i rk5 = _mm_loadl_epi64((__m128i*)&rk[5-1]);
885+
const __m128i rk7 = _mm_loadu_si128((__m128i*)&rk[7-1]);
886+
887+
crc1x = _mm_cvtsi64_si128(crc);
888+
constx = _mm_cvtsi64_si128(x);
889+
crc1x = _mm_clmulepi64_si128(crc1x, constx, 0x00);
890+
891+
// Fold to 64b
892+
crc2x = _mm_clmulepi64_si128(crc1x, rk5, 0x00);
893+
crc3x = _mm_bsrli_si128(crc1x, 8);
894+
crc1x = _mm_xor_si128(crc2x, crc3x);
895+
896+
// Reduce
897+
crc2x = _mm_clmulepi64_si128(crc1x, rk7, 0x00);
898+
crc3x = _mm_clmulepi64_si128(crc2x, rk7, 0x10);
899+
crc2x = _mm_bslli_si128(crc2x, 8);
900+
crc1x = _mm_xor_si128(crc1x, crc2x);
901+
crc1x = _mm_xor_si128(crc1x, crc3x);
902+
return _mm_extract_epi64(crc1x, 1);
903+
}
904+
905+
uint64_t crc64ecma_combine_hw(uint64_t crc1, uint64_t crc2, uint32_t len2) {
906+
if (unlikely(!crc1)) return crc2;
907+
if (unlikely(!len2)) return crc1;
908+
if (unlikely(len2 & 31)) {
909+
crc1 = ~crc64ecma_hw(zeros, len2 & 31, ~crc1);
910+
}
911+
crc1 = crc64ecma_lshift_big(crc1, len2, do_crc64ecma_lshift_hw);
912+
return crc1 ^ crc2;
913+
}
914+
915+
853916
#ifdef __x86_64__
854917
#ifdef __clang__
855918
#pragma clang attribute pop

common/checksum/crc64ecma.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,3 +41,5 @@ inline bool is_crc64ecma_hw_available() {
4141
extern uint64_t (*crc64ecma_auto)(const uint8_t *data, size_t nbytes, uint64_t crc);
4242
return crc64ecma_auto != crc64ecma_sw;
4343
}
44+
45+
uint64_t crc64ecma_combine_hw(uint64_t crc1, uint64_t crc2, uint32_t len2);

common/checksum/test/test_checksum.cpp

Lines changed: 47 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -228,32 +228,64 @@ TEST(TestChecksum, crc64ecma_hw_asm) {
228228
}
229229

230230
TEST(TestChecksum, crc32_combine) {
231-
const uint32_t N = 10, M = 512;
232-
uint32_t crc[N] = {0};
231+
const uint32_t N = 10, M = 510;
233232
unsigned char buf[M * N];
234233
for (auto& c: buf) c = rand();
234+
auto x = crc32c_sw(buf, M * N, 0);
235+
EXPECT_EQ(x, crc32c_hw(buf, M * N, 0));
236+
237+
for (size_t i = 0; i < 10000; ++i) {
238+
uint32_t L1 = rand() % (sizeof(buf) / 2);
239+
uint32_t L2 = sizeof(buf) - L1;
240+
auto crc1 = crc32c_hw(buf, L1, 0);
241+
auto crc2 = crc32c_hw(buf + L1, L2, 0);
242+
EXPECT_EQ(x, crc32c_combine_hw(crc1, crc2, L2));
243+
}
244+
245+
uint32_t crc[N] = {0};
235246
crc32c_series_sw(buf, M, N, crc);
236-
auto x = crc[0];
237-
for (uint32_t i = 1; i < N; ++i)
238-
x = crc32c_combine_sw(x, crc[i], M);
239-
EXPECT_EQ(x, crc32c_sw(buf, M * N, 0));
240247
EXPECT_EQ(x, crc32c_combine_series_sw(crc, M, N));
248+
EXPECT_EQ(x, crc32c_combine_series_hw(crc, M, N));
241249

242250
memset(crc, 0, sizeof(crc));
243251
crc32c_series_hw(buf, M, N, crc);
244-
/*auto*/ x = crc[0];
245-
for (uint32_t i = 1; i < N; ++i)
246-
x = crc32c_combine_hw(x, crc[i], M);
247-
EXPECT_EQ(x, crc32c_hw(buf, M * N, 0));
252+
EXPECT_EQ(x, crc32c_combine_series_sw(crc, M, N));
248253
EXPECT_EQ(x, crc32c_combine_series_hw(crc, M, N));
249254

250-
auto crc1 = crc32c_hw(buf, 128, 0);
251-
auto crc2 = crc32c_hw(buf + 128, sizeof(buf) - 128*2, 0);
252-
auto crc3 = crc32c_hw(buf + sizeof(buf) - 128, 128, 0);
253-
EXPECT_EQ(crc32c_trim({x, sizeof(buf)}, {crc1, 128}, {crc3, 128}), crc2);
255+
for (size_t i = 0; i < 10000; ++i) {
256+
uint32_t L1 = 100 + rand() % 64,
257+
L3 = 100 + rand() % 64;
258+
auto crc1 = crc32c_hw(buf, L1, 0);
259+
auto crc2 = crc32c_hw(buf + L1, sizeof(buf) - L1 - L3, 0);
260+
auto crc3 = crc32c_hw(buf + sizeof(buf) - L3, L3, 0);
261+
EXPECT_EQ(crc32c_trim({x, sizeof(buf)}, {crc1, L1}, {crc3, L3}), crc2);
262+
}
254263
}
255264

256-
void crc32c_series_hw_old(const uint8_t *buffer, uint32_t part_size, uint32_t n_parts, uint32_t* crc_parts);
265+
TEST(TestChecksum, crc64_combine) {
266+
const uint32_t N = 10, M = 510;
267+
unsigned char buf[M * N];
268+
memset(buf, 0, sizeof(buf));
269+
*(uint64_t*)buf = 0x1234567890abcdef;
270+
auto y = crc64ecma_sw(buf, 30, 0);
271+
auto y1 = crc64ecma_sw(buf, 8, 0);
272+
EXPECT_EQ(y, crc64ecma_sw(buf+8, 30-8, y1));
273+
auto y2 = crc64ecma_sw(buf+8, 30-8, 0);
274+
EXPECT_EQ(y, crc64ecma_combine_hw(y1, y2, 30-8));
275+
276+
for (auto& c: buf) c = rand();
277+
auto x = crc64ecma_sw(buf, M * N, 0);
278+
EXPECT_EQ(x, crc64ecma_hw(buf, M * N, 0));
279+
280+
for (size_t i = 0; i < 10000; ++i) {
281+
uint32_t L2 = rand() % (sizeof(buf) / 2);
282+
uint32_t L1 = sizeof(buf) - L2;
283+
auto crc1 = crc64ecma_sw(buf, L1, 0);
284+
auto crc2 = crc64ecma_sw(buf + L1, L2, 0);
285+
EXPECT_EQ(x, crc64ecma_combine_hw(crc1, crc2, L2));
286+
EXPECT_EQ(x, crc64ecma_sw(buf + L1, L2, crc1));
287+
}
288+
}
257289

258290
TEST(Perf, crc32c_hw_series) {
259291
const uint32_t PART_SIZE = 4096;

0 commit comments

Comments
 (0)