Deepity
Ultra-fast, SIMD-accelerated Predictive Coding
Loading...
Searching...
No Matches
Relu.h
Go to the documentation of this file.
1#pragma once
2#include <cassert>
3#include <cstddef>
4#include <immintrin.h>
5#include <omp.h>
7
10#ifndef MAX
11#define MAX(a, b) (((a) > (b)) ? (a) : (b))
12#endif
13
19
20namespace Deep
21{
25 static inline void relu(float *RESTRICT x, const size_t n) noexcept
26 {
27 assert(n != 0 && "n must not be 0.");
28 assert(x != nullptr && "x must not be null.");
29
30 size_t simd_end = 0;
31
32#if defined(__AVX512F__)
33 const __m512 zeros512 = _mm512_setzero_ps();
34 simd_end = n - (n % 16);
35
36 for (size_t i = 0; i < simd_end; i += 16)
37 {
38 __m512 xi = _mm512_loadu_ps(x + i);
39 xi = _mm512_max_ps(zeros512, xi);
40 _mm512_storeu_ps(x + i, xi);
41 }
42
43#elif defined(__AVX2__) || defined(__AVX__)
44 const __m256 zeros256 = _mm256_setzero_ps();
45 simd_end = n - (n % 8);
46
47 for (size_t i = 0; i < simd_end; i += 8)
48 {
49 __m256 xi = _mm256_loadu_ps(x + i);
50 xi = _mm256_max_ps(zeros256, xi);
51 _mm256_storeu_ps(x + i, xi);
52 }
53
54#elif defined(__SSE__) || defined(_M_AMD64) || defined(_M_X64)
55 const __m128 zeros128 = _mm_setzero_ps();
56 simd_end = n - (n % 4);
57
58 for (size_t i = 0; i < simd_end; i += 4)
59 {
60 __m128 xi = _mm_loadu_ps(x + i);
61 xi = _mm_max_ps(zeros128, xi);
62 _mm_storeu_ps(x + i, xi);
63 }
64#endif
65
66 for (size_t i = simd_end; i < n; i++)
67 {
68 x[i] = MAX(0.0f, x[i]);
69 }
70 }
71
78 static inline void dRelu(float *RESTRICT x, const size_t n, [[maybe_unused]] bool activated = false) noexcept
79 {
80 assert(n != 0 && "n must not be 0.");
81 assert(x != nullptr && "x must not be null.");
82
83 size_t simd_end = 0;
84
85#if defined(__AVX512F__)
86 __m512 ones = _mm512_set1_ps(1.0f);
87 __m512 zeros = _mm512_setzero_ps();
88 size_t simd_end4 = n - (n % 64);
89
90#pragma omp parallel for schedule(static) if (n > 65536 && !omp_in_parallel())
91 for (ptrdiff_t i = 0; i < (ptrdiff_t)(simd_end4); i += 64)
92 {
93 __m512 x0 = _mm512_loadu_ps(x + i);
94 __m512 x1 = _mm512_loadu_ps(x + i + 16);
95 __m512 x2 = _mm512_loadu_ps(x + i + 32);
96 __m512 x3 = _mm512_loadu_ps(x + i + 48);
97
98 __mmask16 m0 = _mm512_cmp_ps_mask(x0, zeros, _CMP_GT_OQ);
99 __mmask16 m1 = _mm512_cmp_ps_mask(x1, zeros, _CMP_GT_OQ);
100 __mmask16 m2 = _mm512_cmp_ps_mask(x2, zeros, _CMP_GT_OQ);
101 __mmask16 m3 = _mm512_cmp_ps_mask(x3, zeros, _CMP_GT_OQ);
102
103 _mm512_storeu_ps(x + i, _mm512_mask_blend_ps(m0, zeros, ones));
104 _mm512_storeu_ps(x + i + 16, _mm512_mask_blend_ps(m1, zeros, ones));
105 _mm512_storeu_ps(x + i + 32, _mm512_mask_blend_ps(m2, zeros, ones));
106 _mm512_storeu_ps(x + i + 48, _mm512_mask_blend_ps(m3, zeros, ones));
107 }
108
109 simd_end = n - (n % 16);
110 for (size_t i = simd_end4; i < simd_end; i += 16)
111 {
112 __m512 x0 = _mm512_loadu_ps(x + i);
113 __mmask16 m0 = _mm512_cmp_ps_mask(x0, zeros, _CMP_GT_OQ);
114 _mm512_storeu_ps(x + i, _mm512_mask_blend_ps(m0, zeros, ones));
115 }
116
117#elif defined(__AVX2__) || defined(__AVX__)
118 __m256 ones = _mm256_set1_ps(1.0f);
119 __m256 zeros = _mm256_setzero_ps();
120 size_t simd_end4 = n - (n % 32);
121
122#pragma omp parallel for schedule(static) if (n > 65536 && !omp_in_parallel())
123 for (ptrdiff_t i = 0; i < (ptrdiff_t)(simd_end4); i += 32)
124 {
125 __m256 x0 = _mm256_loadu_ps(x + i);
126 __m256 x1 = _mm256_loadu_ps(x + i + 8);
127 __m256 x2 = _mm256_loadu_ps(x + i + 16);
128 __m256 x3 = _mm256_loadu_ps(x + i + 24);
129
130 x0 = _mm256_and_ps(ones, _mm256_cmp_ps(x0, zeros, _CMP_GT_OQ));
131 x1 = _mm256_and_ps(ones, _mm256_cmp_ps(x1, zeros, _CMP_GT_OQ));
132 x2 = _mm256_and_ps(ones, _mm256_cmp_ps(x2, zeros, _CMP_GT_OQ));
133 x3 = _mm256_and_ps(ones, _mm256_cmp_ps(x3, zeros, _CMP_GT_OQ));
134
135 _mm256_storeu_ps(x + i, x0);
136 _mm256_storeu_ps(x + i + 8, x1);
137 _mm256_storeu_ps(x + i + 16, x2);
138 _mm256_storeu_ps(x + i + 24, x3);
139 }
140
141 simd_end = n - (n % 8);
142 for (size_t i = simd_end4; i < simd_end; i += 8)
143 {
144 __m256 x0 = _mm256_loadu_ps(x + i);
145 _mm256_storeu_ps(x + i, _mm256_and_ps(ones, _mm256_cmp_ps(x0, zeros, _CMP_GT_OQ)));
146 }
147
148#elif defined(__SSE__) || defined(_M_AMD64) || defined(_M_X64)
149 __m128 ones = _mm_set1_ps(1.0f);
150 __m128 zeros = _mm_setzero_ps();
151 size_t simd_end4 = n - (n % 16);
152
153#pragma omp parallel for schedule(static) if (n > 65536 && !omp_in_parallel())
154 for (ptrdiff_t i = 0; i < (ptrdiff_t)(simd_end4); i += 16)
155 {
156 __m128 x0 = _mm_loadu_ps(x + i);
157 __m128 x1 = _mm_loadu_ps(x + i + 4);
158 __m128 x2 = _mm_loadu_ps(x + i + 8);
159 __m128 x3 = _mm_loadu_ps(x + i + 12);
160
161 x0 = _mm_and_ps(ones, _mm_cmpgt_ps(x0, zeros));
162 x1 = _mm_and_ps(ones, _mm_cmpgt_ps(x1, zeros));
163 x2 = _mm_and_ps(ones, _mm_cmpgt_ps(x2, zeros));
164 x3 = _mm_and_ps(ones, _mm_cmpgt_ps(x3, zeros));
165
166 _mm_storeu_ps(x + i, x0);
167 _mm_storeu_ps(x + i + 4, x1);
168 _mm_storeu_ps(x + i + 8, x2);
169 _mm_storeu_ps(x + i + 12, x3);
170 }
171
172 simd_end = n - (n % 4);
173 for (size_t i = simd_end4; i < simd_end; i += 4)
174 {
175 __m128 x0 = _mm_loadu_ps(x + i);
176 _mm_storeu_ps(x + i, _mm_and_ps(ones, _mm_cmpgt_ps(x0, zeros)));
177 }
178#endif
179
180 for (size_t i = simd_end; i < n; i++)
181 {
182 x[i] = (x[i] > 0.0f) ? 1.0f : 0.0f;
183 }
184 }
185
188 static inline void dReluInto(float *RESTRICT dst, const float *RESTRICT src, const size_t n) noexcept
189 {
190 assert(n != 0 && "n must not be 0.");
191 assert(dst != nullptr && "dst must not be null.");
192 assert(src != nullptr && "src must not be null.");
193
194 size_t i = 0;
195
196#if defined(__AVX512F__)
197 __m512 ones = _mm512_set1_ps(1.0f);
198 __m512 zeros = _mm512_setzero_ps();
199 size_t simd_end = n - (n % 16);
200#pragma omp parallel for schedule(static) if (n > 65536 && !omp_in_parallel())
201 for (ptrdiff_t j = 0; j < (ptrdiff_t)simd_end; j += 16)
202 {
203 __m512 s = _mm512_loadu_ps(src + j);
204 __mmask16 m = _mm512_cmp_ps_mask(s, zeros, _CMP_GT_OQ);
205 _mm512_storeu_ps(dst + j, _mm512_mask_blend_ps(m, zeros, ones));
206 }
207 i = simd_end;
208#elif defined(__AVX2__) || defined(__AVX__)
209 __m256 ones = _mm256_set1_ps(1.0f);
210 __m256 zeros = _mm256_setzero_ps();
211 size_t simd_end = n - (n % 8);
212#pragma omp parallel for schedule(static) if (n > 65536 && !omp_in_parallel())
213 for (ptrdiff_t j = 0; j < (ptrdiff_t)simd_end; j += 8)
214 {
215 __m256 s = _mm256_loadu_ps(src + j);
216 _mm256_storeu_ps(dst + j, _mm256_and_ps(ones, _mm256_cmp_ps(s, zeros, _CMP_GT_OQ)));
217 }
218 i = simd_end;
219#elif defined(__SSE__) || defined(_M_AMD64) || defined(_M_X64)
220 __m128 ones = _mm_set1_ps(1.0f);
221 __m128 zeros = _mm_setzero_ps();
222 size_t simd_end = n - (n % 4);
223#pragma omp parallel for schedule(static) if (n > 65536 && !omp_in_parallel())
224 for (ptrdiff_t j = 0; j < (ptrdiff_t)simd_end; j += 4)
225 {
226 __m128 s = _mm_loadu_ps(src + j);
227 _mm_storeu_ps(dst + j, _mm_and_ps(ones, _mm_cmpgt_ps(s, zeros)));
228 }
229 i = simd_end;
230#endif
231
232 for (; i < n; i++)
233 {
234 dst[i] = (src[i] > 0.0f) ? 1.0f : 0.0f;
235 }
236 }
237} // namespace Deep
The RESTRICT portability macro shared by every activation header. Split out so each per-activation he...
#define RESTRICT
Portability macro for the C++ restrict pointer qualifier (MSVC spells it differently from everyone el...
Definition ActivationMacros.h:16
#define MAX(a, b)
Guarded so this doesn't clash with a system MAX macro (notably Windows headers) if one is already def...
Definition Relu.h:11
This file exists to provide both Tensor.h and IComputeBackend.h with device types without introducing...
Definition Backend.cpp:10
static void relu(float *, size_t) noexcept
See Relu.h.
static void dReluInto(float *RESTRICT, const float *RESTRICT, size_t) noexcept
See Relu.h.
static void dRelu(float *, size_t, bool) noexcept
See Relu.h.