MCKL
Monte Carlo Kernel Library
threefry_avx2_4x32.hpp
Go to the documentation of this file.
1 //============================================================================
2 // MCKL/include/mckl/random/internal/threefry_avx2_4x32.hpp
3 //----------------------------------------------------------------------------
4 // MCKL: Monte Carlo Kernel Library
5 //----------------------------------------------------------------------------
6 // Copyright (c) 2013-2018, Yan Zhou
7 // All rights reserved.
8 //
9 // Redistribution and use in source and binary forms, with or without
10 // modification, are permitted provided that the following conditions are met:
11 //
12 // Redistributions of source code must retain the above copyright notice,
13 // this list of conditions and the following disclaimer.
14 //
15 // Redistributions in binary form must reproduce the above copyright notice,
16 // this list of conditions and the following disclaimer in the documentation
17 // and/or other materials provided with the distribution.
18 //
19 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS
20 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
23 // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 // POSSIBILITY OF SUCH DAMAGE.
30 //============================================================================
31 
32 #ifndef MCKL_RANDOM_INTERNAL_THREEFRY_AVX2_4X32_HPP
33 #define MCKL_RANDOM_INTERNAL_THREEFRY_AVX2_4X32_HPP
34 
39 
40 MCKL_PUSH_GCC_WARNING("-Wignored-attributes")
41 
42 #define MCKL_RANDOM_INTERNAL_THREEFRY_AVX2_4X32_KBOX(N) \
43  ymmt0 = _mm256_set1_epi32( \
44  static_cast<int>(ThreefryKBox<T, K, N>::template key<0>(par))); \
45  ymmt1 = _mm256_set1_epi32( \
46  static_cast<int>(ThreefryKBox<T, K, N>::template key<1>(par))); \
47  ymmt2 = _mm256_set1_epi32( \
48  static_cast<int>(ThreefryKBox<T, K, N>::template key<2>(par))); \
49  ymmt3 = _mm256_set1_epi32( \
50  static_cast<int>(ThreefryKBox<T, K, N>::template key<3>(par))); \
51  ymms0 = _mm256_add_epi32(ymms0, ymmt0); \
52  ymms1 = _mm256_add_epi32(ymms1, ymmt1); \
53  ymms2 = _mm256_add_epi32(ymms2, ymmt2); \
54  ymms3 = _mm256_add_epi32(ymms3, ymmt3); \
55  ymms4 = _mm256_add_epi32(ymms4, ymmt0); \
56  ymms5 = _mm256_add_epi32(ymms5, ymmt1); \
57  ymms6 = _mm256_add_epi32(ymms6, ymmt2); \
58  ymms7 = _mm256_add_epi32(ymms7, ymmt3);
59 
60 #define MCKL_RANDOM_INTERNAL_THREEFRY_AVX2_4X32_RBOX(N) \
61  { \
62  constexpr int L0 = Constants::rotate::value[0][(N - 1) % 8]; \
63  constexpr int L1 = Constants::rotate::value[1][(N - 1) % 8]; \
64  constexpr int R0 = 32 - L0; \
65  constexpr int R1 = 32 - L1; \
66  ymms0 = _mm256_add_epi32(ymms0, ymms1); \
67  ymms2 = _mm256_add_epi32(ymms2, ymms3); \
68  ymms4 = _mm256_add_epi32(ymms4, ymms5); \
69  ymms6 = _mm256_add_epi32(ymms6, ymms7); \
70  ymmt1 = _mm256_slli_epi32(ymms1, L0); \
71  ymmt3 = _mm256_slli_epi32(ymms3, L1); \
72  ymmt5 = _mm256_slli_epi32(ymms5, L0); \
73  ymmt7 = _mm256_slli_epi32(ymms7, L1); \
74  ymms1 = _mm256_srli_epi32(ymms1, R0); \
75  ymms3 = _mm256_srli_epi32(ymms3, R1); \
76  ymms5 = _mm256_srli_epi32(ymms5, R0); \
77  ymms7 = _mm256_srli_epi32(ymms7, R1); \
78  ymmt1 = _mm256_or_si256(ymms1, ymmt1); \
79  ymmt3 = _mm256_or_si256(ymms3, ymmt3); \
80  ymmt5 = _mm256_or_si256(ymms5, ymmt5); \
81  ymmt7 = _mm256_or_si256(ymms7, ymmt7); \
82  ymms1 = _mm256_xor_si256(ymms2, ymmt3); \
83  ymms3 = _mm256_xor_si256(ymms0, ymmt1); \
84  ymms5 = _mm256_xor_si256(ymms6, ymmt7); \
85  ymms7 = _mm256_xor_si256(ymms4, ymmt5); \
86  }
87 
88 #define MCKL_RANDOM_INTERNAL_THREEFRY_AVX2_4X32_CYCLE_4(N) \
89  MCKL_RANDOM_INTERNAL_THREEFRY_AVX2_4X32_RBOX(N * 8 + 1); \
90  MCKL_RANDOM_INTERNAL_THREEFRY_AVX2_4X32_RBOX(N * 8 + 2); \
91  MCKL_RANDOM_INTERNAL_THREEFRY_AVX2_4X32_RBOX(N * 8 + 3); \
92  MCKL_RANDOM_INTERNAL_THREEFRY_AVX2_4X32_RBOX(N * 8 + 4); \
93  MCKL_RANDOM_INTERNAL_THREEFRY_AVX2_4X32_KBOX(N * 8 + 4);
94 
95 #define MCKL_RANDOM_INTERNAL_THREEFRY_AVX2_4X32_CYCLE_8(N) \
96  MCKL_RANDOM_INTERNAL_THREEFRY_AVX2_4X32_RBOX(N * 8 + 1); \
97  MCKL_RANDOM_INTERNAL_THREEFRY_AVX2_4X32_RBOX(N * 8 + 2); \
98  MCKL_RANDOM_INTERNAL_THREEFRY_AVX2_4X32_RBOX(N * 8 + 3); \
99  MCKL_RANDOM_INTERNAL_THREEFRY_AVX2_4X32_RBOX(N * 8 + 4); \
100  MCKL_RANDOM_INTERNAL_THREEFRY_AVX2_4X32_KBOX(N * 8 + 4); \
101  MCKL_RANDOM_INTERNAL_THREEFRY_AVX2_4X32_RBOX(N * 8 + 5); \
102  MCKL_RANDOM_INTERNAL_THREEFRY_AVX2_4X32_RBOX(N * 8 + 6); \
103  MCKL_RANDOM_INTERNAL_THREEFRY_AVX2_4X32_RBOX(N * 8 + 7); \
104  MCKL_RANDOM_INTERNAL_THREEFRY_AVX2_4X32_RBOX(N * 8 + 8); \
105  MCKL_RANDOM_INTERNAL_THREEFRY_AVX2_4X32_KBOX(N * 8 + 8);
106 
107 namespace mckl {
108 
109 namespace internal {
110 
111 template <typename T>
113 {
114  static_assert(std::numeric_limits<T>::digits == 32,
115  "**Threefry4x32GeneratorAVX2Impl** used with T other than a "
116  "32-bit unsigned integers");
117 
118  static constexpr std::size_t K = 4;
119  static constexpr std::size_t Rounds = 20;
121 
122  public:
123  static void eval(
124  const void *plain, void *cipher, const std::array<T, K + 4> &par)
125  {
126  Threefry4x32GeneratorGenericImpl<T>::eval(plain, cipher, par);
127  }
128 
129  template <typename ResultType>
130  static void eval(std::array<std::uint64_t, 2> &ctr, ResultType *r,
131  const std::array<T, K + 4> &par)
132  {
134  }
135 
136  template <typename ResultType>
137  static void eval(std::array<std::uint64_t, 2> &ctr, std::size_t n,
138  ResultType *r, const std::array<T, K + 4> &par)
139  {
140  constexpr std::size_t R = sizeof(T) * K / sizeof(ResultType);
141 
142  const std::size_t n0 =
143  static_cast<std::size_t>(std::min(static_cast<std::uint64_t>(n),
144  std::numeric_limits<std::uint64_t>::max() - ctr.front()));
145 
146  eval_kernel(ctr, n0, r, par);
147  n -= n0;
148  r += n0 * R;
149 
150  if (n != 0) {
151  eval(ctr, r, par);
152  n -= 1;
153  r += R;
154  }
155 
156  eval_kernel(ctr, n, r, par);
157  }
158 
159  private:
160  template <typename ResultType>
161  static void eval_kernel(std::array<std::uint64_t, 2> &ctr, std::size_t n,
162  ResultType *r, const std::array<T, K + 4> &par)
163  {
164  constexpr std::size_t S = 8;
165  constexpr std::size_t N = sizeof(__m256i) * S / (sizeof(T) * K);
166 
167  __m256i ymmc =
168  _mm256_set_epi64x(static_cast<MCKL_INT64>(std::get<1>(ctr)),
169  static_cast<MCKL_INT64>(std::get<0>(ctr)),
170  static_cast<MCKL_INT64>(std::get<1>(ctr)),
171  static_cast<MCKL_INT64>(std::get<0>(ctr)));
172  ctr.front() += n;
173 
174  __m256i *rptr = reinterpret_cast<__m256i *>(r);
175  while (n != 0) {
176  __m256i ymms0 =
177  _mm256_add_epi64(ymmc, _mm256_set_epi64x(0, 0x02, 0, 0x01));
178  __m256i ymms1 =
179  _mm256_add_epi64(ymmc, _mm256_set_epi64x(0, 0x04, 0, 0x03));
180  __m256i ymms2 =
181  _mm256_add_epi64(ymmc, _mm256_set_epi64x(0, 0x06, 0, 0x05));
182  __m256i ymms3 =
183  _mm256_add_epi64(ymmc, _mm256_set_epi64x(0, 0x08, 0, 0x07));
184  __m256i ymms4 =
185  _mm256_add_epi64(ymmc, _mm256_set_epi64x(0, 0x0A, 0, 0x09));
186  __m256i ymms5 =
187  _mm256_add_epi64(ymmc, _mm256_set_epi64x(0, 0x0C, 0, 0x0B));
188  __m256i ymms6 =
189  _mm256_add_epi64(ymmc, _mm256_set_epi64x(0, 0x0E, 0, 0x0D));
190  __m256i ymms7 =
191  _mm256_add_epi64(ymmc, _mm256_set_epi64x(0, 0x10, 0, 0x0F));
192  ymmc = _mm256_add_epi64(ymmc, _mm256_set_epi64x(0, 0x10, 0, 0x10));
193 
194  __m256i ymmt0 = _mm256_unpacklo_epi32(ymms0, ymms1);
195  __m256i ymmt1 = _mm256_unpacklo_epi32(ymms2, ymms3);
196  __m256i ymmt2 = _mm256_unpacklo_epi32(ymms4, ymms5);
197  __m256i ymmt3 = _mm256_unpacklo_epi32(ymms6, ymms7);
198  __m256i ymmt4 = _mm256_unpackhi_epi32(ymms0, ymms1);
199  __m256i ymmt5 = _mm256_unpackhi_epi32(ymms2, ymms3);
200  __m256i ymmt6 = _mm256_unpackhi_epi32(ymms4, ymms5);
201  __m256i ymmt7 = _mm256_unpackhi_epi32(ymms6, ymms7);
202  __m256i ymmt8 = _mm256_unpacklo_epi64(ymmt0, ymmt1);
203  __m256i ymmt9 = _mm256_unpacklo_epi64(ymmt2, ymmt3);
204  __m256i ymmtA = _mm256_unpacklo_epi64(ymmt4, ymmt5);
205  __m256i ymmtB = _mm256_unpacklo_epi64(ymmt6, ymmt7);
206  __m256i ymmtC = _mm256_unpackhi_epi64(ymmt0, ymmt1);
207  __m256i ymmtD = _mm256_unpackhi_epi64(ymmt2, ymmt3);
208  __m256i ymmtE = _mm256_unpackhi_epi64(ymmt4, ymmt5);
209  __m256i ymmtF = _mm256_unpackhi_epi64(ymmt6, ymmt7);
210  ymms0 = _mm256_permute2x128_si256(ymmt8, ymmt9, 0x20);
211  ymms1 = _mm256_permute2x128_si256(ymmtC, ymmtD, 0x20);
212  ymms2 = _mm256_permute2x128_si256(ymmtA, ymmtB, 0x20);
213  ymms3 = _mm256_permute2x128_si256(ymmtE, ymmtF, 0x20);
214  ymms4 = _mm256_permute2x128_si256(ymmt8, ymmt9, 0x31);
215  ymms5 = _mm256_permute2x128_si256(ymmtC, ymmtD, 0x31);
216  ymms6 = _mm256_permute2x128_si256(ymmtA, ymmtB, 0x31);
217  ymms7 = _mm256_permute2x128_si256(ymmtE, ymmtF, 0x31);
218 
223 
224  ymmt0 = _mm256_unpacklo_epi32(ymms0, ymms1);
225  ymmt1 = _mm256_unpacklo_epi32(ymms2, ymms3);
226  ymmt2 = _mm256_unpacklo_epi32(ymms4, ymms5);
227  ymmt3 = _mm256_unpacklo_epi32(ymms6, ymms7);
228  ymmt4 = _mm256_unpackhi_epi32(ymms0, ymms1);
229  ymmt5 = _mm256_unpackhi_epi32(ymms2, ymms3);
230  ymmt6 = _mm256_unpackhi_epi32(ymms4, ymms5);
231  ymmt7 = _mm256_unpackhi_epi32(ymms6, ymms7);
232  ymmt8 = _mm256_unpacklo_epi64(ymmt0, ymmt1);
233  ymmt9 = _mm256_unpacklo_epi64(ymmt2, ymmt3);
234  ymmtA = _mm256_unpacklo_epi64(ymmt4, ymmt5);
235  ymmtB = _mm256_unpacklo_epi64(ymmt6, ymmt7);
236  ymmtC = _mm256_unpackhi_epi64(ymmt0, ymmt1);
237  ymmtD = _mm256_unpackhi_epi64(ymmt2, ymmt3);
238  ymmtE = _mm256_unpackhi_epi64(ymmt4, ymmt5);
239  ymmtF = _mm256_unpackhi_epi64(ymmt6, ymmt7);
240  ymms0 = _mm256_permute2x128_si256(ymmt8, ymmt9, 0x20);
241  ymms1 = _mm256_permute2x128_si256(ymmtC, ymmtD, 0x20);
242  ymms2 = _mm256_permute2x128_si256(ymmtA, ymmtB, 0x20);
243  ymms3 = _mm256_permute2x128_si256(ymmtE, ymmtF, 0x20);
244  ymms4 = _mm256_permute2x128_si256(ymmt8, ymmt9, 0x31);
245  ymms5 = _mm256_permute2x128_si256(ymmtC, ymmtD, 0x31);
246  ymms6 = _mm256_permute2x128_si256(ymmtA, ymmtB, 0x31);
247  ymms7 = _mm256_permute2x128_si256(ymmtE, ymmtF, 0x31);
248 
249  if (n >= N) {
250  n -= N;
251  _mm256_storeu_si256(rptr++, ymms0);
252  _mm256_storeu_si256(rptr++, ymms1);
253  _mm256_storeu_si256(rptr++, ymms2);
254  _mm256_storeu_si256(rptr++, ymms3);
255  _mm256_storeu_si256(rptr++, ymms4);
256  _mm256_storeu_si256(rptr++, ymms5);
257  _mm256_storeu_si256(rptr++, ymms6);
258  _mm256_storeu_si256(rptr++, ymms7);
259  } else {
260  std::array<__m256i, S> s;
261  std::get<0>(s) = ymms0;
262  std::get<1>(s) = ymms1;
263  std::get<2>(s) = ymms2;
264  std::get<3>(s) = ymms3;
265  std::get<4>(s) = ymms4;
266  std::get<5>(s) = ymms5;
267  std::get<6>(s) = ymms6;
268  std::get<7>(s) = ymms7;
269  std::memcpy(rptr, s.data(), n * sizeof(T) * K);
270  break;
271  }
272  }
273  }
274 }; // class Threefry4x32GeneratorAVX2Impl
275 
276 } // namespace internal
277 
278 } // namespace mckl
279 
281 
282 #endif // MCKL_RANDOM_INTERNAL_THREEFRY_AVX2_4X32_HPP
#define MCKL_RANDOM_INTERNAL_THREEFRY_AVX2_4X32_CYCLE_8(N)
#define MCKL_PUSH_GCC_WARNING(warning)
Definition: compiler.h:78
static void eval(const void *plain, void *cipher, const std::array< T, K+4 > &par)
static void eval(std::array< std::uint64_t, 2 > &ctr, ResultType *r, const std::array< T, K+4 > &par)
Default Threefry constants.
Definition: mcmc.hpp:40
#define MCKL_RANDOM_INTERNAL_THREEFRY_AVX2_4X32_KBOX(N)
static void eval(const void *plain, void *cipher, const std::array< T, K+4 > &par)
#define MCKL_RANDOM_INTERNAL_THREEFRY_AVX2_4X32_CYCLE_4(N)
static void eval(std::array< std::uint64_t, 2 > &ctr, std::size_t n, ResultType *r, const std::array< T, K+4 > &par)
#define MCKL_POP_GCC_WARNING
Definition: compiler.h:79