502f5f0961f5ffcc3f192048c3e38970d25737fa
[alexxy/gromacs.git] / src / gromacs / simd / tests / simd_floatingpoint_util.cpp
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 2015,2017,2018,2019,2020, by the GROMACS development team, led by
5  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
6  * and including many others, as listed in the AUTHORS file in the
7  * top-level source directory and at http://www.gromacs.org.
8  *
9  * GROMACS is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public License
11  * as published by the Free Software Foundation; either version 2.1
12  * of the License, or (at your option) any later version.
13  *
14  * GROMACS is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with GROMACS; if not, see
21  * http://www.gnu.org/licenses, or write to the Free Software Foundation,
22  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
23  *
24  * If you want to redistribute modifications to GROMACS, please
25  * consider that scientific software is very special. Version
26  * control is crucial - bugs must be traceable. We will be happy to
27  * consider code for inclusion in the official distribution, but
28  * derived work must not be called official GROMACS. Details are found
29  * in the README & COPYING files - if they are missing, get the
30  * official version at http://www.gromacs.org.
31  *
32  * To help us fund GROMACS development, we humbly ask that you cite
33  * the research papers on the package. Check out http://www.gromacs.org.
34  */
35 #include "gmxpre.h"
36
37 #include <numeric>
38
39 #include "gromacs/simd/simd.h"
40 #include "gromacs/utility/alignedallocator.h"
41 #include "gromacs/utility/basedefinitions.h"
42
43 #include "testutils/testasserts.h"
44
45 #include "simd.h"
46
47 namespace gmx
48 {
49 namespace test
50 {
51 namespace
52 {
53
54 /*! \cond internal */
55 /*! \addtogroup module_simd */
56 /*! \{ */
57
58 #if GMX_SIMD_HAVE_REAL
59
60 /*! \brief Test fixture for higher-level floating-point utility functions.
61  *
62  * Inherit from main SimdTest, add code to generate aligned memory and data.
63  */
64 class SimdFloatingpointUtilTest : public SimdTest
65 {
66 public:
67     SimdFloatingpointUtilTest()
68     {
69         // Resize vectors to get the amount of memory we need
70         integerMemory_.resize(GMX_SIMD_REAL_WIDTH);
71
72         // The total memory we allocate corresponds to two work arrays
73         // and 4 values each of GMX_SIMD_REAL_WIDTH.
74         realMemory_.resize(2 * s_workMemSize_ + 4 * GMX_SIMD_REAL_WIDTH);
75
76         offset_ = integerMemory_.data();
77         val0_   = realMemory_.data();
78         val1_   = val0_ + GMX_SIMD_REAL_WIDTH;
79         val2_   = val1_ + GMX_SIMD_REAL_WIDTH;
80         val3_   = val2_ + GMX_SIMD_REAL_WIDTH;
81         mem0_   = val3_ + GMX_SIMD_REAL_WIDTH;
82         mem1_   = mem0_ + s_workMemSize_;
83
84         // Set default values for offset and variables val0_ through val3_
85         // We cannot fill mem_ here since those values depend on the test.
86         for (int i = 0; i < GMX_SIMD_REAL_WIDTH; i++)
87         {
88             // Use every third point to avoid a continguous access pattern
89             offset_[i] = 3 * i;
90             // Multiply numbers by 1+100*GMX_REAL_EPS ensures some low bits are
91             // set too, so the tests make sure we read all bits correctly.
92             val0_[i] = (i) * (1.0 + 100 * GMX_REAL_EPS);
93             val1_[i] = (i + 0.1) * (1.0 + 100 * GMX_REAL_EPS);
94             val2_[i] = (i + 0.2) * (1.0 + 100 * GMX_REAL_EPS);
95             val3_[i] = (i + 0.3) * (1.0 + 100 * GMX_REAL_EPS);
96         }
97     }
98
99 protected:
100     //! \brief Size of memory work buffers
101     //
102     // To have a somewhat odd access pattern, we use every
103     // third entry, so the largest value of offset_[i] is 3*GMX_SIMD_REAL_WIDTH.
104     // Then we also allow alignments up to 16, which means the largest index in mem0_[]
105     // that we might access is 16*3*GMX_SIMD_REAL_WIDTH+3.
106     static const std::size_t s_workMemSize_ = 16 * 3 * GMX_SIMD_REAL_WIDTH + 4;
107
108     std::vector<int, AlignedAllocator<int>>   integerMemory_; //!< Aligned integer memory
109     std::vector<real, AlignedAllocator<real>> realMemory_;    //!< Aligned real memory
110
111     int*  offset_; //!< Pointer to offset indices, aligned memory
112     real* val0_;   //!< Pointer to GMX_SIMD_REAL_WIDTH values, aligned
113     real* val1_;   //!< Pointer to GMX_SIMD_REAL_WIDTH values, aligned
114     real* val2_;   //!< Pointer to GMX_SIMD_REAL_WIDTH values, aligned
115     real* val3_;   //!< Pointer to GMX_SIMD_REAL_WIDTH values, aligned
116
117     real* mem0_; //!< Pointer to aligned memory, s_workMemSize real values
118     real* mem1_; //!< Pointer to aligned memory, s_workMemSize real values
119 };
120
121
122 TEST_F(SimdFloatingpointUtilTest, gatherLoadTranspose4)
123 {
124     SimdReal  v0, v1, v2, v3;
125     SimdReal  ref0, ref1, ref2, ref3;
126     const int nalign                = 3;
127     int       alignmentList[nalign] = { 4, 8, 12 };
128     int       i, j, align;
129
130     for (i = 0; i < nalign; i++)
131     {
132         align = alignmentList[i];
133         for (j = 0; j < GMX_SIMD_REAL_WIDTH; j++)
134         {
135             mem0_[align * offset_[j]]     = val0_[j];
136             mem0_[align * offset_[j] + 1] = val1_[j];
137             mem0_[align * offset_[j] + 2] = val2_[j];
138             mem0_[align * offset_[j] + 3] = val3_[j];
139         }
140
141         ref0 = load<SimdReal>(val0_);
142         ref1 = load<SimdReal>(val1_);
143         ref2 = load<SimdReal>(val2_);
144         ref3 = load<SimdReal>(val3_);
145
146         if (align == 4)
147         {
148             gatherLoadTranspose<4>(mem0_, offset_, &v0, &v1, &v2, &v3);
149         }
150         else if (align == 8)
151         {
152             gatherLoadTranspose<8>(mem0_, offset_, &v0, &v1, &v2, &v3);
153         }
154         else if (align == 12)
155         {
156             gatherLoadTranspose<12>(mem0_, offset_, &v0, &v1, &v2, &v3);
157         }
158         else
159         {
160             FAIL();
161         }
162
163         GMX_EXPECT_SIMD_REAL_EQ(ref0, v0);
164         GMX_EXPECT_SIMD_REAL_EQ(ref1, v1);
165         GMX_EXPECT_SIMD_REAL_EQ(ref2, v2);
166         GMX_EXPECT_SIMD_REAL_EQ(ref3, v3);
167     }
168 }
169
170 TEST_F(SimdFloatingpointUtilTest, gatherLoadTranspose2)
171 {
172     SimdReal  v0, v1;
173     SimdReal  ref0, ref1;
174     const int nalign                = 3;
175     int       alignmentList[nalign] = { 2, 4, c_simdBestPairAlignment };
176     int       i, j, align;
177
178     EXPECT_TRUE(c_simdBestPairAlignment <= GMX_SIMD_REAL_WIDTH);
179
180     for (i = 0; i < nalign; i++)
181     {
182         align = alignmentList[i];
183         for (j = 0; j < GMX_SIMD_REAL_WIDTH; j++)
184         {
185             mem0_[align * offset_[j]]     = val0_[j];
186             mem0_[align * offset_[j] + 1] = val1_[j];
187         }
188
189         ref0 = load<SimdReal>(val0_);
190         ref1 = load<SimdReal>(val1_);
191
192         if (align == 2)
193         {
194             gatherLoadTranspose<2>(mem0_, offset_, &v0, &v1);
195         }
196         else if (align == 4)
197         {
198             gatherLoadTranspose<4>(mem0_, offset_, &v0, &v1);
199         }
200         else if (align == c_simdBestPairAlignment)
201         {
202             gatherLoadTranspose<c_simdBestPairAlignment>(mem0_, offset_, &v0, &v1);
203         }
204         else
205         {
206             FAIL();
207         }
208
209         GMX_EXPECT_SIMD_REAL_EQ(ref0, v0);
210         GMX_EXPECT_SIMD_REAL_EQ(ref1, v1);
211     }
212 }
213
214 TEST_F(SimdFloatingpointUtilTest, gatherLoadUTranspose3)
215 {
216     SimdReal  v0, v1, v2;
217     SimdReal  ref0, ref1, ref2;
218     const int nalign                = 2;
219     int       alignmentList[nalign] = { 3, 4 };
220     int       i, j, align;
221
222     for (i = 0; i < nalign; i++)
223     {
224         align = alignmentList[i];
225         for (j = 0; j < GMX_SIMD_REAL_WIDTH; j++)
226         {
227             mem0_[align * offset_[j]]     = val0_[j];
228             mem0_[align * offset_[j] + 1] = val1_[j];
229             mem0_[align * offset_[j] + 2] = val2_[j];
230         }
231
232         ref0 = load<SimdReal>(val0_);
233         ref1 = load<SimdReal>(val1_);
234         ref2 = load<SimdReal>(val2_);
235
236         if (align == 3)
237         {
238             gatherLoadUTranspose<3>(mem0_, offset_, &v0, &v1, &v2);
239         }
240         else if (align == 4)
241         {
242             gatherLoadUTranspose<4>(mem0_, offset_, &v0, &v1, &v2);
243         }
244         else
245         {
246             FAIL();
247         }
248
249         GMX_EXPECT_SIMD_REAL_EQ(ref0, v0);
250         GMX_EXPECT_SIMD_REAL_EQ(ref1, v1);
251         GMX_EXPECT_SIMD_REAL_EQ(ref2, v2);
252     }
253 }
254
255 TEST_F(SimdFloatingpointUtilTest, transposeScatterStoreU3)
256 {
257     SimdReal               v0, v1, v2;
258     real                   refmem[s_workMemSize_];
259     const int              nalign                = 2;
260     int                    alignmentList[nalign] = { 3, 4 };
261     int                    i, align;
262     FloatingPointTolerance tolerance(defaultRealTolerance());
263
264     for (i = 0; i < nalign; i++)
265     {
266         align = alignmentList[i];
267
268         // Set test and reference memory to background value
269         for (std::size_t j = 0; j < s_workMemSize_; j++)
270         {
271             // Multiply by 1+100*eps to make sure low bits are also used
272             mem0_[j] = refmem[j] = (1000.0 + j) * (1.0 + 100 * GMX_REAL_EPS);
273         }
274
275         for (std::size_t j = 0; j < GMX_SIMD_REAL_WIDTH; j++)
276         {
277             // set values in _reference_ memory (we will then test with mem0_, and compare)
278             refmem[align * offset_[j]]     = val0_[j];
279             refmem[align * offset_[j] + 1] = val1_[j];
280             refmem[align * offset_[j] + 2] = val2_[j];
281         }
282
283         v0 = load<SimdReal>(val0_);
284         v1 = load<SimdReal>(val1_);
285         v2 = load<SimdReal>(val2_);
286
287         if (align == 3)
288         {
289             transposeScatterStoreU<3>(mem0_, offset_, v0, v1, v2);
290         }
291         else if (align == 4)
292         {
293             transposeScatterStoreU<4>(mem0_, offset_, v0, v1, v2);
294         }
295         else
296         {
297             FAIL();
298         }
299
300         for (std::size_t j = 0; j < s_workMemSize_; j++)
301         {
302             EXPECT_REAL_EQ_TOL(refmem[j], mem0_[j], tolerance);
303         }
304     }
305 }
306
307 TEST_F(SimdFloatingpointUtilTest, transposeScatterIncrU3)
308 {
309     SimdReal               v0, v1, v2;
310     real                   refmem[s_workMemSize_];
311     const int              nalign                = 2;
312     int                    alignmentList[nalign] = { 3, 4 };
313     int                    i, align;
314     FloatingPointTolerance tolerance(defaultRealTolerance());
315
316     for (i = 0; i < nalign; i++)
317     {
318         align = alignmentList[i];
319
320         // Set test and reference memory to background value
321         for (std::size_t j = 0; j < s_workMemSize_; j++)
322         {
323             // Multiply by 1+100*eps to make sure low bits are also used
324             mem0_[j] = refmem[j] = (1000.0 + j) * (1.0 + 100 * GMX_REAL_EPS);
325         }
326
327         for (std::size_t j = 0; j < GMX_SIMD_REAL_WIDTH; j++)
328         {
329             // Add values to _reference_ memory (we will then test with mem0_, and compare)
330             refmem[align * offset_[j]] += val0_[j];
331             refmem[align * offset_[j] + 1] += val1_[j];
332             refmem[align * offset_[j] + 2] += val2_[j];
333         }
334
335         v0 = load<SimdReal>(val0_);
336         v1 = load<SimdReal>(val1_);
337         v2 = load<SimdReal>(val2_);
338
339         if (align == 3)
340         {
341             transposeScatterIncrU<3>(mem0_, offset_, v0, v1, v2);
342         }
343         else if (align == 4)
344         {
345             transposeScatterIncrU<4>(mem0_, offset_, v0, v1, v2);
346         }
347         else
348         {
349             FAIL();
350         }
351
352         for (std::size_t j = 0; j < s_workMemSize_; j++)
353         {
354             EXPECT_REAL_EQ_TOL(refmem[j], mem0_[j], tolerance);
355         }
356     }
357 }
358
359 TEST_F(SimdFloatingpointUtilTest, transposeScatterIncrU3Overlapping)
360 {
361     SimdReal               v0, v1, v2;
362     real                   refmem[s_workMemSize_];
363     FloatingPointTolerance tolerance(defaultRealTolerance());
364
365     // Alter offset_ to make all entries point to the same (first) value, so all entries will overlap
366     for (std::size_t j = 0; j < GMX_SIMD_REAL_WIDTH; j++)
367     {
368         offset_[j] = 0;
369     }
370
371     // Set test and reference memory to background value
372     for (std::size_t j = 0; j < s_workMemSize_; j++)
373     {
374         // Multiply by 1+100*eps to make sure low bits are also used
375         mem0_[j] = refmem[j] = (1000.0 + j) * (1.0 + 100 * GMX_REAL_EPS);
376     }
377
378     for (std::size_t j = 0; j < GMX_SIMD_REAL_WIDTH; j++)
379     {
380         // Add values to _reference_ memory (we will then test with mem0_, and compare)
381         refmem[3 * offset_[j]] += val0_[j];
382         refmem[3 * offset_[j] + 1] += val1_[j];
383         refmem[3 * offset_[j] + 2] += val2_[j];
384     }
385
386     v0 = load<SimdReal>(val0_);
387     v1 = load<SimdReal>(val1_);
388     v2 = load<SimdReal>(val2_);
389
390     transposeScatterIncrU<3>(mem0_, offset_, v0, v1, v2);
391
392     for (std::size_t j = 0; j < s_workMemSize_; j++)
393     {
394         EXPECT_REAL_EQ_TOL(refmem[j], mem0_[j], tolerance);
395     }
396 }
397
398 TEST_F(SimdFloatingpointUtilTest, transposeScatterDecrU3)
399 {
400     SimdReal               v0, v1, v2;
401     real                   refmem[s_workMemSize_];
402     const int              nalign                = 2;
403     int                    alignmentList[nalign] = { 3, 4 };
404     int                    i, align;
405     FloatingPointTolerance tolerance(defaultRealTolerance());
406
407     for (i = 0; i < nalign; i++)
408     {
409         align = alignmentList[i];
410
411         // Set test and reference memory to background value
412         for (std::size_t j = 0; j < s_workMemSize_; j++)
413         {
414             // Multiply by 1+100*eps to make sure low bits are also used
415             mem0_[j] = refmem[j] = (1000.0 + j) * (1.0 + 100 * GMX_REAL_EPS);
416         }
417
418         for (std::size_t j = 0; j < GMX_SIMD_REAL_WIDTH; j++)
419         {
420             // Subtract values from _reference_ memory (we will then test with mem0_, and compare)
421             refmem[align * offset_[j]] -= val0_[j];
422             refmem[align * offset_[j] + 1] -= val1_[j];
423             refmem[align * offset_[j] + 2] -= val2_[j];
424         }
425
426         v0 = load<SimdReal>(val0_);
427         v1 = load<SimdReal>(val1_);
428         v2 = load<SimdReal>(val2_);
429
430         if (align == 3)
431         {
432             transposeScatterDecrU<3>(mem0_, offset_, v0, v1, v2);
433         }
434         else if (align == 4)
435         {
436             transposeScatterDecrU<4>(mem0_, offset_, v0, v1, v2);
437         }
438         else
439         {
440             FAIL();
441         }
442
443         for (std::size_t j = 0; j < s_workMemSize_; j++)
444         {
445             EXPECT_REAL_EQ_TOL(refmem[j], mem0_[j], tolerance);
446         }
447     }
448 }
449
450 TEST_F(SimdFloatingpointUtilTest, transposeScatterDecrU3Overlapping)
451 {
452     SimdReal               v0, v1, v2;
453     real                   refmem[s_workMemSize_];
454     FloatingPointTolerance tolerance(defaultRealTolerance());
455
456     // Alter offset_ to make all entries point to the same (first) value, so all entries will overlap
457     for (std::size_t j = 0; j < GMX_SIMD_REAL_WIDTH; j++)
458     {
459         offset_[j] = 0;
460     }
461
462     // Set test and reference memory to background value
463     for (std::size_t j = 0; j < s_workMemSize_; j++)
464     {
465         // Multiply by 1+100*eps to make sure low bits are also used
466         mem0_[j] = refmem[j] = (1000.0 + j) * (1.0 + 100 * GMX_REAL_EPS);
467     }
468
469 #    if defined(__INTEL_COMPILER) && (__INTEL_COMPILER < 2021) // Bug in (at least) 19u1 and 18u5 (03424712)
470 #        pragma novector
471 #    endif
472     for (std::size_t j = 0; j < GMX_SIMD_REAL_WIDTH; j++)
473     {
474         // Subtract values from _reference_ memory (we will then test with mem0_, and compare)
475         refmem[3 * offset_[j]] -= val0_[j];
476         refmem[3 * offset_[j] + 1] -= val1_[j];
477         refmem[3 * offset_[j] + 2] -= val2_[j];
478     }
479
480     v0 = load<SimdReal>(val0_);
481     v1 = load<SimdReal>(val1_);
482     v2 = load<SimdReal>(val2_);
483
484     transposeScatterDecrU<3>(mem0_, offset_, v0, v1, v2);
485
486     for (std::size_t j = 0; j < s_workMemSize_; j++)
487     {
488         EXPECT_REAL_EQ_TOL(refmem[j], mem0_[j], tolerance);
489     }
490 }
491
492 TEST_F(SimdFloatingpointUtilTest, expandScalarsToTriplets)
493 {
494     SimdReal vs, v0, v1, v2;
495     int      i;
496
497     for (i = 0; i < GMX_SIMD_REAL_WIDTH; i++)
498     {
499         mem0_[i] = i;
500     }
501
502     vs = load<SimdReal>(mem0_);
503
504     expandScalarsToTriplets(vs, &v0, &v1, &v2);
505
506     store(val0_, v0);
507     store(val1_, v1);
508     store(val2_, v2);
509
510     for (i = 0; i < GMX_SIMD_REAL_WIDTH; i++)
511     {
512         EXPECT_EQ(i / 3, val0_[i]);
513         EXPECT_EQ((i + GMX_SIMD_REAL_WIDTH) / 3, val1_[i]);
514         EXPECT_EQ((i + 2 * GMX_SIMD_REAL_WIDTH) / 3, val2_[i]);
515     }
516 }
517
518
519 TEST_F(SimdFloatingpointUtilTest, gatherLoadBySimdIntTranspose4)
520 {
521     SimdReal  v0, v1, v2, v3;
522     SimdReal  ref0, ref1, ref2, ref3;
523     SimdInt32 simdoffset;
524     const int nalign                = 3;
525     int       alignmentList[nalign] = { 4, 8, 12 };
526     int       i, j, align;
527
528     for (i = 0; i < nalign; i++)
529     {
530         align = alignmentList[i];
531         for (j = 0; j < GMX_SIMD_REAL_WIDTH; j++)
532         {
533             mem0_[align * offset_[j]]     = val0_[j];
534             mem0_[align * offset_[j] + 1] = val1_[j];
535             mem0_[align * offset_[j] + 2] = val2_[j];
536             mem0_[align * offset_[j] + 3] = val3_[j];
537         }
538
539         simdoffset = load<SimdInt32>(offset_);
540         ref0       = load<SimdReal>(val0_);
541         ref1       = load<SimdReal>(val1_);
542         ref2       = load<SimdReal>(val2_);
543         ref3       = load<SimdReal>(val3_);
544
545         if (align == 4)
546         {
547             gatherLoadBySimdIntTranspose<4>(mem0_, simdoffset, &v0, &v1, &v2, &v3);
548         }
549         else if (align == 8)
550         {
551             gatherLoadBySimdIntTranspose<8>(mem0_, simdoffset, &v0, &v1, &v2, &v3);
552         }
553         else if (align == 12)
554         {
555             gatherLoadBySimdIntTranspose<12>(mem0_, simdoffset, &v0, &v1, &v2, &v3);
556         }
557         else
558         {
559             FAIL();
560         }
561
562         GMX_EXPECT_SIMD_REAL_EQ(ref0, v0);
563         GMX_EXPECT_SIMD_REAL_EQ(ref1, v1);
564         GMX_EXPECT_SIMD_REAL_EQ(ref2, v2);
565         GMX_EXPECT_SIMD_REAL_EQ(ref3, v3);
566     }
567 }
568
569
570 TEST_F(SimdFloatingpointUtilTest, gatherLoadBySimdIntTranspose2)
571 {
572     SimdReal  v0, v1;
573     SimdReal  ref0, ref1;
574     SimdInt32 simdoffset;
575     const int nalign                = 3;
576     int       alignmentList[nalign] = { 4, 8, 12 };
577     int       i, j, align;
578
579     for (i = 0; i < nalign; i++)
580     {
581         align = alignmentList[i];
582         for (j = 0; j < GMX_SIMD_REAL_WIDTH; j++)
583         {
584             mem0_[align * offset_[j]]     = val0_[j];
585             mem0_[align * offset_[j] + 1] = val1_[j];
586         }
587
588         simdoffset = load<SimdInt32>(offset_);
589         ref0       = load<SimdReal>(val0_);
590         ref1       = load<SimdReal>(val1_);
591
592         if (align == 4)
593         {
594             gatherLoadBySimdIntTranspose<4>(mem0_, simdoffset, &v0, &v1);
595         }
596         else if (align == 8)
597         {
598             gatherLoadBySimdIntTranspose<8>(mem0_, simdoffset, &v0, &v1);
599         }
600         else if (align == 12)
601         {
602             gatherLoadBySimdIntTranspose<12>(mem0_, simdoffset, &v0, &v1);
603         }
604         else
605         {
606             FAIL();
607         }
608
609         GMX_EXPECT_SIMD_REAL_EQ(ref0, v0);
610         GMX_EXPECT_SIMD_REAL_EQ(ref1, v1);
611     }
612 }
613
614 #    if GMX_SIMD_HAVE_GATHER_LOADU_BYSIMDINT_TRANSPOSE_REAL
615 TEST_F(SimdFloatingpointUtilTest, gatherLoadUBySimdIntTranspose2)
616 {
617     SimdReal  v0, v1;
618     SimdReal  ref0, ref1;
619     SimdInt32 simdoffset;
620     const int nalign                = 3;
621     int       alignmentList[nalign] = { 1, 3, 5 };
622     int       i, j, align;
623
624     for (i = 0; i < nalign; i++)
625     {
626         align = alignmentList[i];
627         for (j = 0; j < GMX_SIMD_REAL_WIDTH; j++)
628         {
629             mem0_[align * offset_[j]]     = val0_[j];
630             mem0_[align * offset_[j] + 1] = val1_[j];
631         }
632
633         simdoffset = load<SimdInt32>(offset_);
634         ref0       = load<SimdReal>(val0_);
635         ref1       = load<SimdReal>(val1_);
636
637         if (align == 1)
638         {
639             gatherLoadUBySimdIntTranspose<1>(mem0_, simdoffset, &v0, &v1);
640         }
641         else if (align == 3)
642         {
643             gatherLoadUBySimdIntTranspose<3>(mem0_, simdoffset, &v0, &v1);
644         }
645         else if (align == 5)
646         {
647             gatherLoadUBySimdIntTranspose<5>(mem0_, simdoffset, &v0, &v1);
648         }
649         else
650         {
651             FAIL();
652         }
653
654         GMX_EXPECT_SIMD_REAL_EQ(ref0, v0);
655         GMX_EXPECT_SIMD_REAL_EQ(ref1, v1);
656     }
657 }
658 #    endif // GMX_SIMD_HAVE_GATHER_LOADU_BYSIMDINT_TRANSPOSE_REAL
659
660 TEST_F(SimdFloatingpointUtilTest, reduceIncr4Sum)
661 {
662     int                    i;
663     SimdReal               v0, v1, v2, v3;
664     real                   sum0, sum1, sum2, sum3, tstsum;
665     FloatingPointTolerance tolerance(defaultRealTolerance());
666
667     v0 = load<SimdReal>(val0_);
668     v1 = load<SimdReal>(val1_);
669     v2 = load<SimdReal>(val2_);
670     v3 = load<SimdReal>(val3_);
671
672     sum0 = sum1 = sum2 = sum3 = 0;
673     for (i = 0; i < GMX_SIMD_REAL_WIDTH; i++)
674     {
675         sum0 += val0_[i];
676         sum1 += val1_[i];
677         sum2 += val2_[i];
678         sum3 += val3_[i];
679     }
680
681     // Just put some numbers in memory so we check the addition is correct
682     mem0_[0] = c0;
683     mem0_[1] = c1;
684     mem0_[2] = c2;
685     mem0_[3] = c3;
686
687     tstsum = reduceIncr4ReturnSum(mem0_, v0, v1, v2, v3);
688
689     EXPECT_REAL_EQ_TOL(c0 + sum0, mem0_[0], tolerance);
690     EXPECT_REAL_EQ_TOL(c1 + sum1, mem0_[1], tolerance);
691     EXPECT_REAL_EQ_TOL(c2 + sum2, mem0_[2], tolerance);
692     EXPECT_REAL_EQ_TOL(c3 + sum3, mem0_[3], tolerance);
693
694     EXPECT_REAL_EQ_TOL(sum0 + sum1 + sum2 + sum3, tstsum, tolerance);
695 }
696
697 #    if GMX_SIMD_HAVE_HSIMD_UTIL_REAL
698
699 TEST_F(SimdFloatingpointUtilTest, loadDualHsimd)
700 {
701     SimdReal v0, v1;
702
703     // Point p to the upper half of val0_
704     real* p = val0_ + GMX_SIMD_REAL_WIDTH / 2;
705
706     v0 = load<SimdReal>(val0_);
707     v1 = loadDualHsimd(val0_, p);
708
709     GMX_EXPECT_SIMD_REAL_EQ(v0, v1);
710 }
711
712 TEST_F(SimdFloatingpointUtilTest, loadDuplicateHsimd)
713 {
714     SimdReal v0, v1;
715     int      i;
716     // Point p to the upper half of val0_
717     real* p = val0_ + GMX_SIMD_REAL_WIDTH / 2;
718     // Copy data so upper half is identical to lower
719     for (i = 0; i < GMX_SIMD_REAL_WIDTH / 2; i++)
720     {
721         p[i] = val0_[i];
722     }
723
724     v0 = load<SimdReal>(val0_);
725     v1 = loadDuplicateHsimd(val0_);
726
727     GMX_EXPECT_SIMD_REAL_EQ(v0, v1);
728 }
729
730
731 TEST_F(SimdFloatingpointUtilTest, loadU1DualHsimd)
732 {
733     SimdReal v0, v1;
734     int      i;
735     real     data[2] = { 1, 2 };
736
737     // Point p to the upper half of val0_
738     real* p = val0_ + GMX_SIMD_REAL_WIDTH / 2;
739     // Set all low elements to data[0], an high to data[1]
740     for (i = 0; i < GMX_SIMD_REAL_WIDTH / 2; i++)
741     {
742         val0_[i] = data[0];
743         p[i]     = data[1];
744     }
745
746     v0 = load<SimdReal>(val0_);
747     v1 = loadU1DualHsimd(data);
748
749     GMX_EXPECT_SIMD_REAL_EQ(v0, v1);
750 }
751
752
753 TEST_F(SimdFloatingpointUtilTest, storeDualHsimd)
754 {
755     SimdReal v0;
756     int      i;
757
758     // Point p to the upper half of val0_
759     real* p = val0_ + GMX_SIMD_REAL_WIDTH / 2;
760
761     v0 = load<SimdReal>(val2_);
762     storeDualHsimd(val0_, p, v0);
763
764     for (i = 0; i < GMX_SIMD_REAL_WIDTH; i++)
765     {
766         EXPECT_EQ(val2_[i], val0_[i]);
767     }
768 }
769
770 TEST_F(SimdFloatingpointUtilTest, incrDualHsimd)
771 {
772     real     reference[GMX_SIMD_REAL_WIDTH];
773     SimdReal v0;
774
775     // Create reference values
776     for (std::size_t i = 0; i < GMX_SIMD_REAL_WIDTH; i++)
777     {
778         reference[i] = val0_[i] + val2_[i];
779     }
780
781     // Point p to the upper half of val0_
782     real* p = val0_ + GMX_SIMD_REAL_WIDTH / 2;
783
784     v0 = load<SimdReal>(val2_);
785     incrDualHsimd(val0_, p, v0);
786
787     for (std::size_t i = 0; i < GMX_SIMD_REAL_WIDTH; i++)
788     {
789         EXPECT_EQ(reference[i], val0_[i]);
790     }
791 }
792
793 TEST_F(SimdFloatingpointUtilTest, incrDualHsimdOverlapping)
794 {
795     real     reference[GMX_SIMD_REAL_WIDTH / 2];
796     SimdReal v0;
797
798     // Create reference values
799     for (std::size_t i = 0; i < GMX_SIMD_REAL_WIDTH / 2; i++)
800     {
801         reference[i] = val0_[i] + val2_[i] + val2_[GMX_SIMD_REAL_WIDTH / 2 + i];
802     }
803
804     v0 = load<SimdReal>(val2_);
805     incrDualHsimd(val0_, val0_, v0);
806
807     for (std::size_t i = 0; i < GMX_SIMD_REAL_WIDTH / 2; i++)
808     {
809         EXPECT_EQ(reference[i], val0_[i]);
810     }
811 }
812
813 TEST_F(SimdFloatingpointUtilTest, decr3Hsimd)
814 {
815     SimdReal               v0, v1, v2;
816     real                   ref[3 * GMX_SIMD_REAL_WIDTH / 2];
817     int                    i, j;
818     FloatingPointTolerance tolerance(defaultRealTolerance());
819
820     // Point p to the upper half of val1_
821     real* p = val1_ + GMX_SIMD_REAL_WIDTH / 2;
822     for (i = 0; i < GMX_SIMD_REAL_WIDTH / 2; i++)
823     {
824         ref[i] = val0_[i] - (val1_[i] + p[i]);
825     }
826     p = val2_ + GMX_SIMD_REAL_WIDTH / 2;
827     for (j = 0; j < GMX_SIMD_REAL_WIDTH / 2; i++, j++)
828     {
829         ref[i] = val0_[i] - (val2_[j] + p[j]);
830     }
831     p = val3_ + GMX_SIMD_REAL_WIDTH / 2;
832     for (j = 0; j < GMX_SIMD_REAL_WIDTH / 2; i++, j++)
833     {
834         ref[i] = val0_[i] - (val3_[j] + p[j]);
835     }
836
837     v0 = load<SimdReal>(val1_);
838     v1 = load<SimdReal>(val2_);
839     v2 = load<SimdReal>(val3_);
840     decr3Hsimd(val0_, v0, v1, v2);
841
842     for (i = 0; i < 3 * GMX_SIMD_REAL_WIDTH / 2; i++)
843     {
844         EXPECT_REAL_EQ_TOL(ref[i], val0_[i], tolerance);
845     }
846 }
847
848
849 TEST_F(SimdFloatingpointUtilTest, gatherLoadTranspose2Hsimd)
850 {
851     SimdReal v0, v1;
852     SimdReal ref0, ref1;
853
854     const int nalign                = 3;
855     int       alignmentList[nalign] = { 2, 4, c_simdBestPairAlignment };
856     int       i, j, align;
857
858     for (i = 0; i < nalign; i++)
859     {
860         align = alignmentList[i];
861         for (j = 0; j < GMX_SIMD_REAL_WIDTH / 2; j++)
862         {
863             // Use mem0_ as base for lower half
864             mem0_[align * offset_[j]]     = val0_[j];
865             mem0_[align * offset_[j] + 1] = val1_[j];
866             // Use mem1_ as base for upper half
867             mem1_[align * offset_[j]]     = val0_[GMX_SIMD_REAL_WIDTH / 2 + j];
868             mem1_[align * offset_[j] + 1] = val1_[GMX_SIMD_REAL_WIDTH / 2 + j];
869         }
870
871         ref0 = load<SimdReal>(val0_);
872         ref1 = load<SimdReal>(val1_);
873
874         if (align == 2)
875         {
876             gatherLoadTransposeHsimd<2>(mem0_, mem1_, offset_, &v0, &v1);
877         }
878         else if (align == 4)
879         {
880             gatherLoadTransposeHsimd<4>(mem0_, mem1_, offset_, &v0, &v1);
881         }
882         else if (align == c_simdBestPairAlignment)
883         {
884             gatherLoadTransposeHsimd<c_simdBestPairAlignment>(mem0_, mem1_, offset_, &v0, &v1);
885         }
886         else
887         {
888             FAIL();
889         }
890
891         GMX_EXPECT_SIMD_REAL_EQ(ref0, v0);
892         GMX_EXPECT_SIMD_REAL_EQ(ref1, v1);
893     }
894 }
895
896
897 TEST_F(SimdFloatingpointUtilTest, reduceIncr4SumHsimd)
898 {
899     int                    i;
900     SimdReal               v0, v1;
901     real                   sum0, sum1, sum2, sum3, tstsum;
902     FloatingPointTolerance tolerance(defaultRealTolerance());
903
904     // Use the half-SIMD storage in memory val0_ and val1_.
905     v0 = load<SimdReal>(val0_);
906     v1 = load<SimdReal>(val1_);
907
908     sum0 = sum1 = sum2 = sum3 = 0;
909     for (i = 0; i < GMX_SIMD_REAL_WIDTH / 2; i++)
910     {
911         sum0 += val0_[i];
912         sum1 += val0_[GMX_SIMD_REAL_WIDTH / 2 + i];
913         sum2 += val1_[i];
914         sum3 += val1_[GMX_SIMD_REAL_WIDTH / 2 + i];
915     }
916
917     // Just put some numbers in memory so we check the addition is correct
918     mem0_[0] = c0;
919     mem0_[1] = c1;
920     mem0_[2] = c2;
921     mem0_[3] = c3;
922
923     tstsum = reduceIncr4ReturnSumHsimd(mem0_, v0, v1);
924
925     EXPECT_REAL_EQ_TOL(c0 + sum0, mem0_[0], tolerance);
926     EXPECT_REAL_EQ_TOL(c1 + sum1, mem0_[1], tolerance);
927     EXPECT_REAL_EQ_TOL(c2 + sum2, mem0_[2], tolerance);
928     EXPECT_REAL_EQ_TOL(c3 + sum3, mem0_[3], tolerance);
929
930     EXPECT_REAL_EQ_TOL(sum0 + sum1 + sum2 + sum3, tstsum, tolerance);
931 }
932
933 #    endif // GMX_SIMD_HAVE_HSIMD_UTIL_REAL
934
935 // Test Currently doesn't work for GMX_SIMD_REAL_WIDTH<4. Should be fixed by having GMX_EXPECT_SIMD_REAL_EQ which works for both Simd and Simd4
936 #    if GMX_SIMD_HAVE_4NSIMD_UTIL_REAL && GMX_SIMD_REAL_WIDTH >= 4
937
938 TEST_F(SimdFloatingpointUtilTest, loadUNDuplicate4)
939 {
940     Simd4NReal v0, v1;
941     int        i;
942     real       data[GMX_SIMD_REAL_WIDTH / 4];
943     std::iota(data, data + GMX_SIMD_REAL_WIDTH / 4, 1);
944
945 #        if defined __ICC && __ICC == 1800 || defined __ICL && __ICL == 1800
946 #            pragma novector /* Work-around for incorrect vectorization for AVX_512(_KNL) */
947 #        endif
948     for (i = 0; i < GMX_SIMD_REAL_WIDTH / 4; i++)
949     {
950         val0_[i * 4] = val0_[i * 4 + 1] = val0_[i * 4 + 2] = val0_[i * 4 + 3] = data[i];
951     }
952
953     v0 = load<Simd4NReal>(val0_);
954     v1 = loadUNDuplicate4(data);
955
956     GMX_EXPECT_SIMD_REAL_EQ(v0, v1);
957 }
958
959 TEST_F(SimdFloatingpointUtilTest, load4DuplicateN)
960 {
961     Simd4NReal v0, v1;
962     int        i;
963     real       data[4] = { 1, 2, 3, 4 };
964
965     for (i = 0; i < GMX_SIMD_REAL_WIDTH / 4; i++)
966     {
967         val0_[i * 4]     = data[0];
968         val0_[i * 4 + 1] = data[1];
969         val0_[i * 4 + 2] = data[2];
970         val0_[i * 4 + 3] = data[3];
971     }
972
973     v0 = load<Simd4NReal>(val0_);
974     v1 = load4DuplicateN(val0_);
975
976     GMX_EXPECT_SIMD_REAL_EQ(v0, v1);
977 }
978
979 TEST_F(SimdFloatingpointUtilTest, loadU4NOffset)
980 {
981     constexpr int offset  = 6; // non power of 2
982     constexpr int dataLen = 4 + offset * (GMX_SIMD_REAL_WIDTH / 4 - 1);
983     real          data[dataLen];
984     std::iota(data, data + dataLen, 1);
985
986     for (int i = 0; i < GMX_SIMD_REAL_WIDTH / 4; i++)
987     {
988         val0_[i * 4]     = data[0 + offset * i];
989         val0_[i * 4 + 1] = data[1 + offset * i];
990         val0_[i * 4 + 2] = data[2 + offset * i];
991         val0_[i * 4 + 3] = data[3 + offset * i];
992     }
993
994     const Simd4NReal v0 = load<Simd4NReal>(val0_);
995     const Simd4NReal v1 = loadU4NOffset(data, offset);
996
997     GMX_EXPECT_SIMD_REAL_EQ(v0, v1);
998 }
999
1000 #    endif // GMX_SIMD_HAVE_4NSIMD_UTIL_REAL
1001
1002 #endif // GMX_SIMD_HAVE_REAL
1003
1004 /*! \} */
1005 /*! \endcond */
1006
1007 } // namespace
1008 } // namespace test
1009 } // namespace gmx