Deepity
Ultra-fast, SIMD-accelerated Predictive Coding
Loading...
Searching...
No Matches
Sigmoid.h
Go to the documentation of this file.
1#pragma once
2#include <cassert>
3#include <cmath>
4#include <cstddef>
5#include <immintrin.h>
6#include <omp.h>
7#include <sleef.h>
9
16
17namespace Deep
18{
19
23 static inline void sigmoid(float *RESTRICT x, const size_t n) noexcept
24 {
25 assert(n != 0 && "n must not be 0.");
26 assert(x != nullptr && "x must not be null.");
27
28 size_t i = 0;
29
30#if defined(__AVX512F__)
31 __m512 one = _mm512_set1_ps(1.0f);
32 __m512 neg_one = _mm512_set1_ps(-1.0f);
33 size_t r = n % 16;
34 size_t simd_end = n - r;
35 for (; i < simd_end; i += 16)
36 {
37 __m512 x_512 = _mm512_loadu_ps(x + i);
38 __m512 neg_x = _mm512_mul_ps(x_512, neg_one);
39 __m512 exp_neg_x = Sleef_expf16_u10avx512f(neg_x);
40 __m512 den = _mm512_add_ps(exp_neg_x, one);
41 __m512 sig = _mm512_div_ps(one, den);
42 _mm512_storeu_ps(x + i, sig);
43 }
44#elif defined(__AVX2__)
45 __m256 one = _mm256_set1_ps(1.0f);
46 __m256 neg_one = _mm256_set1_ps(-1.0f);
47 size_t r = n % 8;
48 size_t simd_end = n - r;
49 for (; i < simd_end; i += 8)
50 {
51 __m256 x_256 = _mm256_loadu_ps(x + i);
52 __m256 neg_x = _mm256_mul_ps(x_256, neg_one);
53 __m256 exp_neg_x = Sleef_expf8_u10avx2(neg_x);
54 __m256 den = _mm256_add_ps(exp_neg_x, one);
55 __m256 sig = _mm256_div_ps(one, den);
56 _mm256_storeu_ps(x + i, sig);
57 }
58#elif defined(__SSE__) || defined(_M_AMD64) || defined(_M_X64)
59 __m128 one = _mm_set1_ps(1.0f);
60 __m128 neg_one = _mm_set1_ps(-1.0f);
61 size_t r = n % 4;
62 size_t simd_end = n - r;
63 for (; i < simd_end; i += 4)
64 {
65 __m128 x_128 = _mm_loadu_ps(x + i);
66 __m128 neg_x = _mm_mul_ps(x_128, neg_one);
67 __m128 exp_neg_x = Sleef_expf4_u10sse2(neg_x);
68 __m128 den = _mm_add_ps(exp_neg_x, one);
69 __m128 sig = _mm_div_ps(one, den);
70 _mm_storeu_ps(x + i, sig);
71 }
72#endif
73 for (; i < n; i++)
74 {
75 x[i] = 1.0f / (1.0f + std::exp(-x[i]));
76 }
77 }
78
82 inline void e_sigmoid(float *RESTRICT x, const size_t n) noexcept
83 {
84 assert(n != 0 && "n must not be 0.");
85 assert(x != nullptr && "x must not be null.");
86
87 size_t i = 0;
88
89#if defined(__AVX512F__)
90 __m512 half = _mm512_set1_ps(0.5f);
91 __m512 one = _mm512_set1_ps(1.0f);
92 size_t r = n % 16;
93 size_t simd_end = n - r;
94 for (; i < simd_end; i += 16)
95 {
96 __m512 x_512 = _mm512_loadu_ps(x + i);
97 __m512 den = _mm512_add_ps(
98 _mm512_abs_ps(x_512),
99 one);
100 __m512 div = _mm512_div_ps(x_512, den);
101 __m512 sig = _mm512_fmadd_ps(div, half, half);
102
103 _mm512_storeu_ps(x + i, sig);
104 }
105#elif defined(__AVX2__)
106 __m256 half = _mm256_set1_ps(0.5f);
107 __m256 one = _mm256_set1_ps(1.0f);
108 __m256 mask = _mm256_castsi256_ps(_mm256_set1_epi32(0x7FFFFFFF)); // for 256-bit abs
109 size_t r = n % 8;
110 size_t simd_end = n - r;
111 for (; i < simd_end; i += 8)
112 {
113 __m256 x_256 = _mm256_loadu_ps(x + i);
114 __m256 den = _mm256_add_ps(
115 _mm256_and_ps(x_256, mask),
116 one);
117 __m256 div = _mm256_div_ps(x_256, den);
118#ifdef __FMA__
119 __m256 sig = _mm256_fmadd_ps(div, half, half);
120#else
121 __m256 sig = _mm256_add_ps(_mm256_mul_ps(div, half), half);
122#endif
123
124 _mm256_storeu_ps(x + i, sig);
125 }
126#elif defined(__SSE__) || defined(_M_AMD64) || defined(_M_X64)
127 __m128 half = _mm_set1_ps(0.5f);
128 __m128 one = _mm_set1_ps(1.0f);
129 __m128 mask = _mm_castsi128_ps(_mm_set1_epi32(0x7FFFFFFF)); // for 256-bit abs
130 size_t r = n % 4;
131 size_t simd_end = n - r;
132 for (; i < simd_end; i += 4)
133 {
134 __m128 x_128 = _mm_loadu_ps(x + i);
135 __m128 den = _mm_add_ps(
136 _mm_and_ps(x_128, mask),
137 one);
138 __m128 div = _mm_div_ps(x_128, den);
139#ifdef __FMA__
140 __m128 sig = _mm_fmadd_ps(div, half, half);
141#else
142 __m128 sig = _mm_add_ps(_mm_mul_ps(div, half), half);
143#endif
144
145 _mm_storeu_ps(x + i, sig);
146 }
147#endif
148 for (; i < n; i++)
149 {
150 x[i] = 0.5f * (x[i] / (1.0f + fabsf(x[i])) + 1.0f);
151 }
152 }
153
159 static inline void dSigmoid(float *RESTRICT x, const size_t n, bool activated = false) noexcept
160 {
161 assert(n != 0 && "n must not be 0.");
162 assert(x != nullptr && "x must not be null.");
163
164 if (!activated)
165 sigmoid(x, n);
166 size_t i = 0;
167
168#if defined(__AVX512F__)
169
170 size_t r = n % 16;
171 size_t simd_end = n - r;
172 for (; i < simd_end; i += 16)
173 {
174 __m512 x_512 = _mm512_loadu_ps(x + i);
175 __m512 d = _mm512_fnmadd_ps(x_512, x_512, x_512); // d = x * (1 - x) = x - x^2 = -x*x + x
176 _mm512_storeu_ps(x + i, d);
177 }
178#elif defined(__AVX2__)
179
180 size_t r = n % 8;
181 size_t simd_end = n - r;
182 for (; i < simd_end; i += 8)
183 {
184 __m256 x_256 = _mm256_loadu_ps(x + i);
185#ifdef __FMA__
186 __m256 d = _mm256_fnmadd_ps(x_256, x_256, x_256); // d = x * (1 - x) = x - x^2 = -x*x + x
187#else
188 __m256 d = _mm256_sub_ps(x_256, _mm256_mul_ps(x_256, x_256));
189#endif
190 _mm256_storeu_ps(x + i, d);
191 }
192#elif defined(__SSE__) || defined(_M_AMD64) || defined(_M_X64)
193 size_t r = n % 4;
194 size_t simd_end = n - r;
195 for (; i < simd_end; i += 4)
196 {
197 __m128 x_128 = _mm_loadu_ps(x + i);
198
199#ifdef __FMA__
200 __m128 d = _mm_fnmadd_ps(x_128, x_128, x_128); // d = x * (1 - x) = x - x^2 = -x*x + x
201#else
202 __m128 d = _mm_sub_ps(x_128, _mm_mul_ps(x_128, x_128));
203#endif
204 _mm_storeu_ps(x + i, d);
205 }
206#endif
207
208 for (; i < n; i++)
209 {
210 x[i] = x[i] * (1.0f - x[i]);
211 }
212 }
213
219 static inline void dSigmoidInto(float *RESTRICT dst, const float *RESTRICT src, const size_t n) noexcept
220 {
221 assert(n != 0 && "n must not be 0.");
222 assert(dst != nullptr && "dst must not be null.");
223 assert(src != nullptr && "src must not be null.");
224
225 size_t i = 0;
226
227#if defined(__AVX512F__)
228 __m512 one = _mm512_set1_ps(1.0f);
229 __m512 neg_one = _mm512_set1_ps(-1.0f);
230 size_t simd_end = n - (n % 16);
231#pragma omp parallel for schedule(static) if (n > 65536 && !omp_in_parallel())
232 for (ptrdiff_t j = 0; j < (ptrdiff_t)simd_end; j += 16)
233 {
234 __m512 s = _mm512_loadu_ps(src + j);
235 __m512 neg_s = _mm512_mul_ps(s, neg_one);
236 __m512 exp_neg_s = Sleef_expf16_u10avx512f(neg_s);
237 __m512 den = _mm512_add_ps(exp_neg_s, one);
238 __m512 sig = _mm512_div_ps(one, den);
239 __m512 d = _mm512_fnmadd_ps(sig, sig, sig); // sig - sig^2 = sig*(1-sig)
240 _mm512_storeu_ps(dst + j, d);
241 }
242 i = simd_end;
243#elif defined(__AVX2__)
244 __m256 one = _mm256_set1_ps(1.0f);
245 __m256 neg_one = _mm256_set1_ps(-1.0f);
246 size_t simd_end = n - (n % 8);
247#pragma omp parallel for schedule(static) if (n > 65536 && !omp_in_parallel())
248 for (ptrdiff_t j = 0; j < (ptrdiff_t)simd_end; j += 8)
249 {
250 __m256 s = _mm256_loadu_ps(src + j);
251 __m256 neg_s = _mm256_mul_ps(s, neg_one);
252 __m256 exp_neg_s = Sleef_expf8_u10avx2(neg_s);
253 __m256 den = _mm256_add_ps(exp_neg_s, one);
254 __m256 sig = _mm256_div_ps(one, den);
255#ifdef __FMA__
256 __m256 d = _mm256_fnmadd_ps(sig, sig, sig);
257#else
258 __m256 d = _mm256_sub_ps(sig, _mm256_mul_ps(sig, sig));
259#endif
260 _mm256_storeu_ps(dst + j, d);
261 }
262 i = simd_end;
263#elif defined(__SSE__) || defined(_M_AMD64) || defined(_M_X64)
264 __m128 one = _mm_set1_ps(1.0f);
265 __m128 neg_one = _mm_set1_ps(-1.0f);
266 size_t simd_end = n - (n % 4);
267#pragma omp parallel for schedule(static) if (n > 65536 && !omp_in_parallel())
268 for (ptrdiff_t j = 0; j < (ptrdiff_t)simd_end; j += 4)
269 {
270 __m128 s = _mm_loadu_ps(src + j);
271 __m128 neg_s = _mm_mul_ps(s, neg_one);
272 __m128 exp_neg_s = Sleef_expf4_u10sse2(neg_s);
273 __m128 den = _mm_add_ps(exp_neg_s, one);
274 __m128 sig = _mm_div_ps(one, den);
275#ifdef __FMA__
276 __m128 d = _mm_fnmadd_ps(sig, sig, sig);
277#else
278 __m128 d = _mm_sub_ps(sig, _mm_mul_ps(sig, sig));
279#endif
280 _mm_storeu_ps(dst + j, d);
281 }
282 i = simd_end;
283#endif
284
285 for (; i < n; i++)
286 {
287 float sig = 1.0f / (1.0f + std::exp(-src[i]));
288 dst[i] = sig * (1.0f - sig);
289 }
290 }
291
297 static inline void d_eSigmoid(float *RESTRICT x,
298 const size_t n,
299 const bool activated = false) noexcept
300 {
301 assert(n != 0 && "n must not be 0.");
302 assert(x != nullptr && "x must not be null.");
303
304 if (!activated)
305 e_sigmoid(x, n);
306
307 size_t i = 0;
308
309#if defined(__AVX512F__)
310
311 const __m512 two = _mm512_set1_ps(2.0f);
312
313 const size_t r = n % 16;
314 const size_t simd_end = n - r;
315
316 for (; i < simd_end; i += 16)
317 {
318 const __m512 x_512 = _mm512_loadu_ps(x + i);
319
320 // d = 2 * x * (1 - x)
321 const __m512 d = _mm512_mul_ps(
322 _mm512_fnmadd_ps(x_512, x_512, x_512),
323 two);
324
325 _mm512_storeu_ps(x + i, d);
326 }
327
328#elif defined(__AVX2__)
329
330 const __m256 two = _mm256_set1_ps(2.0f);
331
332 const size_t r = n % 8;
333 const size_t simd_end = n - r;
334
335 for (; i < simd_end; i += 8)
336 {
337 const __m256 x_256 = _mm256_loadu_ps(x + i);
338
339#ifdef __FMA__
340 // x * (1 - x) = x - x^2
341 const __m256 d = _mm256_mul_ps(
342 _mm256_fnmadd_ps(x_256, x_256, x_256),
343 two);
344#else
345 const __m256 d = _mm256_mul_ps(
346 _mm256_sub_ps(x_256, _mm256_mul_ps(x_256, x_256)),
347 two);
348#endif
349
350 _mm256_storeu_ps(x + i, d);
351 }
352
353#elif defined(__SSE2__) || defined(_M_AMD64) || defined(_M_X64)
354
355 const __m128 two = _mm_set1_ps(2.0f);
356
357 const size_t r = n % 4;
358 const size_t simd_end = n - r;
359
360 for (; i < simd_end; i += 4)
361 {
362 const __m128 x_128 = _mm_loadu_ps(x + i);
363
364#ifdef __FMA__
365 const __m128 d = _mm_mul_ps(
366 _mm_fnmadd_ps(x_128, x_128, x_128),
367 two);
368#else
369 const __m128 d = _mm_mul_ps(
370 _mm_sub_ps(x_128, _mm_mul_ps(x_128, x_128)),
371 two);
372#endif
373
374 _mm_storeu_ps(x + i, d);
375 }
376
377#endif
378
379 for (; i < n; ++i)
380 {
381 x[i] = 2.0f * x[i] * (1.0f - x[i]);
382 }
383 }
384
387 static inline void d_eSigmoidInto(float *RESTRICT dst,
388 const float *RESTRICT src,
389 const size_t n) noexcept
390 {
391 assert(n != 0 && "n must not be 0.");
392 assert(dst != nullptr && "dst must not be null.");
393 assert(src != nullptr && "src must not be null.");
394
395 size_t i = 0;
396
397#if defined(__AVX512F__)
398
399 const __m512 half = _mm512_set1_ps(0.5f);
400 const __m512 one = _mm512_set1_ps(1.0f);
401
402 const size_t simd_end = n - (n % 16);
403
404#pragma omp parallel for schedule(static) if (n > 16384 && !omp_in_parallel())
405 for (ptrdiff_t j = 0; j < (ptrdiff_t)simd_end; j += 16)
406 {
407 const __m512 s = _mm512_loadu_ps(src + j);
408 const __m512 a = _mm512_add_ps(_mm512_abs_ps(s), one);
409 const __m512 d = _mm512_div_ps(half, _mm512_mul_ps(a, a));
410
411 _mm512_storeu_ps(dst + j, d);
412 }
413
414 i = simd_end;
415
416#elif defined(__AVX2__)
417
418 const __m256 half = _mm256_set1_ps(0.5f);
419 const __m256 one = _mm256_set1_ps(1.0f);
420 const __m256 abs_mask =
421 _mm256_castsi256_ps(_mm256_set1_epi32(0x7FFFFFFF));
422
423 const size_t simd_end = n - (n % 8);
424
425#pragma omp parallel for schedule(static) if (n > 16384 && !omp_in_parallel())
426 for (ptrdiff_t j = 0; j < (ptrdiff_t)simd_end; j += 8)
427 {
428 const __m256 s = _mm256_loadu_ps(src + j);
429 const __m256 a = _mm256_add_ps(
430 _mm256_and_ps(s, abs_mask),
431 one);
432
433 const __m256 d = _mm256_div_ps(
434 half,
435 _mm256_mul_ps(a, a));
436
437 _mm256_storeu_ps(dst + j, d);
438 }
439
440 i = simd_end;
441
442#elif defined(__SSE2__) || defined(_M_AMD64) || defined(_M_X64)
443
444 const __m128 half = _mm_set1_ps(0.5f);
445 const __m128 one = _mm_set1_ps(1.0f);
446 const __m128 abs_mask =
447 _mm_castsi128_ps(_mm_set1_epi32(0x7FFFFFFF));
448
449 const size_t simd_end = n - (n % 4);
450
451#pragma omp parallel for schedule(static) if (n > 16384 && !omp_in_parallel())
452 for (ptrdiff_t j = 0; j < (ptrdiff_t)simd_end; j += 4)
453 {
454 const __m128 s = _mm_loadu_ps(src + j);
455 const __m128 a = _mm_add_ps(
456 _mm_and_ps(s, abs_mask),
457 one);
458
459 const __m128 d = _mm_div_ps(
460 half,
461 _mm_mul_ps(a, a));
462
463 _mm_storeu_ps(dst + j, d);
464 }
465
466 i = simd_end;
467
468#endif
469
470 for (; i < n; ++i)
471 {
472 const float a = 1.0f + std::fabs(src[i]);
473 dst[i] = 0.5f / (a * a);
474 }
475 }
476
477} // 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
This file exists to provide both Tensor.h and IComputeBackend.h with device types without introducing...
Definition Backend.cpp:10
static void sigmoid(float *, size_t) noexcept
See Sigmoid.h.
static void d_eSigmoidInto(float *RESTRICT, const float *RESTRICT, size_t) noexcept
See Sigmoid.h.
static void d_eSigmoid(float *, size_t, bool) noexcept
See Sigmoid.h.
static void dSigmoidInto(float *RESTRICT, const float *RESTRICT, size_t) noexcept
See Sigmoid.h.
static void dSigmoid(float *, size_t, bool) noexcept
See Sigmoid.h.
static void e_sigmoid(float *, size_t) noexcept
See Sigmoid.h.