/src/zlib-ng/arch/x86/adler32_avx512_vnni.c
Line | Count | Source |
1 | | /* adler32_avx512_vnni.c -- compute the Adler-32 checksum of a data stream |
2 | | * Based on Brian Bockelman's AVX2 version |
3 | | * Copyright (C) 1995-2011 Mark Adler |
4 | | * Authors: |
5 | | * Adam Stylinski <kungfujesus06@gmail.com> |
6 | | * Brian Bockelman <bockelman@gmail.com> |
7 | | * For conditions of distribution and use, see copyright notice in zlib.h |
8 | | */ |
9 | | |
10 | | #ifdef X86_AVX512VNNI |
11 | | |
12 | | #include "zbuild.h" |
13 | | #include "adler32_p.h" |
14 | | #include "arch_functions.h" |
15 | | #include <immintrin.h> |
16 | | #include "x86_intrins.h" |
17 | | #include "adler32_avx512_p.h" |
18 | | #include "adler32_avx2_p.h" |
19 | | |
20 | 0 | Z_INTERNAL uint32_t adler32_avx512_vnni(uint32_t adler, const uint8_t *src, size_t len) { |
21 | 0 | uint32_t adler0, adler1; |
22 | 0 | adler1 = (adler >> 16) & 0xffff; |
23 | 0 | adler0 = adler & 0xffff; |
24 | |
|
25 | 0 | rem_peel: |
26 | 0 | if (len < 32) |
27 | 0 | return adler32_ssse3(adler, src, len); |
28 | | |
29 | 0 | if (len < 64) |
30 | 0 | return adler32_avx2(adler, src, len); |
31 | | |
32 | 0 | const __m512i dot2v = _mm512_set_epi8(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, |
33 | 0 | 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, |
34 | 0 | 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, |
35 | 0 | 56, 57, 58, 59, 60, 61, 62, 63, 64); |
36 | |
|
37 | 0 | const __m512i zero = _mm512_setzero_si512(); |
38 | 0 | __m512i vs1, vs2_0; |
39 | |
|
40 | 0 | while (len >= 64) { |
41 | 0 | vs1 = _mm512_zextsi128_si512(_mm_cvtsi32_si128(adler0)); |
42 | 0 | vs2_0 = _mm512_zextsi128_si512(_mm_cvtsi32_si128(adler1)); |
43 | 0 | size_t k = ALIGN_DOWN(MIN(len, NMAX), 64); |
44 | 0 | len -= k; |
45 | 0 | __m512i vs1_0 = vs1; |
46 | 0 | __m512i vs3 = _mm512_setzero_si512(); |
47 | |
|
48 | 0 | __m512i vs2_1 = _mm512_setzero_si512(); |
49 | 0 | __m512i vs2_2 = _mm512_setzero_si512(); |
50 | 0 | __m512i vs2_3 = _mm512_setzero_si512(); |
51 | |
|
52 | 0 | __m512i vbuf0, vbuf1, vbuf2, vbuf3; |
53 | | |
54 | | /* Remainder peeling for 256-byte alignment boundaries */ |
55 | 0 | while (k % 256) { |
56 | 0 | vbuf0 = _mm512_loadu_si512((__m512i*)src); |
57 | 0 | src += 64; |
58 | 0 | k -= 64; |
59 | |
|
60 | 0 | __m512i vs1_sad = _mm512_sad_epu8(vbuf0, zero); |
61 | 0 | vs1 = _mm512_add_epi32(vs1, vs1_sad); |
62 | 0 | vs3 = _mm512_add_epi32(vs3, vs1_0); |
63 | 0 | vs2_0 = _mm512_dpbusd_epi32(vs2_0, vbuf0, dot2v); |
64 | 0 | vs1_0 = vs1; |
65 | 0 | } |
66 | | |
67 | | /* Manually unrolled by 4 to maximize ILP with separate accumulators */ |
68 | 0 | while (k >= 256) { |
69 | 0 | vbuf0 = _mm512_loadu_si512((__m512i*)src); |
70 | 0 | vbuf1 = _mm512_loadu_si512((__m512i*)(src + 64)); |
71 | 0 | vbuf2 = _mm512_loadu_si512((__m512i*)(src + 128)); |
72 | 0 | vbuf3 = _mm512_loadu_si512((__m512i*)(src + 192)); |
73 | 0 | src += 256; |
74 | 0 | k -= 256; |
75 | | |
76 | | // Chunk 0 |
77 | 0 | __m512i vs1_sad0 = _mm512_sad_epu8(vbuf0, zero); |
78 | 0 | vs1 = _mm512_add_epi32(vs1, vs1_sad0); |
79 | 0 | vs3 = _mm512_add_epi32(vs3, vs1_0); |
80 | 0 | vs2_0 = _mm512_dpbusd_epi32(vs2_0, vbuf0, dot2v); |
81 | | |
82 | | // Chunk 1 |
83 | 0 | vs3 = _mm512_add_epi32(vs3, vs1); |
84 | 0 | __m512i vs1_sad1 = _mm512_sad_epu8(vbuf1, zero); |
85 | 0 | vs1 = _mm512_add_epi32(vs1, vs1_sad1); |
86 | 0 | vs2_1 = _mm512_dpbusd_epi32(vs2_1, vbuf1, dot2v); |
87 | | |
88 | | // Chunk 2 |
89 | 0 | vs3 = _mm512_add_epi32(vs3, vs1); |
90 | 0 | __m512i vs1_sad2 = _mm512_sad_epu8(vbuf2, zero); |
91 | 0 | vs1 = _mm512_add_epi32(vs1, vs1_sad2); |
92 | 0 | vs2_2 = _mm512_dpbusd_epi32(vs2_2, vbuf2, dot2v); |
93 | | |
94 | | // Chunk 3 |
95 | 0 | vs3 = _mm512_add_epi32(vs3, vs1); |
96 | 0 | __m512i vs1_sad3 = _mm512_sad_epu8(vbuf3, zero); |
97 | 0 | vs1 = _mm512_add_epi32(vs1, vs1_sad3); |
98 | 0 | vs2_3 = _mm512_dpbusd_epi32(vs2_3, vbuf3, dot2v); |
99 | |
|
100 | 0 | vs1_0 = vs1; |
101 | 0 | } |
102 | | |
103 | | /* Scale baseline accumulators by 64 bytes. 128 rolls over the sign bit |
104 | | * and the dot product instruction forces the multiplicand to be interpreted |
105 | | * as signed prior to the accumulation */ |
106 | 0 | vs3 = _mm512_slli_epi32(vs3, 6); |
107 | 0 | vs2_0 = _mm512_add_epi32(vs2_0, vs3); |
108 | |
|
109 | 0 | vs2_0 = _mm512_add_epi32(vs2_0, vs2_1); |
110 | 0 | vs2_2 = _mm512_add_epi32(vs2_2, vs2_3); |
111 | 0 | vs2_0 = _mm512_add_epi32(vs2_0, vs2_2); |
112 | |
|
113 | 0 | adler0 = partial_hsum(vs1) % BASE; |
114 | 0 | adler1 = _mm512_reduce_add_epu32(vs2_0) % BASE; |
115 | 0 | } |
116 | |
|
117 | 0 | adler = adler0 | (adler1 << 16); |
118 | | |
119 | | /* Process tail (len < 64). */ |
120 | 0 | if (len) { |
121 | 0 | goto rem_peel; |
122 | 0 | } |
123 | | |
124 | 0 | return adler; |
125 | 0 | } |
126 | | |
127 | | /* Use 256-bit vectors when copying because 512-bit variant is slower for numerous architectural reasons */ |
128 | 0 | Z_INTERNAL uint32_t adler32_copy_avx512_vnni(uint32_t adler, uint8_t *dst, const uint8_t *src, size_t len) { |
129 | 0 | uint32_t adler0, adler1; |
130 | 0 | adler1 = (adler >> 16) & 0xffff; |
131 | 0 | adler0 = adler & 0xffff; |
132 | |
|
133 | 0 | rem_peel_copy: |
134 | 0 | if (len < 32) { |
135 | 0 | __mmask32 storemask = _bzhi_u32(0xFFFFFFFF, (unsigned)len); |
136 | 0 | __m256i copy_vec = _mm256_maskz_loadu_epi8(storemask, src); |
137 | 0 | _mm256_mask_storeu_epi8(dst, storemask, copy_vec); |
138 | |
|
139 | 0 | return adler32_ssse3(adler, src, len); |
140 | 0 | } |
141 | | |
142 | 0 | const __m256i dot2v = _mm256_set_epi8(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, |
143 | 0 | 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32); |
144 | |
|
145 | 0 | const __m256i zero = _mm256_setzero_si256(); |
146 | 0 | __m256i vs1, vs2_0; |
147 | |
|
148 | 0 | while (len >= 32) { |
149 | 0 | vs1 = _mm256_zextsi128_si256(_mm_cvtsi32_si128(adler0)); |
150 | 0 | vs2_0 = _mm256_zextsi128_si256(_mm_cvtsi32_si128(adler1)); |
151 | |
|
152 | 0 | size_t k = ALIGN_DOWN(MIN(len, NMAX), 32); |
153 | 0 | len -= k; |
154 | |
|
155 | 0 | __m256i vs1_0 = vs1; |
156 | 0 | __m256i vs3 = _mm256_setzero_si256(); |
157 | |
|
158 | 0 | __m256i vs2_1 = _mm256_setzero_si256(); |
159 | 0 | __m256i vs2_2 = _mm256_setzero_si256(); |
160 | 0 | __m256i vs2_3 = _mm256_setzero_si256(); |
161 | |
|
162 | 0 | __m256i vbuf0, vbuf1, vbuf2, vbuf3; |
163 | |
|
164 | 0 | while (k % 128) { |
165 | 0 | vbuf0 = _mm256_loadu_si256((__m256i*)src); |
166 | 0 | _mm256_storeu_si256((__m256i*)dst, vbuf0); |
167 | 0 | dst += 32; |
168 | 0 | src += 32; |
169 | 0 | k -= 32; |
170 | |
|
171 | 0 | __m256i vs1_sad = _mm256_sad_epu8(vbuf0, zero); |
172 | 0 | vs1 = _mm256_add_epi32(vs1, vs1_sad); |
173 | 0 | vs3 = _mm256_add_epi32(vs3, vs1_0); |
174 | 0 | vs2_0 = _mm256_dpbusd_epi32(vs2_0, vbuf0, dot2v); |
175 | 0 | vs1_0 = vs1; |
176 | 0 | } |
177 | | |
178 | | /* Manually unrolled by 4 blocks (128 bytes total per iteration) */ |
179 | 0 | while (k >= 128) { |
180 | 0 | vbuf0 = _mm256_loadu_si256((__m256i*)src); |
181 | 0 | vbuf1 = _mm256_loadu_si256((__m256i*)(src + 32)); |
182 | 0 | vbuf2 = _mm256_loadu_si256((__m256i*)(src + 64)); |
183 | 0 | vbuf3 = _mm256_loadu_si256((__m256i*)(src + 96)); |
184 | 0 | src += 128; |
185 | 0 | k -= 128; |
186 | |
|
187 | 0 | _mm256_storeu_si256((__m256i*)dst, vbuf0); |
188 | 0 | _mm256_storeu_si256((__m256i*)(dst + 32), vbuf1); |
189 | 0 | _mm256_storeu_si256((__m256i*)(dst + 64), vbuf2); |
190 | 0 | _mm256_storeu_si256((__m256i*)(dst + 96), vbuf3); |
191 | 0 | dst += 128; |
192 | |
|
193 | 0 | __m256i vs1_sad0 = _mm256_sad_epu8(vbuf0, zero); |
194 | 0 | vs1 = _mm256_add_epi32(vs1, vs1_sad0); |
195 | 0 | vs3 = _mm256_add_epi32(vs3, vs1_0); |
196 | 0 | vs2_0 = _mm256_dpbusd_epi32(vs2_0, vbuf0, dot2v); |
197 | |
|
198 | 0 | vs3 = _mm256_add_epi32(vs3, vs1); |
199 | 0 | __m256i vs1_sad1 = _mm256_sad_epu8(vbuf1, zero); |
200 | 0 | vs1 = _mm256_add_epi32(vs1, vs1_sad1); |
201 | 0 | vs2_1 = _mm256_dpbusd_epi32(vs2_1, vbuf1, dot2v); |
202 | |
|
203 | 0 | vs3 = _mm256_add_epi32(vs3, vs1); |
204 | 0 | __m256i vs1_sad2 = _mm256_sad_epu8(vbuf2, zero); |
205 | 0 | vs1 = _mm256_add_epi32(vs1, vs1_sad2); |
206 | 0 | vs2_2 = _mm256_dpbusd_epi32(vs2_2, vbuf2, dot2v); |
207 | |
|
208 | 0 | vs3 = _mm256_add_epi32(vs3, vs1); |
209 | 0 | __m256i vs1_sad3 = _mm256_sad_epu8(vbuf3, zero); |
210 | 0 | vs1 = _mm256_add_epi32(vs1, vs1_sad3); |
211 | 0 | vs2_3 = _mm256_dpbusd_epi32(vs2_3, vbuf3, dot2v); |
212 | |
|
213 | 0 | vs1_0 = vs1; |
214 | 0 | } |
215 | |
|
216 | 0 | vs3 = _mm256_slli_epi32(vs3, 5); |
217 | 0 | vs2_0 = _mm256_add_epi32(vs2_0, vs3); |
218 | |
|
219 | 0 | vs2_0 = _mm256_add_epi32(vs2_0, vs2_1); |
220 | 0 | vs2_2 = _mm256_add_epi32(vs2_2, vs2_3); |
221 | 0 | vs2_0 = _mm256_add_epi32(vs2_0, vs2_2); |
222 | |
|
223 | 0 | adler0 = partial_hsum256(vs1) % BASE; |
224 | 0 | adler1 = hsum256(vs2_0) % BASE; |
225 | 0 | } |
226 | |
|
227 | 0 | adler = adler0 | (adler1 << 16); |
228 | | |
229 | | /* Process tail (len < 32). */ |
230 | 0 | if (len) { |
231 | 0 | goto rem_peel_copy; |
232 | 0 | } |
233 | | |
234 | 0 | return adler; |
235 | 0 | } |
236 | | |
237 | | #endif |