25#if defined(_MSC_VER) && \
26 (defined(_M_AMD64) || defined(_M_IX86) || defined(_M_ARM64) || defined(_M_ARM))
30#define DISPENSO_MAJOR_VERSION 1
31#define DISPENSO_MINOR_VERSION 6
32#define DISPENSO_PATCH_VERSION 2
42#if defined(_MSVC_LANG) && _MSVC_LANG > __cplusplus
43#define DISPENSO_CPLUSPLUS _MSVC_LANG
45#define DISPENSO_CPLUSPLUS __cplusplus
49#if DISPENSO_CPLUSPLUS >= 202002L && defined(__cpp_concepts) && __cpp_concepts >= 201907L
50#define DISPENSO_HAS_CONCEPTS 1
53#define DISPENSO_HAS_CONCEPTS 0
72#if DISPENSO_HAS_CONCEPTS
73#define DISPENSO_REQUIRES(...) requires(__VA_ARGS__)
75#define DISPENSO_REQUIRES(...)
89#if DISPENSO_CPLUSPLUS >= 201703L
90#define DISPENSO_DEPRECATED(msg) [[deprecated(msg)]]
92#define DISPENSO_DEPRECATED(msg)
95#if defined(DISPENSO_SHARED_LIB)
98#if defined(DISPENSO_LIB_EXPORT)
99#define DISPENSO_DLL_ACCESS __declspec(dllexport)
101#define DISPENSO_DLL_ACCESS __declspec(dllimport)
104#elif defined(__clang__) || defined(__GNUC__)
105#define DISPENSO_DLL_ACCESS __attribute__((visibility("default")))
109#if !defined(DISPENSO_DLL_ACCESS)
110#define DISPENSO_DLL_ACCESS
116#if defined(__clang__)
117#define DISPENSO_NO_THREAD_SAFETY_ANALYSIS __attribute__((no_thread_safety_analysis))
119#define DISPENSO_NO_THREAD_SAFETY_ANALYSIS
122using ssize_t = std::make_signed<std::size_t>::type;
124#if defined(__CUDACC__)
125#define DISPENSO_INLINE __host__ __device__ __forceinline__
126#elif defined(__clang__) || defined(__GNUC__)
127#define DISPENSO_INLINE __attribute__((always_inline)) inline
128#elif defined(_MSC_VER) || defined(__INTEL_COMPILER)
129#define DISPENSO_INLINE __forceinline
131#define DISPENSO_INLINE inline
138#if defined(__APPLE__) && defined(__arm64__)
156#define DISPENSO_CACHELINE_ALIGNED alignas(kCacheLineSize)
165#define DISPENSO_THREAD_LOCAL __declspec(thread)
166#elif defined(__GNUC__) || defined(__clang__)
167#define DISPENSO_THREAD_LOCAL __thread
169#error Supply lightweight thread-locals for this compiler. Can define to thread_local if lightweight not available
172#if (defined(__GNUC__) || defined(__clang__))
173#define DISPENSO_EXPECT(a, b) __builtin_expect(a, b)
175#define DISPENSO_EXPECT(a, b) a
179#if (defined(__GNUC__) || defined(__clang__))
180#define DISPENSO_DO_PRAGMA(X) _Pragma(#X)
181#define DISPENSO_DISABLE_WARNING_PUSH DISPENSO_DO_PRAGMA(GCC diagnostic push)
182#define DISPENSO_DISABLE_WARNING_POP DISPENSO_DO_PRAGMA(GCC diagnostic pop)
183#define DISPENSO_DISABLE_WARNING(warningName) DISPENSO_DO_PRAGMA(GCC diagnostic ignored #warningName)
184#if !defined(__clang__)
185#define DISPENSO_DISABLE_WARNING_ZERO_VARIADIC_MACRO_ARGUMENTS
186#define DISPENSO_DISABLE_WARNING_GLOBAL_CONSTRUCTORS
187#define DISPENSO_DISABLE_WARNING_FREE_NONHEAP_OBJECT \
188 DISPENSO_DISABLE_WARNING(-Wfree-nonheap-object)
190#define DISPENSO_DISABLE_WARNING_ZERO_VARIADIC_MACRO_ARGUMENTS \
191 DISPENSO_DISABLE_WARNING(-Wgnu-zero-variadic-macro-arguments)
192#define DISPENSO_DISABLE_WARNING_GLOBAL_CONSTRUCTORS \
193 DISPENSO_DISABLE_WARNING(-Wglobal-constructors)
194#define DISPENSO_DISABLE_WARNING_FREE_NONHEAP_OBJECT
196#elif defined(_MSC_VER)
197#define DISPENSO_DISABLE_WARNING_PUSH __pragma(warning(push))
198#define DISPENSO_DISABLE_WARNING_POP __pragma(warning(pop))
199#define DISPENSO_DISABLE_WARNING(warningNumber) __pragma(warning(disable : warningNumber))
200#define DISPENSO_DISABLE_WARNING_ZERO_VARIADIC_MACRO_ARGUMENTS
201#define DISPENSO_DISABLE_WARNING_GLOBAL_CONSTRUCTORS
202#define DISPENSO_DISABLE_WARNING_FREE_NONHEAP_OBJECT
204#define DISPENSO_DISABLE_WARNING_PUSH
205#define DISPENSO_DISABLE_WARNING_POP
206#define DISPENSO_DISABLE_WARNING_ZERO_VARIADIC_MACRO_ARGUMENTS
207#define DISPENSO_DISABLE_WARNING_GLOBAL_CONSTRUCTORS
208#define DISPENSO_DISABLE_WARNING_FREE_NONHEAP_OBJECT
234 operator const T&()
const {
246 alignas(
alignof(T))
char b[
sizeof(T)];
250struct alignas(kCacheLineSize)
AlignedAtomic :
public std::atomic<T*> {};
253inline void* alignedMalloc(
size_t bytes,
size_t alignment) {
254 alignment = std::max(alignment,
sizeof(uintptr_t));
255 if (bytes > ~
size_t{0} - alignment) {
258 char* ptr =
reinterpret_cast<char*
>(::malloc(bytes + alignment));
262 uintptr_t base =
reinterpret_cast<uintptr_t
>(ptr);
263 uintptr_t oldBase = base;
264 uintptr_t mask = alignment - 1;
268 uintptr_t* recovery =
reinterpret_cast<uintptr_t*
>(base -
sizeof(uintptr_t));
270 return reinterpret_cast<void*
>(base);
273inline void* alignedMalloc(
size_t bytes) {
274 return alignedMalloc(bytes, kCacheLineSize);
279inline void* alignedMallocOrThrow(
size_t bytes,
size_t alignment) {
280 void* ptr = alignedMalloc(bytes, alignment);
281 if (DISPENSO_EXPECT(ptr ==
nullptr,
false)) {
282#if defined(__cpp_exceptions)
283 throw std::bad_alloc();
291inline void* alignedMallocOrThrow(
size_t bytes) {
292 return alignedMallocOrThrow(bytes, kCacheLineSize);
295inline void alignedFree(
void* ptr) {
299 char* p =
reinterpret_cast<char*
>(ptr);
300 uintptr_t recovered = *
reinterpret_cast<uintptr_t*
>(p -
sizeof(uintptr_t));
301 ::free(
reinterpret_cast<void*
>(recovered));
311struct AlignedAllocator {
312 using value_type = T;
313 AlignedAllocator() noexcept = default;
314 template <typename U>
315 AlignedAllocator(const AlignedAllocator<U>&) noexcept {}
316 T* allocate(
size_t n) {
317 if (n > std::numeric_limits<size_t>::max() /
sizeof(T)) {
318#if defined(__cpp_exceptions)
319 throw std::bad_alloc();
324 return static_cast<T*
>(detail::alignedMallocOrThrow(n *
sizeof(T),
alignof(T)));
326 void deallocate(T* p,
size_t)
noexcept {
327 detail::alignedFree(p);
330template <
typename T,
typename U>
331bool operator==(
const AlignedAllocator<T>&,
const AlignedAllocator<U>&)
noexcept {
334template <
typename T,
typename U>
335bool operator!=(
const AlignedAllocator<T>&,
const AlignedAllocator<U>&)
noexcept {
340struct AlignedFreeDeleter {
341 void operator()(T* ptr) {
343 detail::alignedFree(ptr);
347struct AlignedFreeDeleter<void> {
348 void operator()(
void* ptr) {
349 detail::alignedFree(ptr);
356struct AlignedArrayFreeDeleter {
358 void operator()(T* ptr) {
359 for (
size_t i = 0; i < count; ++i) {
362 detail::alignedFree(ptr);
369std::unique_ptr<T[], AlignedArrayFreeDeleter<T>> makeAlignedArray(
size_t n) {
370 void* raw = detail::alignedMallocOrThrow(
sizeof(T) * n,
alignof(T));
371 T* arr =
static_cast<T*
>(raw);
372 for (
size_t i = 0; i < n; ++i) {
375 return std::unique_ptr<T[], AlignedArrayFreeDeleter<T>>(arr, AlignedArrayFreeDeleter<T>{n});
379template <
typename T,
class... Args>
380std::unique_ptr<T, AlignedFreeDeleter<T>> makeAligned(Args&&... args) {
381 void* raw = detail::alignedMallocOrThrow(
sizeof(T),
alignof(T));
382 T* obj =
new (raw) T(std::forward<Args>(args)...);
383 return std::unique_ptr<T, AlignedFreeDeleter<T>>(obj);
386template <
typename T,
class... Args>
387std::shared_ptr<T> make_shared(Args&&... args) {
388 void* tv = alignedMallocOrThrow(
sizeof(T),
alignof(T));
389 T* t =
new (tv) T(std::forward<Args>(args)...);
390 return std::shared_ptr<T>(t, AlignedFreeDeleter<T>());
393inline constexpr uintptr_t alignToCacheLine(uintptr_t val) {
400#if defined __x86_64__ || defined __i386__
401inline void cpuRelax() {
402 asm volatile(
"pause" :::
"memory");
404#elif defined _MSC_VER && (defined _M_AMD64 || defined _M_IX86)
405inline void cpuRelax() {
408#elif defined __arm64__ || defined __aarch64__
409inline void cpuRelax() {
410 asm volatile(
"yield" :::
"memory");
412#elif defined _MSC_VER && (defined _M_ARM64 || defined _M_ARM)
413inline void cpuRelax() {
416#elif defined __powerpc__ || defined __POWERPC__
418inline void cpuRelax() {
419 asm volatile(
"or r27,r27,r27" :::
"memory");
422inline void cpuRelax() {
423 asm volatile(
"or 27,27,27" :::
"memory");
428inline void cpuRelax() {}
438 ssize_t transitionTaskIndex;
439 ssize_t ceilChunkSize;
442inline StaticChunking staticChunkSize(ssize_t items, ssize_t chunks) {
445 chunking.ceilChunkSize = (items + chunks - 1) / chunks;
446 ssize_t numLeft = chunking.ceilChunkSize * chunks - items;
447 chunking.transitionTaskIndex = chunks - numLeft;
457inline StaticChunking staticChunkSizeGranular(ssize_t items, ssize_t chunks, uint32_t granularity) {
459 assert(granularity >= 1);
460 if (granularity <= 1) {
461 return staticChunkSize(items, chunks);
463 assert(items %
static_cast<ssize_t
>(granularity) == 0);
466 ssize_t gUnits = items /
static_cast<ssize_t
>(granularity);
467 ssize_t ceilG = (gUnits + chunks - 1) / chunks;
468 ssize_t numLeft = ceilG * chunks - gUnits;
469 chunking.ceilChunkSize = ceilG *
static_cast<ssize_t
>(granularity);
470 chunking.transitionTaskIndex = chunks - numLeft;
detail::AlignedAtomic< T > AlignedAtomic
Cache-line aligned atomic pointer.
detail::AlignedBuffer< T > AlignedBuffer
Buffer with proper alignment for type T.
detail::StaticChunking StaticChunking
Information for statically chunking a range across threads.