11#ifndef NUMERICAL_X86_HPP
12#define NUMERICAL_X86_HPP
23#if defined(__x86_64__) || defined(_M_X64)
24# define NUMERICAL_X86_COMPILED
25# include <immintrin.h>
26# if defined(_MSC_VER) && !defined(__clang__)
28# define NUMERICAL_TARGET(isa)
29# define NUMERICAL_UNROLL_TILE
30# define NUMERICAL_UNROLL_DEPTH
31# define NUMERICAL_UNROLL_DEPTH_AVX2
34# define NUMERICAL_TARGET(isa) __attribute__((target(isa)))
36# define NUMERICAL_UNROLL_TILE _Pragma("GCC unroll 24")
39# define NUMERICAL_UNROLL_DEPTH _Pragma("GCC unroll 2")
42# define NUMERICAL_UNROLL_DEPTH_AVX2 _Pragma("GCC unroll 4")
48#ifdef NUMERICAL_X86_COMPILED
58 std::uint32_t
sub)
noexcept
60 std::array<std::uint32_t, 4> r {};
61# if defined(_MSC_VER) && !defined(__clang__)
62 std::array<int, 4>
regs {};
64 for (std::size_t i {0}; i < 4; ++i) {
65 r[i] =
static_cast<std::uint32_t
>(
regs[i]);
85 std::uint64_t
state)
noexcept
100 const std::array<std::uint32_t, 4>
leaf1 {
cpuid(1, 0)};
103 const std::array<std::uint32_t, 4>
leaf7 {
cpuid(0, 0)[0] >= 7U ?
cpuid(7, 0)
124 inline constexpr std::size_t
avx512_mr {12};
127 inline constexpr std::size_t
avx512_nr {16};
130 inline constexpr std::size_t
avx2_mr {6};
133 inline constexpr std::size_t
avx2_nr {8};
147 std::array<zmm_register, avx512_mr * 2>
acc {};
149 for (std::size_t i {0}; i <
avx512_mr; ++i) {
154 for (std::size_t p {0}; p <
kc; ++p) {
158 for (std::size_t i {0}; i <
avx512_mr; ++i) {
165 for (std::size_t i {0}; i <
avx512_mr; ++i) {
181 std::size_t cols)
noexcept
183 through_edge<avx512_mr, avx512_nr>(c, ldc, rows, cols, [&](
double* tile, std::size_t ld)
noexcept {
avx512_full_tile_f64(
kc,
ap,
bp,
ldb, tile, ld); });
200 std::array<ymm_register, avx2_mr * 2>
acc {};
202 for (std::size_t i {0}; i <
avx2_mr; ++i) {
207 for (std::size_t p {0}; p <
kc; ++p) {
215 for (std::size_t i {0}; i <
avx2_mr; ++i) {
222 for (std::size_t i {0}; i <
avx2_mr; ++i) {
238 std::size_t cols)
noexcept
240 through_edge<avx2_mr, avx2_nr>(c, ldc, rows, cols, [&](
double* tile, std::size_t ld)
noexcept {
avx2_full_tile_f64(
kc,
ap,
bp,
ldb, tile, ld); });
269 std::array<zmm_register_f32, avx512_mr * 2>
acc {};
271 for (std::size_t i {0}; i <
avx512_mr; ++i) {
276 for (std::size_t p {0}; p <
kc; ++p) {
280 for (std::size_t i {0}; i <
avx512_mr; ++i) {
287 for (std::size_t i {0}; i <
avx512_mr; ++i) {
302 std::size_t cols)
noexcept
304 through_edge<avx512_mr, avx512_nr_f32>(c, ldc, rows, cols, [&](
float* tile, std::size_t ld)
noexcept {
avx512_full_tile_f32(
kc,
ap,
bp,
ldb, tile, ld); });
315 std::array<ymm_register_f32, avx2_mr * 2>
acc {};
317 for (std::size_t i {0}; i <
avx2_mr; ++i) {
322 for (std::size_t p {0}; p <
kc; ++p) {
328 for (std::size_t i {0}; i <
avx2_mr; ++i) {
335 for (std::size_t i {0}; i <
avx2_mr; ++i) {
350 std::size_t cols)
noexcept
352 through_edge<avx2_mr, avx2_nr_f32>(c, ldc, rows, cols, [&](
float* tile, std::size_t ld)
noexcept {
avx2_full_tile_f32(
kc,
ap,
bp,
ldb, tile, ld); });
375 const auto*
const a {
reinterpret_cast<const double*
>(
ap)};
376 const auto*
const b {
reinterpret_cast<const double*
>(
bp)};
377 auto*
const c {
reinterpret_cast<double*
>(tile)};
378 std::array<ymm_register, avx2_complex_mr * 4>
acc {};
380 for (std::size_t p {0}; p <
kc; ++p) {
395 double*
const row {c + (2 * i * ld)};
407 const std::complex<double>*
ap,
408 const std::complex<double>*
bp,
410 std::complex<double>* c,
413 std::size_t cols)
noexcept
438 const auto*
const a {
reinterpret_cast<const double*
>(
ap)};
439 const auto*
const b {
reinterpret_cast<const double*
>(
bp)};
440 auto*
const c {
reinterpret_cast<double*
>(tile)};
441 std::array<zmm_register, avx512_complex_mr * 4>
acc {};
443 for (std::size_t p {0}; p <
kc; ++p) {
459 double*
const row {c + (2 * i * ld)};
469 const std::complex<double>*
ap,
470 const std::complex<double>*
bp,
472 std::complex<double>* c,
475 std::size_t cols)
noexcept
496 const auto*
const a {
reinterpret_cast<const float*
>(
ap)};
497 const auto*
const b {
reinterpret_cast<const float*
>(
bp)};
498 auto*
const c {
reinterpret_cast<float*
>(tile)};
499 std::array<ymm_register_f32, avx2_complex_mr * 4>
acc {};
501 for (std::size_t p {0}; p <
kc; ++p) {
516 float*
const row {c + (2 * i * ld)};
527 const std::complex<float>*
ap,
528 const std::complex<float>*
bp,
530 std::complex<float>* c,
533 std::size_t cols)
noexcept
552 const auto*
const a {
reinterpret_cast<const float*
>(
ap)};
553 const auto*
const b {
reinterpret_cast<const float*
>(
bp)};
554 auto*
const c {
reinterpret_cast<float*
>(tile)};
555 std::array<zmm_register_f32, avx512_complex_mr * 4>
acc {};
557 for (std::size_t p {0}; p <
kc; ++p) {
570 const __m512 signs {
_mm512_set_ps(1.0F, -1.0F, 1.0F, -1.0F, 1.0F, -1.0F, 1.0F, -1.0F, 1.0F, -1.0F, 1.0F, -1.0F, 1.0F, -1.0F, 1.0F, -1.0F)};
573 float*
const row {c + (2 * i * ld)};
583 const std::complex<float>*
ap,
584 const std::complex<float>*
bp,
586 std::complex<float>* c,
589 std::size_t cols)
noexcept
607 constexpr std::size_t width {8};
608 constexpr std::size_t
lanes {4};
610 std::array<zmm_register, lanes>
sum {};
611 const std::size_t done {count - (count % (width *
lanes))};
612 for (std::size_t i {0}; i < done; i += width *
lanes) {
614 for (std::size_t
l {0};
l <
lanes; ++
l) {
621 for (std::size_t
l {0};
l <
lanes; ++
l) {
624 for (std::size_t i {done}; i < count; ++i) {
639 constexpr std::size_t width {4};
640 constexpr std::size_t
lanes {4};
642 std::array<ymm_register, lanes>
sum {};
643 const std::size_t done {count - (count % (width *
lanes))};
644 for (std::size_t i {0}; i < done; i += width *
lanes) {
646 for (std::size_t
l {0};
l <
lanes; ++
l) {
653 for (std::size_t
l {0};
l <
lanes; ++
l) {
654 alignas(32) std::array<double, width>
part {};
658 for (std::size_t i {done}; i < count; ++i) {
676 constexpr std::size_t width {8};
677 std::array<zmm_register, symmetric_row_group>
sum {};
678 std::array<zmm_register, symmetric_row_group>
splat {};
682 const std::size_t done {i - (i % width)};
683 for (std::size_t j {0}; j < done; j += width) {
694 std::array<double, symmetric_row_group>
sums {};
709 constexpr std::size_t width {4};
710 std::array<ymm_register, symmetric_row_group>
sum {};
711 std::array<ymm_register, symmetric_row_group>
splat {};
715 const std::size_t done {i - (i % width)};
716 for (std::size_t j {0}; j < done; j += width) {
727 std::array<double, symmetric_row_group>
sums {};
729 alignas(32) std::array<double, width>
part {};
745 const std::size_t done {count - (count % 8)};
746 for (std::size_t i {0}; i < done; i += 8) {
749 for (std::size_t i {done}; i < count; ++i) {
750 y[i] = std::fma(
f, x[i],
y[i]);
768 for (; i + 32 <= count; i += 32) {
773 for (std::size_t t {0}; t <
terms; ++t) {
775 const double*
const row {x + (t *
ldx) + i};
786 for (; i + 8 <= count; i += 8) {
788 for (std::size_t t {0}; t <
terms; ++t) {
793 for (; i < count; ++i) {
794 for (std::size_t t {0}; t <
terms; ++t) {
795 y[i] = std::fma(-
f[t], x[(t *
ldx) + i],
y[i]);
819 for (std::size_t i {0}; i < count; i += 8) {
820 const auto mask {
static_cast<__mmask8>(count - i >= 8 ? 0xFFU : (1U << (count - i)) - 1U)};
821 std::array<lane, 8>
held {};
822 for (std::size_t r {0}; r < 8; ++r) {
825 for (std::size_t t {0}; t <
terms; ++t) {
827 for (std::size_t r {0}; r < 8; ++r) {
831 for (std::size_t r {0}; r < 8; ++r) {
853 for (; i + 4 <= count; i += 4) {
854 std::array<lane, 8>
held {};
855 for (std::size_t r {0}; r < 8; ++r) {
858 for (std::size_t t {0}; t <
terms; ++t) {
860 for (std::size_t r {0}; r < 8; ++r) {
864 for (std::size_t r {0}; r < 8; ++r) {
868 for (; i < count; ++i) {
869 std::array<double, 8>
held {};
870 for (std::size_t r {0}; r < 8; ++r) {
873 for (std::size_t t {0}; t <
terms; ++t) {
874 const double value {x[(t *
ldx) + i]};
875 for (std::size_t r {0}; r < 8; ++r) {
879 for (std::size_t r {0}; r < 8; ++r) {
895 for (; i + 16 <= count; i += 16) {
900 for (std::size_t t {0}; t <
terms; ++t) {
902 const double*
const row {x + (t *
ldx) + i};
913 for (; i + 4 <= count; i += 4) {
915 for (std::size_t t {0}; t <
terms; ++t) {
920 for (; i < count; ++i) {
921 for (std::size_t t {0}; t <
terms; ++t) {
922 y[i] = std::fma(-
f[t], x[(t *
ldx) + i],
y[i]);
943 for (; i + 16 <= count; i += 16) {
944 std::array<zmm_register, 4>
acc {};
946 for (std::size_t r {0}; r < 4; ++r) {
949 for (std::size_t t {0}; t <
terms; ++t) {
952 const double*
const row {x + (2 * ((t *
ldx) + i))};
954 for (std::size_t r {0}; r < 4; ++r) {
961 for (std::size_t r {0}; r < 4; ++r) {
965 for (; i + 4 <= count; i += 4) {
967 for (std::size_t t {0}; t <
terms; ++t) {
974 for (; i < count; ++i) {
975 double real {
y[2 * i]};
976 double imag {
y[(2 * i) + 1]};
977 for (std::size_t t {0}; t <
terms; ++t) {
978 const double xr {x[2 * ((t *
ldx) + i)]};
979 const double xi {x[(2 * ((t *
ldx) + i)) + 1]};
980 real = std::fma(-
f[2 * t],
xr, real);
982 real = std::fma(
f[(2 * t) + 1],
xi, real);
986 y[(2 * i) + 1] =
imag;
1001 for (; i + 8 <= count; i += 8) {
1002 std::array<ymm_register, 4>
acc {};
1004 for (std::size_t r {0}; r < 4; ++r) {
1007 for (std::size_t t {0}; t <
terms; ++t) {
1010 const double*
const row {x + (2 * ((t *
ldx) + i))};
1012 for (std::size_t r {0}; r < 4; ++r) {
1019 for (std::size_t r {0}; r < 4; ++r) {
1023 for (; i + 2 <= count; i += 2) {
1025 for (std::size_t t {0}; t <
terms; ++t) {
1032 for (; i < count; ++i) {
1033 double real {
y[2 * i]};
1034 double imag {
y[(2 * i) + 1]};
1035 for (std::size_t t {0}; t <
terms; ++t) {
1036 const double xr {x[2 * ((t *
ldx) + i)]};
1037 const double xi {x[(2 * ((t *
ldx) + i)) + 1]};
1038 real = std::fma(-
f[2 * t],
xr, real);
1040 real = std::fma(
f[(2 * t) + 1],
xi, real);
1044 y[(2 * i) + 1] =
imag;
1067 for (; j + 4 <= count; j += 4) {
1079 alignas(64) std::array<double, 8>
lanes_same {};
1085 for (std::size_t
l {0};
l < 8;
l += 2) {
1089 for (; j < count; ++j) {
1090 const double rr {
row[2 * j]};
1091 const double ri {
row[(2 * j) + 1]};
1093 out[(2 * j) + 1] = std::fma(
vi,
rr, std::fma(-
vr,
ri,
out[(2 * j) + 1]));
1094 re += (
rr * v[2 * j]) - (
ri * v[(2 * j) + 1]);
1095 im += (
ri * v[2 * j]) + (
rr * v[(2 * j) + 1]);
1098 return std::complex<double> {re, im};
1114 for (; j + 2 <= count; j += 2) {
1125 alignas(32) std::array<double, 4>
lanes_same {};
1131 for (; j < count; ++j) {
1132 const double rr {
row[2 * j]};
1133 const double ri {
row[(2 * j) + 1]};
1135 out[(2 * j) + 1] = std::fma(
vi,
rr, std::fma(-
vr,
ri,
out[(2 * j) + 1]));
1136 re += (
rr * v[2 * j]) - (
ri * v[(2 * j) + 1]);
1137 im += (
ri * v[2 * j]) + (
rr * v[(2 * j) + 1]);
1140 return std::complex<double> {re, im};
1150 const std::size_t done {count - (count % 4)};
1151 for (std::size_t i {0}; i < done; i += 4) {
1154 for (std::size_t i {done}; i < count; ++i) {
1155 y[i] = std::fma(
f, x[i],
y[i]);
1171 auto*
const out {
reinterpret_cast<float*
>(
y)};
1172 const auto*
const in {
reinterpret_cast<const float*
>(x)};
1175 const std::size_t done {count - (count % 4)};
1176 for (std::size_t i {0}; i < 2 * done; i += 8) {
1191 auto*
const out {
reinterpret_cast<double*
>(
y)};
1192 const auto*
const in {
reinterpret_cast<const double*
>(x)};
1195 const std::size_t done {count - (count % 2)};
1196 for (std::size_t i {0}; i < 2 * done; i += 4) {
1219 constexpr std::size_t
group {8};
1220 std::size_t j {first};
1222 std::array<double, group>
sums {};
1223 for (std::size_t k {0}; k < inner; ++k) {
1224 const double f {a[k]};
1225 for (std::size_t c {0}; c <
group; ++c) {
1229 std::copy(
sums.begin(),
sums.end(),
y + j);
1231 for (; j <
last; ++j) {
1233 for (std::size_t k {0}; k < inner; ++k) {
1246 constexpr std::size_t width {8};
1247 constexpr std::size_t
lanes {4};
1248 std::array<zmm_register, lanes>
sum {};
1249 const std::size_t done {count - (count % (width *
lanes))};
1250 for (std::size_t i {0}; i < done; i += width *
lanes) {
1252 for (std::size_t
l {0};
l <
lanes; ++
l) {
1257 for (std::size_t
l {0};
l <
lanes; ++
l) {
1260 for (std::size_t i {done}; i < count; ++i) {
1272 constexpr std::size_t width {4};
1273 constexpr std::size_t
lanes {4};
1274 std::array<ymm_register, lanes>
sum {};
1275 const std::size_t done {count - (count % (width *
lanes))};
1276 for (std::size_t i {0}; i < done; i += width *
lanes) {
1278 for (std::size_t
l {0};
l <
lanes; ++
l) {
1283 for (std::size_t
l {0};
l <
lanes; ++
l) {
1284 alignas(32) std::array<double, width>
part {};
1288 for (std::size_t i {done}; i < count; ++i) {
1306 constexpr std::size_t width {4};
1307 constexpr std::size_t
lanes {4};
1308 const auto*
const a {
reinterpret_cast<const double*
>(x)};
1309 const auto*
const b {
reinterpret_cast<const double*
>(
y)};
1310 const std::size_t
parts {2 * count};
1311 std::array<ymm_register, lanes>
same {};
1312 std::array<ymm_register, lanes>
cross {};
1314 for (std::size_t i {0}; i < done; i += width *
lanes) {
1316 for (std::size_t
l {0};
l <
lanes; ++
l) {
1327 for (std::size_t
l {0};
l <
lanes; ++
l) {
1328 alignas(32) std::array<double, width> s {};
1329 alignas(32) std::array<double, width> c {};
1337 for (std::size_t i {done}; i <
parts; i += 2) {
1339 ii += a[i + 1] * b[i + 1];
1340 ri += a[i] * b[i + 1];
1341 ir += a[i + 1] * b[i];
1352 constexpr std::size_t width {8};
1353 constexpr std::size_t
lanes {4};
1354 const auto*
const a {
reinterpret_cast<const float*
>(x)};
1355 const auto*
const b {
reinterpret_cast<const float*
>(
y)};
1356 const std::size_t
parts {2 * count};
1357 std::array<ymm_register_f32, lanes>
same {};
1358 std::array<ymm_register_f32, lanes>
cross {};
1360 for (std::size_t i {0}; i < done; i += width *
lanes) {
1362 for (std::size_t
l {0};
l <
lanes; ++
l) {
1373 for (std::size_t
l {0};
l <
lanes; ++
l) {
1374 alignas(32) std::array<float, width> s {};
1375 alignas(32) std::array<float, width> c {};
1378 rr += (s[0] + s[2]) + (s[4] + s[6]);
1379 ii += (s[1] + s[3]) + (s[5] + s[7]);
1380 ri += (c[0] + c[2]) + (c[4] + c[6]);
1381 ir += (c[1] + c[3]) + (c[5] + c[7]);
1383 for (std::size_t i {done}; i <
parts; i += 2) {
1385 ii += a[i + 1] * b[i + 1];
1386 ri += a[i] * b[i + 1];
1387 ir += a[i + 1] * b[i];
constexpr std::size_t symmetric_row_group
The rows of a lower triangle symmetric_rows takes at once.
std::array< T, symmetric_row_group > symmetric_rows_tail(const T *rows, std::size_t n, const T *v, T *out, std::size_t i, std::size_t done, std::array< T, symmetric_row_group > sums)
The columns done…i+r of symmetric_rows, one by one: for each row r in order, out[k] += row_r[k] · v[i...
T sum(const tensor< T > &t)
The sum of every element, by pairwise halving (0 for an empty tensor).
The native vector of float and double the kernels hold their lanes in (GNU vector extensions,...