1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240
|
#ifndef ONEFLOW_CORE_CUDA_ELEMENTWISE_H_ #define ONEFLOW_CORE_CUDA_ELEMENTWISE_H_
#include <cuda_runtime.h> #include <cstdint> #include <algorithm> #include <type_traits>
namespace oneflow {
namespace cuda {
namespace elementwise {
constexpr int kBlockSize = 256; constexpr int kNumWaves = 32;
inline cudaError_t GetNumBlocks(int64_t n, int* num_blocks) { int dev; { cudaError_t err = cudaGetDevice(&dev); if (err != cudaSuccess) { return err; } } int sm_count; { cudaError_t err = cudaDeviceGetAttribute(&sm_count, cudaDevAttrMultiProcessorCount, dev); if (err != cudaSuccess) { return err; } } int tpm; { cudaError_t err = cudaDeviceGetAttribute(&tpm, cudaDevAttrMaxThreadsPerMultiProcessor, dev); if (err != cudaSuccess) { return err; } } *num_blocks = std::max<int>(1, std::min<int64_t>((n + kBlockSize - 1) / kBlockSize, sm_count * tpm / kBlockSize * kNumWaves)); return cudaSuccess; }
template<typename T, int pack_size> struct GetPackType { using type = typename std::aligned_storage<pack_size * sizeof(T), pack_size * sizeof(T)>::type; };
template<typename T, int pack_size> using PackType = typename GetPackType<T, pack_size>::type;
template<typename T, int pack_size> union Pack { static_assert(sizeof(PackType<T, pack_size>) == sizeof(T) * pack_size, ""); __device__ Pack() { } PackType<T, pack_size> storage; T elem[pack_size]; };
template<typename T, int pack_size> struct alignas(sizeof(T) * pack_size) Packed { __device__ Packed() { } union { T elem[pack_size]; }; };
constexpr int kMaxPackBytes = 128 / 8; constexpr int kMaxPackSize = 8;
constexpr int Min(int a, int b) { return a < b ? a : b; }
template<typename T> constexpr int PackSize() { return Min(kMaxPackBytes / sizeof(T), kMaxPackSize); }
template<typename T, typename U, typename... Args> constexpr int PackSize() { return Min(PackSize<T>(), PackSize<U, Args...>()); }
template<typename T> class HasApply2 { typedef char one; struct two { char x[2]; };
template<typename C> static one test(decltype(&C::Apply2)); template<typename C> static two test(...);
public: enum { value = sizeof(test<T>(0)) == sizeof(char) }; };
template<int pack_size, typename FunctorT, typename R, typename... IN> __device__ typename std::enable_if<HasApply2<FunctorT>::value == true && pack_size % 2 == 0, Packed<R, pack_size>>::type ApplyPack(const FunctorT& functor, const Packed<IN, pack_size>... in) { Packed<R, pack_size> ret; #pragma unroll for (int j = 0; j < pack_size; j += 2) { functor.Apply2(ret.elem + j, (in.elem + j)...); } return ret; }
template<int pack_size, typename FunctorT, typename R, typename... IN> __device__ typename std::enable_if<HasApply2<FunctorT>::value == false || pack_size % 2 != 0, Packed<R, pack_size>>::type ApplyPack(const FunctorT& functor, const Packed<IN, pack_size>... in) { Packed<R, pack_size> ret; #pragma unroll for (int j = 0; j < pack_size; ++j) { ret.elem[j] = functor((in.elem[j])...); } return ret; }
template<int pack_size, typename FactoryT, typename R, typename... IN> __global__ void __launch_bounds__(kBlockSize) ApplyGeneric(FactoryT factory, int64_t n_pack, Packed<R, pack_size>* pack_r, const Packed<IN, pack_size>*... pack_in, int64_t n_tail, R* tail_r, const IN*... tail_in) { auto functor = factory(); const int global_tid = blockIdx.x * kBlockSize + threadIdx.x; for (int64_t i = global_tid; i < n_pack; i += blockDim.x * gridDim.x) { pack_r[i] = ApplyPack<pack_size, decltype(functor), R, IN...>(functor, (pack_in[i])...); } if (global_tid < n_tail) { tail_r[global_tid] = functor((tail_in[global_tid])...); } }
template<typename FunctorT> struct SimpleFactory { explicit SimpleFactory(FunctorT functor) : tpl(functor) {} __device__ FunctorT operator()() const { return tpl; }
private: FunctorT tpl; };
template<size_t pack_size> bool IsAlignedForPack() { return true; }
template<size_t pack_size, typename T, typename... Args> bool IsAlignedForPack(const T* ptr, const Args*... others) { return reinterpret_cast<uintptr_t>(ptr) % sizeof(Pack<T, pack_size>) == 0 && IsAlignedForPack<pack_size, Args...>(others...); }
template<size_t pack_size, typename FactoryT, typename R, typename... IN> cudaError_t LaunchKernel(FactoryT factory, int64_t n, R* r, const IN*... in, cudaStream_t stream) { const int64_t n_pack = n / pack_size; const int64_t tail_offset = n_pack * pack_size; const int64_t n_tail = n - tail_offset; int num_blocks; { cudaError_t err = GetNumBlocks(n_pack, &num_blocks); if (err != cudaSuccess) { return err; } } ApplyGeneric<pack_size, FactoryT, R, IN...><<<num_blocks, kBlockSize, 0, stream>>>( factory, n_pack, reinterpret_cast<Packed<R, pack_size>*>(r), (reinterpret_cast<const Packed<IN, pack_size>*>(in))..., n_tail, r + tail_offset, (in + tail_offset)...); return cudaPeekAtLastError(); }
template<typename FactoryT, typename R, typename... IN> struct GenericLauncher { static cudaError_t Launch(FactoryT factory, int64_t n, R* r, const IN*... in, cudaStream_t stream) { constexpr int max_pack_size = PackSize<R, IN...>(); if (IsAlignedForPack<max_pack_size, R, IN...>(r, in...)) { return LaunchKernel<max_pack_size, FactoryT, R, IN...>(factory, n, r, in..., stream); } else { return LaunchKernel<1, FactoryT, R, IN...>(factory, n, r, in..., stream); } } };
template<typename FactoryT, typename R, typename A> inline cudaError_t UnaryWithFactory(FactoryT factory, int64_t n, R* r, const A* a, cudaStream_t stream) { return GenericLauncher<FactoryT, R, A>::Launch(factory, n, r, a, stream); }
template<typename FunctorT, typename R, typename A> inline cudaError_t Unary(FunctorT functor, int64_t n, R* r, const A* a, cudaStream_t stream) { return UnaryWithFactory(SimpleFactory<FunctorT>(functor), n, r, a, stream); }
template<typename FactoryT, typename R, typename A, typename B> inline cudaError_t BinaryWithFactory(FactoryT factory, int64_t n, R* r, const A* a, const B* b, cudaStream_t stream) { return GenericLauncher<FactoryT, R, A, B>::Launch(factory, n, r, a, b, stream); }
template<typename FunctorT, typename R, typename A, typename B> inline cudaError_t Binary(FunctorT functor, int64_t n, R* r, const A* a, const B* b, cudaStream_t stream) { return BinaryWithFactory(SimpleFactory<FunctorT>(functor), n, r, a, b, stream); }
template<typename FactoryT, typename R, typename A, typename B, typename C> inline cudaError_t TernaryWithFactory(FactoryT factory, int64_t n, R* r, const A* a, const B* b, const C* c, cudaStream_t stream) { return GenericLauncher<FactoryT, R, A, B, C>::Launch(factory, n, r, a, b, c, stream); }
template<typename FunctorT, typename R, typename A, typename B, typename C> inline cudaError_t Ternary(FunctorT functor, int64_t n, R* r, const A* a, const B* b, const C* c, cudaStream_t stream) { return TernaryWithFactory(SimpleFactory<FunctorT>(functor), n, r, a, b, c, stream); }
}
}
}
#endif
|