morton-arithmetic 0.1.0
Fast Morton (Z-order) codes with O(1) arithmetic
Loading...
Searching...
No Matches
simd.hpp
Go to the documentation of this file.
1
16
17#ifndef MORTON_SIMD_HPP
18#define MORTON_SIMD_HPP
19
20#include <cstddef>
21#include <cstdint>
22
23#include "morton/morton.hpp"
24
25#if MORTON_X86
26
27#include <immintrin.h>
28
29#define MORTON_AVX512_TARGET __attribute__((target("avx512f")))
30
31namespace morton {
32namespace detail {
33namespace avx512 {
34
35// ---- 2D (one zero-bit gap): spread 32-bit -> 64-bit, and its inverse --------
36
37MORTON_AVX512_TARGET inline __m512i spread2(__m512i x) {
38 x = _mm512_and_si512(_mm512_or_si512(x, _mm512_slli_epi64(x, 16)),
39 _mm512_set1_epi64(0x0000FFFF0000FFFFLL));
40 x = _mm512_and_si512(_mm512_or_si512(x, _mm512_slli_epi64(x, 8)),
41 _mm512_set1_epi64(0x00FF00FF00FF00FFLL));
42 x = _mm512_and_si512(_mm512_or_si512(x, _mm512_slli_epi64(x, 4)),
43 _mm512_set1_epi64(0x0F0F0F0F0F0F0F0FLL));
44 x = _mm512_and_si512(_mm512_or_si512(x, _mm512_slli_epi64(x, 2)),
45 _mm512_set1_epi64(0x3333333333333333LL));
46 x = _mm512_and_si512(_mm512_or_si512(x, _mm512_slli_epi64(x, 1)),
47 _mm512_set1_epi64(0x5555555555555555LL));
48 return x;
49}
50
51MORTON_AVX512_TARGET inline __m512i compact2(__m512i x) {
52 x = _mm512_and_si512(x, _mm512_set1_epi64(0x5555555555555555LL));
53 x = _mm512_and_si512(_mm512_or_si512(x, _mm512_srli_epi64(x, 1)),
54 _mm512_set1_epi64(0x3333333333333333LL));
55 x = _mm512_and_si512(_mm512_or_si512(x, _mm512_srli_epi64(x, 2)),
56 _mm512_set1_epi64(0x0F0F0F0F0F0F0F0FLL));
57 x = _mm512_and_si512(_mm512_or_si512(x, _mm512_srli_epi64(x, 4)),
58 _mm512_set1_epi64(0x00FF00FF00FF00FFLL));
59 x = _mm512_and_si512(_mm512_or_si512(x, _mm512_srli_epi64(x, 8)),
60 _mm512_set1_epi64(0x0000FFFF0000FFFFLL));
61 x = _mm512_and_si512(_mm512_or_si512(x, _mm512_srli_epi64(x, 16)),
62 _mm512_set1_epi64(0x00000000FFFFFFFFLL));
63 return x;
64}
65
66// ---- 3D (two zero-bit gaps): spread 21-bit -> 63-bit, and its inverse -------
67
68MORTON_AVX512_TARGET inline __m512i spread3(__m512i x) {
69 x = _mm512_and_si512(x, _mm512_set1_epi64(0x1FFFFFLL)); // keep low 21 bits
70 x = _mm512_and_si512(_mm512_or_si512(x, _mm512_slli_epi64(x, 32)),
71 _mm512_set1_epi64(0x1F00000000FFFFLL));
72 x = _mm512_and_si512(_mm512_or_si512(x, _mm512_slli_epi64(x, 16)),
73 _mm512_set1_epi64(0x1F0000FF0000FFLL));
74 x = _mm512_and_si512(_mm512_or_si512(x, _mm512_slli_epi64(x, 8)),
75 _mm512_set1_epi64(0x100F00F00F00F00FLL));
76 x = _mm512_and_si512(_mm512_or_si512(x, _mm512_slli_epi64(x, 4)),
77 _mm512_set1_epi64(0x10C30C30C30C30C3LL));
78 x = _mm512_and_si512(_mm512_or_si512(x, _mm512_slli_epi64(x, 2)),
79 _mm512_set1_epi64(0x1249249249249249LL));
80 return x;
81}
82
83MORTON_AVX512_TARGET inline __m512i compact3(__m512i x) {
84 x = _mm512_and_si512(x, _mm512_set1_epi64(0x1249249249249249LL));
85 x = _mm512_and_si512(_mm512_or_si512(x, _mm512_srli_epi64(x, 2)),
86 _mm512_set1_epi64(0x10C30C30C30C30C3LL));
87 x = _mm512_and_si512(_mm512_or_si512(x, _mm512_srli_epi64(x, 4)),
88 _mm512_set1_epi64(0x100F00F00F00F00FLL));
89 x = _mm512_and_si512(_mm512_or_si512(x, _mm512_srli_epi64(x, 8)),
90 _mm512_set1_epi64(0x1F0000FF0000FFLL));
91 x = _mm512_and_si512(_mm512_or_si512(x, _mm512_srli_epi64(x, 16)),
92 _mm512_set1_epi64(0x1F00000000FFFFLL));
93 x = _mm512_and_si512(_mm512_or_si512(x, _mm512_srli_epi64(x, 32)), _mm512_set1_epi64(0x1FFFFFLL));
94 return x;
95}
96
97// ---- encode / decode (2,32) and (3,21) --------------------------------------
98// The scalar tail (n not a multiple of 8) reuses the core Morton<> path, so the
99// results are bit-for-bit identical to the scalar functions.
100
101MORTON_AVX512_TARGET inline void encode2(const std::uint32_t* x, const std::uint32_t* y,
102 std::uint64_t* out, std::size_t n) {
103 std::size_t i = 0;
104 for (; i + 8 <= n; i += 8) {
105 __m512i vx = _mm512_cvtepu32_epi64(_mm256_loadu_si256((const __m256i*)(x + i)));
106 __m512i vy = _mm512_cvtepu32_epi64(_mm256_loadu_si256((const __m256i*)(y + i)));
107 __m512i code = _mm512_or_si512(spread2(vx), _mm512_slli_epi64(spread2(vy), 1));
108 _mm512_storeu_si512((__m512i*)(out + i), code);
109 }
110 for (; i < n; ++i)
111 out[i] = Morton<2, 32>::encode(x[i], y[i]).code();
112}
113
114MORTON_AVX512_TARGET inline void decode2(const std::uint64_t* in, std::uint32_t* x,
115 std::uint32_t* y, std::size_t n) {
116 std::size_t i = 0;
117 for (; i + 8 <= n; i += 8) {
118 __m512i c = _mm512_loadu_si512((const __m512i*)(in + i));
119 _mm256_storeu_si256((__m256i*)(x + i), _mm512_cvtepi64_epi32(compact2(c)));
120 _mm256_storeu_si256((__m256i*)(y + i),
121 _mm512_cvtepi64_epi32(compact2(_mm512_srli_epi64(c, 1))));
122 }
123 for (; i < n; ++i) {
124 auto a = Morton<2, 32>::from_code(in[i]).decode();
125 x[i] = a[0];
126 y[i] = a[1];
127 }
128}
129
130MORTON_AVX512_TARGET inline void encode3(const std::uint32_t* x, const std::uint32_t* y,
131 const std::uint32_t* z, std::uint64_t* out,
132 std::size_t n) {
133 std::size_t i = 0;
134 for (; i + 8 <= n; i += 8) {
135 __m512i vx = _mm512_cvtepu32_epi64(_mm256_loadu_si256((const __m256i*)(x + i)));
136 __m512i vy = _mm512_cvtepu32_epi64(_mm256_loadu_si256((const __m256i*)(y + i)));
137 __m512i vz = _mm512_cvtepu32_epi64(_mm256_loadu_si256((const __m256i*)(z + i)));
138 __m512i code = _mm512_or_si512(_mm512_or_si512(spread3(vx), _mm512_slli_epi64(spread3(vy), 1)),
139 _mm512_slli_epi64(spread3(vz), 2));
140 _mm512_storeu_si512((__m512i*)(out + i), code);
141 }
142 for (; i < n; ++i)
143 out[i] = Morton<3, 21>::encode(x[i], y[i], z[i]).code();
144}
145
146MORTON_AVX512_TARGET inline void decode3(const std::uint64_t* in, std::uint32_t* x,
147 std::uint32_t* y, std::uint32_t* z, std::size_t n) {
148 std::size_t i = 0;
149 for (; i + 8 <= n; i += 8) {
150 __m512i c = _mm512_loadu_si512((const __m512i*)(in + i));
151 _mm256_storeu_si256((__m256i*)(x + i), _mm512_cvtepi64_epi32(compact3(c)));
152 _mm256_storeu_si256((__m256i*)(y + i),
153 _mm512_cvtepi64_epi32(compact3(_mm512_srli_epi64(c, 1))));
154 _mm256_storeu_si256((__m256i*)(z + i),
155 _mm512_cvtepi64_epi32(compact3(_mm512_srli_epi64(c, 2))));
156 }
157 for (; i < n; ++i) {
158 auto a = Morton<3, 21>::from_code(in[i]).decode();
159 x[i] = a[0];
160 y[i] = a[1];
161 z[i] = a[2];
162 }
163}
164
165// ---- per-axis add / sub on 64-bit codes (any 64-bit-code layout) ------------
166// Mirrors the masked-add identity in batch.hpp; the mask/increment are
167// loop-invariant scalars supplied by the caller.
168
169MORTON_AVX512_TARGET inline void add64(const std::uint64_t* in, std::uint64_t* out, std::size_t n,
170 std::uint64_t notM, std::uint64_t dk, std::uint64_t mask,
171 std::uint64_t keep) {
172 const __m512i vnotM = _mm512_set1_epi64((long long)notM);
173 const __m512i vdk = _mm512_set1_epi64((long long)dk);
174 const __m512i vmask = _mm512_set1_epi64((long long)mask);
175 const __m512i vkeep = _mm512_set1_epi64((long long)keep);
176 std::size_t i = 0;
177 for (; i + 8 <= n; i += 8) {
178 __m512i c = _mm512_loadu_si512((const __m512i*)(in + i));
179 __m512i t = _mm512_add_epi64(_mm512_or_si512(c, vnotM), vdk);
180 __m512i r = _mm512_or_si512(_mm512_and_si512(t, vmask), _mm512_and_si512(c, vkeep));
181 _mm512_storeu_si512((__m512i*)(out + i), r);
182 }
183 for (; i < n; ++i)
184 out[i] = (((in[i] | notM) + dk) & mask) | (in[i] & keep);
185}
186
187MORTON_AVX512_TARGET inline void sub64(const std::uint64_t* in, std::uint64_t* out, std::size_t n,
188 std::uint64_t dk, std::uint64_t mask, std::uint64_t keep) {
189 const __m512i vdk = _mm512_set1_epi64((long long)dk);
190 const __m512i vmask = _mm512_set1_epi64((long long)mask);
191 const __m512i vkeep = _mm512_set1_epi64((long long)keep);
192 std::size_t i = 0;
193 for (; i + 8 <= n; i += 8) {
194 __m512i c = _mm512_loadu_si512((const __m512i*)(in + i));
195 __m512i t = _mm512_sub_epi64(_mm512_and_si512(c, vmask), vdk);
196 __m512i r = _mm512_or_si512(_mm512_and_si512(t, vmask), _mm512_and_si512(c, vkeep));
197 _mm512_storeu_si512((__m512i*)(out + i), r);
198 }
199 for (; i < n; ++i)
200 out[i] = (((in[i] & mask) - dk) & mask) | (in[i] & keep);
201}
202
203} // namespace avx512
204} // namespace detail
205} // namespace morton
206
207#undef MORTON_AVX512_TARGET
208
209#endif // MORTON_X86
210
211#endif // MORTON_SIMD_HPP
Core Morton<Dim,Bits> type — Z-order codes with O(1) arithmetic in Morton space.
void decode2(const typename Morton< 2, Bits >::code_type *in, typename Morton< 2, Bits >::coord_type *x, typename Morton< 2, Bits >::coord_type *y, std::size_t n)
Decode an array of codes back to coordinate arrays (2D).
Definition batch.hpp:129
void encode3(const typename Morton< 3, Bits >::coord_type *x, const typename Morton< 3, Bits >::coord_type *y, const typename Morton< 3, Bits >::coord_type *z, typename Morton< 3, Bits >::code_type *out, std::size_t n)
Encode arrays of coordinates (3D) into codes.
Definition batch.hpp:110
void encode2(const typename Morton< 2, Bits >::coord_type *x, const typename Morton< 2, Bits >::coord_type *y, typename Morton< 2, Bits >::code_type *out, std::size_t n)
Encode arrays of coordinates (2D) into codes.
Definition batch.hpp:92
void decode3(const typename Morton< 3, Bits >::code_type *in, typename Morton< 3, Bits >::coord_type *x, typename Morton< 3, Bits >::coord_type *y, typename Morton< 3, Bits >::coord_type *z, std::size_t n)
Decode an array of codes back to coordinate arrays (3D).
Definition batch.hpp:149
Definition batch.hpp:26