Skip to content

Commit 28be6c1

Browse files
committed
make aligned accessor example more in line with standard c++
1 parent beb780b commit 28be6c1

4 files changed

Lines changed: 69 additions & 109 deletions

File tree

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,9 @@
11
mdspan_add_example(aligned_accessor)
2+
3+
if(MDSPAN_ENABLE_OPENMP)
4+
if(OpenMP_CXX_FOUND)
5+
target_link_libraries(aligned_accessor OpenMP::OpenMP_CXX)
6+
target_compile_definitions(aligned_accessor MDSPAN_ENABLE_OPENMP)
7+
message(STATUS "Building aligned accessor example with OpenMP")
8+
endif()
9+
endif()

examples/aligned_accessor/aligned_accessor.cpp

Lines changed: 18 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -29,37 +29,15 @@
2929
#include <memory>
3030
#include <type_traits>
3131
#include <string> // stoi
32+
#ifdef MDSPAN_ENABLE_OPENMP
33+
#include <omp.h>
34+
#endif
3235

3336
// mfh 2022/08/08: This is based on my comment on RAPIDS RAFT issue 725:
3437
// https://github.com/rapidsai/raft/pull/725#discussion_r937991701
3538

3639
namespace {
3740
using Kokkos::aligned_accessor;
38-
using Kokkos::detail::assume_aligned_method;
39-
using Kokkos::detail::align_attribute_method;
40-
41-
// We define aligned_pointer_t through a struct
42-
// so we can check whether the byte alignment is valid.
43-
// This makes it impossible to use the alias
44-
// with an invalid byte alignment.
45-
template<class ElementType, std::size_t byte_alignment>
46-
struct aligned_pointer {
47-
#if defined(__ICC)
48-
// x86-64 ICC 2021.5.0 emits warning #3186 ("expected typedef declaration") here.
49-
// No other compiler (including Clang, which has a similar type attribute) has this issue.
50-
# pragma warning push
51-
# pragma warning disable 3186
52-
#endif
53-
54-
using type = ElementType* MDSPAN_IMPL_ALIGN_VALUE_ATTRIBUTE( byte_alignment );
55-
56-
#if defined(__ICC)
57-
# pragma warning pop
58-
#endif
59-
};
60-
61-
template<class ElementType, std::size_t byte_alignment>
62-
using aligned_pointer_t = typename aligned_pointer<ElementType, byte_alignment>::type;
6341

6442
using test_value_type = float;
6543
constexpr std::size_t min_overalignment_factor = 8;
@@ -69,13 +47,6 @@ constexpr std::size_t min_byte_alignment = min_overalignment_factor * sizeof(flo
6947
// Some compilers have trouble optimizing loops with unsigned or 64-bit index types.
7048
using index_type = int;
7149

72-
template<class ElementType, std::size_t byte_alignment>
73-
aligned_pointer_t<ElementType, byte_alignment>
74-
bless(ElementType* ptr, std::integral_constant<std::size_t, byte_alignment> /* ba */ )
75-
{
76-
return MDSPAN_IMPL_ASSUME_ALIGNED( ElementType, ptr, byte_alignment );
77-
}
78-
7950
template<class ElementType>
8051
struct delete_raw {
8152
void operator()(ElementType* p) const {
@@ -155,14 +126,14 @@ class aligned_array_allocation {
155126
num_elements(number_of_elements)
156127
{}
157128

158-
aligned_pointer_t<ElementType, byte_alignment> data() const
129+
ElementType *data() const
159130
{
160-
return MDSPAN_IMPL_ASSUME_ALIGNED( ElementType, pointer, byte_alignment );
131+
return Kokkos::assume_aligned< byte_alignment >(pointer);
161132
}
162133

163134
private:
164135
allocation_t<ElementType> allocation{nullptr, delete_raw<ElementType>{}};
165-
aligned_pointer_t<ElementType, byte_alignment> pointer{nullptr};
136+
ElementType *pointer{nullptr};
166137
std::size_t num_elements{0};
167138
};
168139

@@ -321,9 +292,9 @@ auto benchmark_add_raw_1d(const std::size_t num_trials,
321292
// Assume that x, y, and z all have the same alignment.
322293
template<class ElementType, std::size_t byte_alignment>
323294
void add_aligned_raw_1d(const index_type n,
324-
aligned_pointer_t<const ElementType, byte_alignment> x,
325-
aligned_pointer_t<const ElementType, byte_alignment> y,
326-
aligned_pointer_t<ElementType, byte_alignment> z)
295+
const ElementType * MDSPAN_ALIGN(byte_alignment) x,
296+
const ElementType * MDSPAN_ALIGN(byte_alignment) y,
297+
ElementType * MDSPAN_ALIGN(byte_alignment) z)
327298
{
328299
for (index_type i = 0; i < n; ++i) {
329300
z[i] = x[i] + y[i];
@@ -337,19 +308,19 @@ auto benchmark_add_aligned_raw_1d(const std::size_t num_trials,
337308
const ElementType x[],
338309
const ElementType y[],
339310
ElementType z[],
340-
std::integral_constant<std::size_t, byte_alignment> ba)
311+
std::integral_constant<std::size_t, byte_alignment>)
341312
{
342313
TICK();
343-
auto x_blessed = bless(x, ba);
344-
auto y_blessed = bless(y, ba);
345-
auto z_blessed = bless(z, ba);
314+
auto x_blessed = Kokkos::assume_aligned< byte_alignment >(x);
315+
auto y_blessed = Kokkos::assume_aligned< byte_alignment >(y);
316+
auto z_blessed = Kokkos::assume_aligned< byte_alignment >(z);
346317
for (std::size_t trial = 0; trial < num_trials; ++trial) {
347318
add_aligned_raw_1d<ElementType, byte_alignment>(n, x_blessed, y_blessed, z_blessed);
348319
}
349320
return TOCK();
350321
}
351322

352-
#ifdef _OPENMP
323+
#ifdef MDSPAN_ENABLE_OPENMP
353324
template<class ElementType, std::size_t byte_alignment>
354325
void add_omp_simd_aligned_mdspan_1d(aligned_mdspan_1d<const ElementType, byte_alignment> x,
355326
aligned_mdspan_1d<const ElementType, byte_alignment> y,
@@ -533,7 +504,7 @@ auto benchmark_add_omp_aligned_simd_aligned_raw_1d(
533504
}
534505
return TOCK();
535506
}
536-
#endif // _OPENMP
507+
#endif // MDSPAN_ENABLE_OPENMP
537508

538509
template<class ElementType>
539510
void set_elements_of_arrays(const index_type n,
@@ -578,7 +549,7 @@ int main(int argc, char* argv[])
578549
benchmark_add_aligned_raw_1d(num_trials, n, x_aligned.data(),
579550
y_aligned.data(), z_aligned.data(),
580551
byte_alignment);
581-
#ifdef _OPENMP
552+
#ifdef MDSPAN_ENABLE_OPENMP
582553
auto omp_simd_aligned_mdspan_result =
583554
benchmark_add_omp_simd_aligned_mdspan_1d(num_trials, n, x_aligned.data(),
584555
y_aligned.data(), z_aligned.data(),
@@ -609,7 +580,7 @@ int main(int argc, char* argv[])
609580
benchmark_add_raw_1d(num_trials, n, x_unaligned.data(),
610581
y_unaligned.data(), z_unaligned.data());
611582

612-
#ifdef _OPENMP
583+
#ifdef MDSPAN_ENABLE_OPENMP
613584
auto omp_simd_mdspan_result =
614585
benchmark_add_omp_simd_mdspan_1d(num_trials, n, x_unaligned.data(),
615586
y_unaligned.data(), z_unaligned.data());
@@ -620,17 +591,13 @@ int main(int argc, char* argv[])
620591

621592
cout << "Number of trials: " << num_trials << endl
622593
<< "Number of loop iterations per trial: " << n << endl
623-
<< "Way to declare a pointer value aligned, if any: "
624-
<< assume_aligned_method << endl
625-
<< "Way to declare a pointer type aligned, if any: "
626-
<< align_attribute_method << endl
627594
<< "Total time in seconds for non-OpenMP loops:" << endl
628595
<< " aligned mdspan: " << aligned_mdspan_result << endl
629596
<< " unaligned mdspan: " << mdspan_result << endl
630597
<< " aligned raw: " << aligned_raw_result << endl
631598
<< " unaligned raw: " << raw_result << endl;
632599

633-
#ifdef _OPENMP
600+
#ifdef MDSPAN_ENABLE_OPENMP
634601
cout << "Total time in seconds for OpenMP (omp simd) loops:" << endl
635602
<< " omp_simd_aligned_mdspan: " << omp_simd_aligned_mdspan_result << endl
636603
<< " omp_simd_mdspan: " << omp_simd_mdspan_result << endl

include/experimental/__p0009_bits/aligned_accessor.hpp

Lines changed: 28 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -60,50 +60,22 @@
6060
#include <cstring>
6161
#endif
6262

63-
namespace MDSPAN_IMPL_STANDARD_NAMESPACE {
64-
namespace detail {
65-
66-
// Prefer std::assume_aligned if available, as it is in the C++ Standard.
67-
// Otherwise, use a compiler-specific equivalent if available.
68-
69-
// NOTE (mfh 2022/08/08) BYTE_ALIGNMENT must be unsigned and a power of 2.
70-
#if defined(__cpp_lib_assume_aligned)
71-
# define MDSPAN_IMPL_ASSUME_ALIGNED( ELEMENT_TYPE, POINTER, BYTE_ALIGNMENT ) (std::assume_aligned< BYTE_ALIGNMENT >( POINTER ))
72-
constexpr char assume_aligned_method[] = "std::assume_aligned";
73-
#elif defined(__ICL)
74-
# define MDSPAN_IMPL_ASSUME_ALIGNED( ELEMENT_TYPE, POINTER, BYTE_ALIGNMENT ) POINTER
75-
constexpr char assume_aligned_method[] = "(none)";
76-
#elif defined(__ICC)
77-
# define MDSPAN_IMPL_ASSUME_ALIGNED( ELEMENT_TYPE, POINTER, BYTE_ALIGNMENT ) POINTER
78-
constexpr char assume_aligned_method[] = "(none)";
79-
#elif defined(__clang__)
80-
# define MDSPAN_IMPL_ASSUME_ALIGNED( ELEMENT_TYPE, POINTER, BYTE_ALIGNMENT ) POINTER
81-
constexpr char assume_aligned_method[] = "(none)";
82-
#elif defined(__GNUC__)
83-
// __builtin_assume_aligned returns void*
84-
# define MDSPAN_IMPL_ASSUME_ALIGNED( ELEMENT_TYPE, POINTER, BYTE_ALIGNMENT ) reinterpret_cast< ELEMENT_TYPE* >(__builtin_assume_aligned( POINTER, BYTE_ALIGNMENT ))
85-
constexpr char assume_aligned_method[] = "__builtin_assume_aligned";
86-
#else
87-
# define MDSPAN_IMPL_ASSUME_ALIGNED( ELEMENT_TYPE, POINTER, BYTE_ALIGNMENT ) POINTER
88-
constexpr char assume_aligned_method[] = "(none)";
89-
#endif
90-
9163
// Some compilers other than Clang or GCC like to define __clang__ or __GNUC__.
9264
// Thus, we order the tests from most to least specific.
9365
#if defined(__ICL)
94-
# define MDSPAN_IMPL_ALIGN_VALUE_ATTRIBUTE( BYTE_ALIGNMENT ) __declspec(align_value( BYTE_ALIGNMENT ))
95-
constexpr char align_attribute_method[] = "__declspec(align_value(BYTE_ALIGNMENT))";
66+
#define MDSPAN_ALIGN(BYTE_ALIGNMENT) __declspec(align_value(BYTE_ALIGNMENT))
9667
#elif defined(__ICC)
97-
# define MDSPAN_IMPL_ALIGN_VALUE_ATTRIBUTE( BYTE_ALIGNMENT ) __attribute__((align_value( BYTE_ALIGNMENT )))
98-
constexpr char align_attribute_method[] = "__attribute__((align_value(BYTE_ALIGNMENT)))";
68+
#define MDSPAN_ALIGN(BYTE_ALIGNMENT) \
69+
__attribute__((align_value(BYTE_ALIGNMENT)))
9970
#elif defined(__clang__)
100-
# define MDSPAN_IMPL_ALIGN_VALUE_ATTRIBUTE( BYTE_ALIGNMENT ) __attribute__((align_value( BYTE_ALIGNMENT )))
101-
constexpr char align_attribute_method[] = "__attribute__((align_value(BYTE_ALIGNMENT)))";
71+
#define MDSPAN_ALIGN(BYTE_ALIGNMENT) \
72+
__attribute__((align_value(BYTE_ALIGNMENT)))
10273
#else
103-
# define MDSPAN_IMPL_ALIGN_VALUE_ATTRIBUTE( BYTE_ALIGNMENT )
104-
constexpr char align_attribute_method[] = "(none)";
74+
#define MDSPAN_ALIGN(BYTE_ALIGNMENT)
10575
#endif
10676

77+
namespace MDSPAN_IMPL_STANDARD_NAMESPACE {
78+
namespace detail {
10779
constexpr bool
10880
has_single_bit(const std::size_t x)
10981
{
@@ -117,6 +89,24 @@ has_single_bit(const std::size_t x)
11789
}
11890
} // namespace detail
11991

92+
#ifdef __cpp_lib_assume_aligned
93+
using std::assume_aligned;
94+
#elif defined(__GNUC__)
95+
template <std::size_t ByteAlignment, class T>
96+
constexpr T *assume_aligned(T *ptr) {
97+
static_assert(detail::has_single_bit(Alignment),
98+
"Alignment must be a power of two.");
99+
return reinterpret_cast<T *>(__builtin_assume_aligned(ptr, ByteAlignment));
100+
}
101+
#else
102+
template <std::size_t ByteAlignment, class T>
103+
constexpr T *assume_aligned(T *ptr) {
104+
static_assert(detail::has_single_bit(Alignment),
105+
"Alignment must be a power of two.");
106+
return ptr;
107+
}
108+
#endif
109+
120110
template<size_t Alignment, class T>
121111
#ifdef __cpp_lib_bit_cast // Only can be constexpr if we have bit_cast
122112
constexpr
@@ -139,7 +129,7 @@ struct aligned_accessor {
139129
using offset_policy = default_accessor<ElementType>;
140130
using element_type = ElementType;
141131
using reference = ElementType&;
142-
using data_handle_type = ElementType* MDSPAN_IMPL_ALIGN_VALUE_ATTRIBUTE( ByteAlignment );
132+
using data_handle_type = ElementType* MDSPAN_ALIGN( ByteAlignment );
143133

144134
static constexpr size_t byte_alignment = ByteAlignment;
145135
static_assert(detail::has_single_bit(byte_alignment) && byte_alignment >= alignof(ElementType),
@@ -172,7 +162,7 @@ struct aligned_accessor {
172162
constexpr reference access(data_handle_type p, size_t i) const noexcept {
173163
// This may declare alignment twice, depending on
174164
// if we have an attribute for marking pointer types.
175-
return MDSPAN_IMPL_ASSUME_ALIGNED( ElementType, p, byte_alignment )[i];
165+
return assume_aligned<byte_alignment>(p)[i];
176166
}
177167

178168
constexpr typename offset_policy::data_handle_type

tests/test_aligned_accessor.cpp

Lines changed: 15 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -5,28 +5,21 @@
55
#include <cstdlib>
66

77
TEST(TestAlignedAccessor, IsSufficientlyAligned) {
8+
alignas(4) const char dummy_arr[16] = {};
89
ASSERT_TRUE(
9-
Kokkos::is_sufficiently_aligned<1>(reinterpret_cast<char *>(0x12345678)));
10+
Kokkos::is_sufficiently_aligned<1>(&dummy_arr[1]));
1011
ASSERT_TRUE(
11-
Kokkos::is_sufficiently_aligned<1>(reinterpret_cast<char *>(0x12345671)));
12-
13-
ASSERT_TRUE(Kokkos::is_sufficiently_aligned<2>(
14-
reinterpret_cast<std::int16_t *>(0x12345678)));
15-
ASSERT_TRUE(!Kokkos::is_sufficiently_aligned<2>(
16-
reinterpret_cast<std::int16_t *>(0x12345671)));
17-
18-
ASSERT_TRUE(Kokkos::is_sufficiently_aligned<4>(
19-
reinterpret_cast<std::int32_t *>(0x12345678)));
20-
ASSERT_TRUE(Kokkos::is_sufficiently_aligned<4>(
21-
reinterpret_cast<std::int32_t *>(0x12345674)));
22-
ASSERT_TRUE(Kokkos::is_sufficiently_aligned<4>(
23-
reinterpret_cast<std::int32_t *>(0x1234567c)));
24-
ASSERT_TRUE(!Kokkos::is_sufficiently_aligned<4>(
25-
reinterpret_cast<std::int32_t *>(0x12345672)));
26-
ASSERT_TRUE(!Kokkos::is_sufficiently_aligned<4>(
27-
reinterpret_cast<std::int32_t *>(0x12345671)));
28-
ASSERT_TRUE(!Kokkos::is_sufficiently_aligned<4>(
29-
reinterpret_cast<std::int32_t *>(0x12345677)));
12+
Kokkos::is_sufficiently_aligned<1>(&dummy_arr[1]));
13+
14+
ASSERT_TRUE(Kokkos::is_sufficiently_aligned<2>(&dummy_arr[0]));
15+
ASSERT_TRUE(!Kokkos::is_sufficiently_aligned<2>(&dummy_arr[1]));
16+
17+
ASSERT_TRUE(Kokkos::is_sufficiently_aligned<4>(&dummy_arr[0]));
18+
ASSERT_TRUE(Kokkos::is_sufficiently_aligned<4>(&dummy_arr[4]));
19+
ASSERT_TRUE(Kokkos::is_sufficiently_aligned<4>(&dummy_arr[8]));
20+
ASSERT_TRUE(!Kokkos::is_sufficiently_aligned<4>(&dummy_arr[1]));
21+
ASSERT_TRUE(!Kokkos::is_sufficiently_aligned<4>(&dummy_arr[2]));
22+
ASSERT_TRUE(!Kokkos::is_sufficiently_aligned<4>(&dummy_arr[3]));
3023
}
3124

3225
// These shouldn't be in the global namespace or they may replace the C version
@@ -66,6 +59,8 @@ namespace testing
6659
return *this;
6760
}
6861
};
62+
63+
static_assert(sizeof(different_align_and_size) != alignof(different_align_and_size));
6964
}
7065

7166
template <typename T, std::size_t ByteAlignment, std::size_t NumElements>

0 commit comments

Comments
 (0)