Eigen does use some SIMD instructions, but (as I said) it does a bad job of it.
Here are some benchmarks with neat sizes (16x24 * 24x14 = 16x14) and awkward prime sizes (31x37 * 37x29 = 31x29). Pardon the awkward code (particularly creating and allocating the matrices), but Eigen uses vmovapd
instead of vmovupd
; they’re more or less the same (I’ve not noticed a performance difference), except vmovapd
causes segfaults without alignment.
julia> using PaddedMatrices, LinearAlgebra, Random, BenchmarkTools
julia> import PaddedMatrices: AbstractMutableFixedSizePaddedMatrix
julia> # Compiled with
# g++ -O3 -fno-signed-zeros -fno-trapping-math -fassociative-math -march=skylake-avx512 -mprefer-vector-width=512 -I/usr/include/eigen3 -shared -fPIC eigen_matmul_test.cpp -o libgppeigentest.so
# clang++ -O3 -fno-signed-zeros -fno-trapping-math -fassociative-math -march=skylake-avx512 -mprefer-vector-width=512 -I/usr/include/eigen3 -shared -fPIC eigen_matmul_test.cpp -o libclangeigentest.so
const gpplib = "/home/chriselrod/Documents/progwork/Cxx/libgppeigentest.so"
"/home/chriselrod/Documents/progwork/Cxx/libgppeigentest.so"
julia> const clanglib = "/home/chriselrod/Documents/progwork/Cxx/libclangeigentest.so"
"/home/chriselrod/Documents/progwork/Cxx/libclangeigentest.so"
julia> function gppeigen!(
C::AbstractMutableFixedSizePaddedMatrix{16,14,Float64,16},
A::AbstractMutableFixedSizePaddedMatrix{16,24,Float64,16},
B::AbstractMutableFixedSizePaddedMatrix{24,14,Float64,24}
)
ccall(
(:mul_16x24times24x14, gpplib), Cvoid,
(Ptr{Float64}, Ptr{Float64}, Ptr{Float64}),
pointer(C), pointer(A), pointer(B)
)
end
gppeigen! (generic function with 1 method)
julia> function gppeigen!(
C::AbstractMutableFixedSizePaddedMatrix{31,29,Float64,31},
A::AbstractMutableFixedSizePaddedMatrix{31,37,Float64,31},
B::AbstractMutableFixedSizePaddedMatrix{37,29,Float64,37}
)
ccall(
(:mul_31x37times37x29, gpplib), Cvoid,
(Ptr{Float64}, Ptr{Float64}, Ptr{Float64}),
pointer(C), pointer(A), pointer(B)
)
end
gppeigen! (generic function with 2 methods)
julia> function clangeigen!(
C::AbstractMutableFixedSizePaddedMatrix{16,14,Float64,16},
A::AbstractMutableFixedSizePaddedMatrix{16,24,Float64,16},
B::AbstractMutableFixedSizePaddedMatrix{24,14,Float64,24}
)
ccall(
(:mul_16x24times24x14, clanglib), Cvoid,
(Ptr{Float64}, Ptr{Float64}, Ptr{Float64}),
pointer(C), pointer(A), pointer(B)
)
end
clangeigen! (generic function with 1 method)
julia> function clangeigen!(
C::AbstractMutableFixedSizePaddedMatrix{31,29,Float64,31},
A::AbstractMutableFixedSizePaddedMatrix{31,37,Float64,31},
B::AbstractMutableFixedSizePaddedMatrix{37,29,Float64,37}
)
ccall(
(:mul_31x37times37x29, clanglib), Cvoid,
(Ptr{Float64}, Ptr{Float64}, Ptr{Float64}),
pointer(C), pointer(A), pointer(B)
)
end
clangeigen! (generic function with 2 methods)
julia> align_64(x) = (x + 63) & ~63
align_64 (generic function with 1 method)
julia> align_64(x, ptr::Ptr{T}) where {T} = align_64(x*sizeof(T) + ptr)
align_64 (generic function with 2 methods)
julia> const PTR = Base.unsafe_convert(Ptr{Float64}, Libc.malloc(1<<20)); # more than enough space
julia> align_64(x::Ptr{T}) where {T} = reinterpret(Ptr{T},align_64(reinterpret(UInt,x)))
align_64 (generic function with 3 methods)
julia> align_64(PTR)
Ptr{Float64} @0x00007fad8b585040
julia> A16x24 = PtrMatrix{16,24,Float64,16}(align_64(PTR)); randn!(A16x24);
julia> B24x14 = PtrMatrix{24,14,Float64,24}(align_64(16*24,pointer(A16x24))); randn!(B24x14);
julia> C16x14 = PtrMatrix{16,14,Float64,16}(align_64(24*14,pointer(B24x14)));
julia> clangeigen!(C16x14, A16x24, B24x14)
julia> @benchmark gppeigen!($C16x14, $A16x24, $B24x14)
BenchmarkTools.Trial:
memory estimate: 0 bytes
allocs estimate: 0
--------------
minimum time: 446.863 ns (0.00% GC)
median time: 454.548 ns (0.00% GC)
mean time: 457.793 ns (0.00% GC)
maximum time: 701.843 ns (0.00% GC)
--------------
samples: 10000
evals/sample: 197
julia> @benchmark clangeigen!($C16x14, $A16x24, $B24x14)
BenchmarkTools.Trial:
memory estimate: 0 bytes
allocs estimate: 0
--------------
minimum time: 470.245 ns (0.00% GC)
median time: 474.612 ns (0.00% GC)
mean time: 477.438 ns (0.00% GC)
maximum time: 663.571 ns (0.00% GC)
--------------
samples: 10000
evals/sample: 196
julia> @benchmark mul!($C16x14, $A16x24, $B24x14)
BenchmarkTools.Trial:
memory estimate: 0 bytes
allocs estimate: 0
--------------
minimum time: 88.141 ns (0.00% GC)
median time: 90.651 ns (0.00% GC)
mean time: 91.153 ns (0.00% GC)
maximum time: 142.651 ns (0.00% GC)
--------------
samples: 10000
evals/sample: 960
julia> # Prime sizes
A31x37 = PtrMatrix{31,37,Float64,31}(align_64(16*14,pointer(C16x14))); randn!(A31x37);
julia> B37x29 = PtrMatrix{37,29,Float64,37}(align_64(31*37,pointer(A31x37))); randn!(B37x29);
julia> C31x29 = PtrMatrix{31,29,Float64,31}(align_64(37*29,pointer(B37x29)));
julia> @benchmark gppeigen!($C31x29, $A31x37, $B37x29)
BenchmarkTools.Trial:
memory estimate: 0 bytes
allocs estimate: 0
--------------
minimum time: 2.435 μs (0.00% GC)
median time: 2.458 μs (0.00% GC)
mean time: 2.494 μs (0.00% GC)
maximum time: 6.107 μs (0.00% GC)
--------------
samples: 10000
evals/sample: 9
julia> @benchmark clangeigen!($C31x29, $A31x37, $B37x29)
BenchmarkTools.Trial:
memory estimate: 0 bytes
allocs estimate: 0
--------------
minimum time: 2.346 μs (0.00% GC)
median time: 2.373 μs (0.00% GC)
mean time: 2.388 μs (0.00% GC)
maximum time: 6.090 μs (0.00% GC)
--------------
samples: 10000
evals/sample: 9
julia> @benchmark mul!($C31x29, $A31x37, $B37x29)
BenchmarkTools.Trial:
memory estimate: 0 bytes
allocs estimate: 0
--------------
minimum time: 585.606 ns (0.00% GC)
median time: 607.706 ns (0.00% GC)
mean time: 610.846 ns (0.00% GC)
maximum time: 1.466 μs (0.00% GC)
--------------
samples: 10000
evals/sample: 180
This roughly four-fold difference in performance was common across an array of small sizes.
You can look at the C++ code and the assembly here.
For comparison, here is the assembly Julia produced (I deleted all lines that only contained comments for brevity’s sake):
julia> @code_native mul!(C16x14, A16x24, B24x14)
.text
movq (%rsi), %rax
vmovupd (%rax), %zmm26
vmovupd 64(%rax), %zmm28
movq (%rdx), %rcx
vbroadcastsd (%rcx), %zmm1
vmulpd %zmm26, %zmm1, %zmm0
vmulpd %zmm28, %zmm1, %zmm1
vbroadcastsd 192(%rcx), %zmm3
vmulpd %zmm26, %zmm3, %zmm2
vmulpd %zmm28, %zmm3, %zmm3
vbroadcastsd 384(%rcx), %zmm5
vmulpd %zmm26, %zmm5, %zmm4
vmulpd %zmm28, %zmm5, %zmm5
vbroadcastsd 576(%rcx), %zmm7
vmulpd %zmm26, %zmm7, %zmm6
vmulpd %zmm28, %zmm7, %zmm7
vbroadcastsd 768(%rcx), %zmm9
vmulpd %zmm26, %zmm9, %zmm8
vmulpd %zmm28, %zmm9, %zmm9
vbroadcastsd 960(%rcx), %zmm11
vmulpd %zmm26, %zmm11, %zmm10
vmulpd %zmm28, %zmm11, %zmm11
vbroadcastsd 1152(%rcx), %zmm13
vmulpd %zmm26, %zmm13, %zmm12
vmulpd %zmm28, %zmm13, %zmm13
vbroadcastsd 1344(%rcx), %zmm15
vmulpd %zmm26, %zmm15, %zmm14
vmulpd %zmm28, %zmm15, %zmm15
vbroadcastsd 1536(%rcx), %zmm17
vmulpd %zmm26, %zmm17, %zmm16
vmulpd %zmm28, %zmm17, %zmm17
vbroadcastsd 1728(%rcx), %zmm19
vmulpd %zmm26, %zmm19, %zmm18
vmulpd %zmm28, %zmm19, %zmm19
vbroadcastsd 1920(%rcx), %zmm21
vmulpd %zmm26, %zmm21, %zmm20
vmulpd %zmm28, %zmm21, %zmm21
vbroadcastsd 2112(%rcx), %zmm23
vmulpd %zmm26, %zmm23, %zmm22
vmulpd %zmm28, %zmm23, %zmm23
vbroadcastsd 2304(%rcx), %zmm25
vmulpd %zmm26, %zmm25, %zmm24
vmulpd %zmm28, %zmm25, %zmm25
vbroadcastsd 2496(%rcx), %zmm29
vmulpd %zmm26, %zmm29, %zmm27
vmulpd %zmm28, %zmm29, %zmm26
addq $192, %rax
movq $-23, %rdx
nopw %cs:(%rax,%rax)
nopl (%rax,%rax)
L336:
vmovapd %zmm27, %zmm28
vmovupd -64(%rax), %zmm27
vmovupd (%rax), %zmm29
vbroadcastsd 192(%rcx,%rdx,8), %zmm30
vfmadd231pd %zmm30, %zmm27, %zmm0 # zmm0 = (zmm27 * zmm30) + zmm0
vfmadd231pd %zmm30, %zmm29, %zmm1 # zmm1 = (zmm29 * zmm30) + zmm1
vbroadcastsd 384(%rcx,%rdx,8), %zmm30
vfmadd231pd %zmm30, %zmm27, %zmm2 # zmm2 = (zmm27 * zmm30) + zmm2
vfmadd231pd %zmm30, %zmm29, %zmm3 # zmm3 = (zmm29 * zmm30) + zmm3
vbroadcastsd 576(%rcx,%rdx,8), %zmm30
vfmadd231pd %zmm30, %zmm27, %zmm4 # zmm4 = (zmm27 * zmm30) + zmm4
vfmadd231pd %zmm30, %zmm29, %zmm5 # zmm5 = (zmm29 * zmm30) + zmm5
vbroadcastsd 768(%rcx,%rdx,8), %zmm30
vfmadd231pd %zmm30, %zmm27, %zmm6 # zmm6 = (zmm27 * zmm30) + zmm6
vfmadd231pd %zmm30, %zmm29, %zmm7 # zmm7 = (zmm29 * zmm30) + zmm7
vbroadcastsd 960(%rcx,%rdx,8), %zmm30
vfmadd231pd %zmm30, %zmm27, %zmm8 # zmm8 = (zmm27 * zmm30) + zmm8
vfmadd231pd %zmm30, %zmm29, %zmm9 # zmm9 = (zmm29 * zmm30) + zmm9
vbroadcastsd 1152(%rcx,%rdx,8), %zmm30
vfmadd231pd %zmm30, %zmm27, %zmm10 # zmm10 = (zmm27 * zmm30) + zmm10
vfmadd231pd %zmm30, %zmm29, %zmm11 # zmm11 = (zmm29 * zmm30) + zmm11
vbroadcastsd 1344(%rcx,%rdx,8), %zmm30
vfmadd231pd %zmm30, %zmm27, %zmm12 # zmm12 = (zmm27 * zmm30) + zmm12
vfmadd231pd %zmm30, %zmm29, %zmm13 # zmm13 = (zmm29 * zmm30) + zmm13
vbroadcastsd 1536(%rcx,%rdx,8), %zmm30
vfmadd231pd %zmm30, %zmm27, %zmm14 # zmm14 = (zmm27 * zmm30) + zmm14
vfmadd231pd %zmm30, %zmm29, %zmm15 # zmm15 = (zmm29 * zmm30) + zmm15
vbroadcastsd 1728(%rcx,%rdx,8), %zmm30
vfmadd231pd %zmm30, %zmm27, %zmm16 # zmm16 = (zmm27 * zmm30) + zmm16
vfmadd231pd %zmm30, %zmm29, %zmm17 # zmm17 = (zmm29 * zmm30) + zmm17
vbroadcastsd 1920(%rcx,%rdx,8), %zmm30
vfmadd231pd %zmm30, %zmm27, %zmm18 # zmm18 = (zmm27 * zmm30) + zmm18
vfmadd231pd %zmm30, %zmm29, %zmm19 # zmm19 = (zmm29 * zmm30) + zmm19
vbroadcastsd 2112(%rcx,%rdx,8), %zmm30
vfmadd231pd %zmm30, %zmm27, %zmm20 # zmm20 = (zmm27 * zmm30) + zmm20
vfmadd231pd %zmm30, %zmm29, %zmm21 # zmm21 = (zmm29 * zmm30) + zmm21
vbroadcastsd 2304(%rcx,%rdx,8), %zmm30
vfmadd231pd %zmm30, %zmm27, %zmm22 # zmm22 = (zmm27 * zmm30) + zmm22
vfmadd231pd %zmm30, %zmm29, %zmm23 # zmm23 = (zmm29 * zmm30) + zmm23
vbroadcastsd 2496(%rcx,%rdx,8), %zmm30
vfmadd231pd %zmm30, %zmm27, %zmm24 # zmm24 = (zmm27 * zmm30) + zmm24
vfmadd231pd %zmm30, %zmm29, %zmm25 # zmm25 = (zmm29 * zmm30) + zmm25
vbroadcastsd 2688(%rcx,%rdx,8), %zmm30
vfmadd213pd %zmm28, %zmm30, %zmm27 # zmm27 = (zmm30 * zmm27) + zmm28
vfmadd231pd %zmm30, %zmm29, %zmm26 # zmm26 = (zmm29 * zmm30) + zmm26
subq $-128, %rax
incq %rdx
jne L336
movq (%rdi), %rax
vmovupd %zmm0, (%rax)
vmovupd %zmm1, 64(%rax)
vmovupd %zmm2, 128(%rax)
vmovupd %zmm3, 192(%rax)
vmovupd %zmm4, 256(%rax)
vmovupd %zmm5, 320(%rax)
vmovupd %zmm6, 384(%rax)
vmovupd %zmm7, 448(%rax)
vmovupd %zmm8, 512(%rax)
vmovupd %zmm9, 576(%rax)
vmovupd %zmm10, 640(%rax)
vmovupd %zmm11, 704(%rax)
vmovupd %zmm12, 768(%rax)
vmovupd %zmm13, 832(%rax)
vmovupd %zmm14, 896(%rax)
vmovupd %zmm15, 960(%rax)
vmovupd %zmm16, 1024(%rax)
vmovupd %zmm17, 1088(%rax)
vmovupd %zmm18, 1152(%rax)
vmovupd %zmm19, 1216(%rax)
vmovupd %zmm20, 1280(%rax)
vmovupd %zmm21, 1344(%rax)
vmovupd %zmm22, 1408(%rax)
vmovupd %zmm23, 1472(%rax)
vmovupd %zmm24, 1536(%rax)
vmovupd %zmm25, 1600(%rax)
vmovupd %zmm27, 1664(%rax)
vmovupd %zmm26, 1728(%rax)
vzeroupper
retq
nopl (%rax)
and for the larger. awkwardly sized matrices:
julia> @code_native mul!(C31x29, A31x37, B37x29)
.text
pushq %rbp
pushq %r15
pushq %r14
pushq %r13
pushq %r12
pushq %rbx
movq %rdx, -8(%rsp)
movq %rsi, -16(%rsp)
movl $1, %r9d
movl $1488, %esi # imm = 0x5D0
movb $127, %r10b
nopw %cs:(%rax,%rax)
nopl (%rax)
L48:
leaq -1(%r9), %rcx
movq -16(%rsp), %rax
movq (%rax), %r11
movq -8(%rsp), %rax
movq (%rax), %r14
imulq $1488, %rcx, %rax # imm = 0x5D0
imulq $1776, %rcx, %r13 # imm = 0x6F0
addq (%rdi), %rax
leaq (%r14,%rsi), %rcx
leaq 440(%r11), %rdx
xorl %r12d, %r12d
nopw %cs:(%rax,%rax)
nopl (%rax)
L112:
imulq $248, %r12, %rbx
leaq (%r11,%rbx), %rbp
vmovupd (%r11,%rbx), %zmm19
vmovupd 64(%r11,%rbx), %zmm22
vmovupd 128(%r11,%rbx), %zmm23
vbroadcastsd (%r14,%r13), %zmm3
vmovupd 192(%r11,%rbx), %zmm24
vmulpd %zmm19, %zmm3, %zmm0
vmulpd %zmm22, %zmm3, %zmm1
vmulpd %zmm23, %zmm3, %zmm2
vbroadcastsd 296(%r14,%r13), %zmm7
vmulpd %zmm24, %zmm3, %zmm6
vmulpd %zmm19, %zmm7, %zmm3
vmulpd %zmm22, %zmm7, %zmm4
vmulpd %zmm23, %zmm7, %zmm5
vbroadcastsd 592(%r14,%r13), %zmm11
vmulpd %zmm24, %zmm7, %zmm10
vmulpd %zmm19, %zmm11, %zmm7
vmulpd %zmm22, %zmm11, %zmm8
vmulpd %zmm23, %zmm11, %zmm9
vbroadcastsd 888(%r14,%r13), %zmm15
vmulpd %zmm24, %zmm11, %zmm14
vmulpd %zmm19, %zmm15, %zmm11
vmulpd %zmm22, %zmm15, %zmm12
vmulpd %zmm23, %zmm15, %zmm13
vbroadcastsd 1184(%r14,%r13), %zmm20
vmulpd %zmm24, %zmm15, %zmm18
vmulpd %zmm19, %zmm20, %zmm15
vmulpd %zmm22, %zmm20, %zmm16
vmulpd %zmm23, %zmm20, %zmm17
vbroadcastsd 1480(%r14,%r13), %zmm25
vmulpd %zmm24, %zmm20, %zmm20
vmulpd %zmm19, %zmm25, %zmm21
vmulpd %zmm22, %zmm25, %zmm22
vmulpd %zmm23, %zmm25, %zmm23
vmulpd %zmm24, %zmm25, %zmm19
movq $-35, %r8
movq %rdx, %r15
nopl (%rax)
L368:
vmovapd %zmm23, %zmm24
vmovapd %zmm22, %zmm25
vmovapd %zmm21, %zmm26
vmovupd -192(%r15), %zmm21
vmovupd -128(%r15), %zmm22
vmovupd -64(%r15), %zmm23
vmovupd (%r15), %zmm27
vbroadcastsd -1200(%rcx,%r8,8), %zmm28
vfmadd231pd %zmm28, %zmm21, %zmm0 # zmm0 = (zmm21 * zmm28) + zmm0
vfmadd231pd %zmm28, %zmm22, %zmm1 # zmm1 = (zmm22 * zmm28) + zmm1
vfmadd231pd %zmm28, %zmm23, %zmm2 # zmm2 = (zmm23 * zmm28) + zmm2
vfmadd231pd %zmm28, %zmm27, %zmm6 # zmm6 = (zmm27 * zmm28) + zmm6
vbroadcastsd -904(%rcx,%r8,8), %zmm28
vfmadd231pd %zmm28, %zmm21, %zmm3 # zmm3 = (zmm21 * zmm28) + zmm3
vfmadd231pd %zmm28, %zmm22, %zmm4 # zmm4 = (zmm22 * zmm28) + zmm4
vfmadd231pd %zmm28, %zmm23, %zmm5 # zmm5 = (zmm23 * zmm28) + zmm5
vfmadd231pd %zmm28, %zmm27, %zmm10 # zmm10 = (zmm27 * zmm28) + zmm10
vbroadcastsd -608(%rcx,%r8,8), %zmm28
vfmadd231pd %zmm28, %zmm21, %zmm7 # zmm7 = (zmm21 * zmm28) + zmm7
vfmadd231pd %zmm28, %zmm22, %zmm8 # zmm8 = (zmm22 * zmm28) + zmm8
vfmadd231pd %zmm28, %zmm23, %zmm9 # zmm9 = (zmm23 * zmm28) + zmm9
vfmadd231pd %zmm28, %zmm27, %zmm14 # zmm14 = (zmm27 * zmm28) + zmm14
vbroadcastsd -312(%rcx,%r8,8), %zmm28
vfmadd231pd %zmm28, %zmm21, %zmm11 # zmm11 = (zmm21 * zmm28) + zmm11
vfmadd231pd %zmm28, %zmm22, %zmm12 # zmm12 = (zmm22 * zmm28) + zmm12
vfmadd231pd %zmm28, %zmm23, %zmm13 # zmm13 = (zmm23 * zmm28) + zmm13
vfmadd231pd %zmm28, %zmm27, %zmm18 # zmm18 = (zmm27 * zmm28) + zmm18
vbroadcastsd -16(%rcx,%r8,8), %zmm28
vfmadd231pd %zmm28, %zmm21, %zmm15 # zmm15 = (zmm21 * zmm28) + zmm15
vfmadd231pd %zmm28, %zmm22, %zmm16 # zmm16 = (zmm22 * zmm28) + zmm16
vfmadd231pd %zmm28, %zmm23, %zmm17 # zmm17 = (zmm23 * zmm28) + zmm17
vfmadd231pd %zmm28, %zmm27, %zmm20 # zmm20 = (zmm27 * zmm28) + zmm20
vbroadcastsd 280(%rcx,%r8,8), %zmm28
vfmadd213pd %zmm26, %zmm28, %zmm21 # zmm21 = (zmm28 * zmm21) + zmm26
vfmadd213pd %zmm25, %zmm28, %zmm22 # zmm22 = (zmm28 * zmm22) + zmm25
vfmadd213pd %zmm24, %zmm28, %zmm23 # zmm23 = (zmm28 * zmm23) + zmm24
vfmadd231pd %zmm28, %zmm27, %zmm19 # zmm19 = (zmm27 * zmm28) + zmm19
addq $248, %r15
incq %r8
jne L368
vmovupd 8928(%rbp), %zmm24
vmovupd 8992(%rbp), %zmm25
vmovupd 9056(%rbp), %zmm26
kmovd %r10d, %k1
vmovupd 9120(%rbp), %zmm27 {%k1} {z}
vbroadcastsd 288(%r14,%r13), %zmm28
vfmadd231pd %zmm28, %zmm24, %zmm0 # zmm0 = (zmm24 * zmm28) + zmm0
vfmadd231pd %zmm28, %zmm25, %zmm1 # zmm1 = (zmm25 * zmm28) + zmm1
vfmadd231pd %zmm28, %zmm26, %zmm2 # zmm2 = (zmm26 * zmm28) + zmm2
vfmadd231pd %zmm28, %zmm27, %zmm6 # zmm6 = (zmm27 * zmm28) + zmm6
vbroadcastsd 584(%r14,%r13), %zmm28
vfmadd231pd %zmm28, %zmm24, %zmm3 # zmm3 = (zmm24 * zmm28) + zmm3
vfmadd231pd %zmm28, %zmm25, %zmm4 # zmm4 = (zmm25 * zmm28) + zmm4
vfmadd231pd %zmm28, %zmm26, %zmm5 # zmm5 = (zmm26 * zmm28) + zmm5
vfmadd231pd %zmm28, %zmm27, %zmm10 # zmm10 = (zmm27 * zmm28) + zmm10
vbroadcastsd 880(%r14,%r13), %zmm28
vfmadd231pd %zmm28, %zmm24, %zmm7 # zmm7 = (zmm24 * zmm28) + zmm7
vfmadd231pd %zmm28, %zmm25, %zmm8 # zmm8 = (zmm25 * zmm28) + zmm8
vfmadd231pd %zmm28, %zmm26, %zmm9 # zmm9 = (zmm26 * zmm28) + zmm9
vfmadd231pd %zmm28, %zmm27, %zmm14 # zmm14 = (zmm27 * zmm28) + zmm14
vbroadcastsd 1176(%r14,%r13), %zmm28
vfmadd231pd %zmm28, %zmm24, %zmm11 # zmm11 = (zmm24 * zmm28) + zmm11
vfmadd231pd %zmm28, %zmm25, %zmm12 # zmm12 = (zmm25 * zmm28) + zmm12
vfmadd231pd %zmm28, %zmm26, %zmm13 # zmm13 = (zmm26 * zmm28) + zmm13
vfmadd231pd %zmm28, %zmm27, %zmm18 # zmm18 = (zmm27 * zmm28) + zmm18
vbroadcastsd 1472(%r14,%r13), %zmm28
vfmadd231pd %zmm28, %zmm24, %zmm15 # zmm15 = (zmm24 * zmm28) + zmm15
vfmadd231pd %zmm28, %zmm25, %zmm16 # zmm16 = (zmm25 * zmm28) + zmm16
vfmadd231pd %zmm28, %zmm26, %zmm17 # zmm17 = (zmm26 * zmm28) + zmm17
vfmadd231pd %zmm28, %zmm27, %zmm20 # zmm20 = (zmm27 * zmm28) + zmm20
vbroadcastsd 1768(%r14,%r13), %zmm28
vfmadd213pd %zmm21, %zmm28, %zmm24 # zmm24 = (zmm28 * zmm24) + zmm21
vfmadd213pd %zmm22, %zmm28, %zmm25 # zmm25 = (zmm28 * zmm25) + zmm22
vfmadd213pd %zmm23, %zmm28, %zmm26 # zmm26 = (zmm28 * zmm26) + zmm23
vmovupd %zmm0, (%rax,%rbx)
vmovupd %zmm1, 64(%rax,%rbx)
vmovupd %zmm2, 128(%rax,%rbx)
vmovupd %zmm6, 192(%rax,%rbx) {%k1}
vmovupd %zmm3, 248(%rax,%rbx)
vmovupd %zmm4, 312(%rax,%rbx)
vmovupd %zmm5, 376(%rax,%rbx)
vmovupd %zmm10, 440(%rax,%rbx) {%k1}
vmovupd %zmm7, 496(%rax,%rbx)
vmovupd %zmm8, 560(%rax,%rbx)
vmovupd %zmm9, 624(%rax,%rbx)
vmovupd %zmm14, 688(%rax,%rbx) {%k1}
vmovupd %zmm11, 744(%rax,%rbx)
vmovupd %zmm12, 808(%rax,%rbx)
vmovupd %zmm13, 872(%rax,%rbx)
vmovupd %zmm18, 936(%rax,%rbx) {%k1}
vmovupd %zmm15, 992(%rax,%rbx)
vmovupd %zmm16, 1056(%rax,%rbx)
vmovupd %zmm17, 1120(%rax,%rbx)
vmovupd %zmm20, 1184(%rax,%rbx) {%k1}
vmovupd %zmm24, 1240(%rax,%rbx)
vmovupd %zmm25, 1304(%rax,%rbx)
vmovupd %zmm26, 1368(%rax,%rbx)
vfmadd231pd %zmm28, %zmm27, %zmm19 # zmm19 = (zmm27 * zmm28) + zmm19
vmovupd %zmm19, 1432(%rax,%rbx) {%k1}
addq $248, %rdx
testq %r12, %r12
leaq 1(%r12), %r12
jne L112
addq $1776, %rsi # imm = 0x6F0
cmpq $4, %r9
leaq 1(%r9), %r9
jne L48
movq (%rdi), %rax
movq -16(%rsp), %rcx
movq (%rcx), %rsi
movq -8(%rsp), %rcx
movq (%rcx), %rcx
vmovupd (%rsi), %zmm16
vmovupd 64(%rsi), %zmm18
vmovupd 128(%rsi), %zmm19
vmovupd 192(%rsi), %zmm20
vbroadcastsd 7104(%rcx), %zmm3
vmulpd %zmm16, %zmm3, %zmm0
vmulpd %zmm18, %zmm3, %zmm1
vmulpd %zmm19, %zmm3, %zmm2
vmulpd %zmm20, %zmm3, %zmm3
vbroadcastsd 7400(%rcx), %zmm7
vmulpd %zmm16, %zmm7, %zmm4
vmulpd %zmm18, %zmm7, %zmm5
vmulpd %zmm19, %zmm7, %zmm6
vmulpd %zmm20, %zmm7, %zmm7
vbroadcastsd 7696(%rcx), %zmm11
vmulpd %zmm16, %zmm11, %zmm8
vmulpd %zmm18, %zmm11, %zmm9
vmulpd %zmm19, %zmm11, %zmm10
vmulpd %zmm20, %zmm11, %zmm11
vbroadcastsd 7992(%rcx), %zmm15
vmulpd %zmm16, %zmm15, %zmm12
vmulpd %zmm18, %zmm15, %zmm13
vmulpd %zmm19, %zmm15, %zmm14
vmulpd %zmm20, %zmm15, %zmm15
vbroadcastsd 8288(%rcx), %zmm21
vmulpd %zmm16, %zmm21, %zmm17
vmulpd %zmm18, %zmm21, %zmm18
vmulpd %zmm19, %zmm21, %zmm19
vmulpd %zmm20, %zmm21, %zmm16
leaq 440(%rsi), %rdx
movq $-35, %rdi
nopw %cs:(%rax,%rax)
nopl (%rax,%rax)
L1408:
vmovapd %zmm19, %zmm20
vmovapd %zmm18, %zmm21
vmovapd %zmm17, %zmm22
vmovupd -192(%rdx), %zmm17
vmovupd -128(%rdx), %zmm18
vmovupd -64(%rdx), %zmm19
vmovupd (%rdx), %zmm23
vbroadcastsd 7392(%rcx,%rdi,8), %zmm24
vfmadd231pd %zmm24, %zmm17, %zmm0 # zmm0 = (zmm17 * zmm24) + zmm0
vfmadd231pd %zmm24, %zmm18, %zmm1 # zmm1 = (zmm18 * zmm24) + zmm1
vfmadd231pd %zmm24, %zmm19, %zmm2 # zmm2 = (zmm19 * zmm24) + zmm2
vfmadd231pd %zmm24, %zmm23, %zmm3 # zmm3 = (zmm23 * zmm24) + zmm3
vbroadcastsd 7688(%rcx,%rdi,8), %zmm24
vfmadd231pd %zmm24, %zmm17, %zmm4 # zmm4 = (zmm17 * zmm24) + zmm4
vfmadd231pd %zmm24, %zmm18, %zmm5 # zmm5 = (zmm18 * zmm24) + zmm5
vfmadd231pd %zmm24, %zmm19, %zmm6 # zmm6 = (zmm19 * zmm24) + zmm6
vfmadd231pd %zmm24, %zmm23, %zmm7 # zmm7 = (zmm23 * zmm24) + zmm7
vbroadcastsd 7984(%rcx,%rdi,8), %zmm24
vfmadd231pd %zmm24, %zmm17, %zmm8 # zmm8 = (zmm17 * zmm24) + zmm8
vfmadd231pd %zmm24, %zmm18, %zmm9 # zmm9 = (zmm18 * zmm24) + zmm9
vfmadd231pd %zmm24, %zmm19, %zmm10 # zmm10 = (zmm19 * zmm24) + zmm10
vfmadd231pd %zmm24, %zmm23, %zmm11 # zmm11 = (zmm23 * zmm24) + zmm11
vbroadcastsd 8280(%rcx,%rdi,8), %zmm24
vfmadd231pd %zmm24, %zmm17, %zmm12 # zmm12 = (zmm17 * zmm24) + zmm12
vfmadd231pd %zmm24, %zmm18, %zmm13 # zmm13 = (zmm18 * zmm24) + zmm13
vfmadd231pd %zmm24, %zmm19, %zmm14 # zmm14 = (zmm19 * zmm24) + zmm14
vfmadd231pd %zmm24, %zmm23, %zmm15 # zmm15 = (zmm23 * zmm24) + zmm15
vbroadcastsd 8576(%rcx,%rdi,8), %zmm24
vfmadd213pd %zmm22, %zmm24, %zmm17 # zmm17 = (zmm24 * zmm17) + zmm22
vfmadd213pd %zmm21, %zmm24, %zmm18 # zmm18 = (zmm24 * zmm18) + zmm21
vfmadd213pd %zmm20, %zmm24, %zmm19 # zmm19 = (zmm24 * zmm19) + zmm20
vfmadd231pd %zmm24, %zmm23, %zmm16 # zmm16 = (zmm23 * zmm24) + zmm16
addq $248, %rdx
incq %rdi
jne L1408
vmovupd 8928(%rsi), %zmm20
vmovupd 8992(%rsi), %zmm21
vmovupd 9056(%rsi), %zmm22
movb $127, %dl
kmovd %edx, %k1
vmovupd 9120(%rsi), %zmm23 {%k1} {z}
vbroadcastsd 7392(%rcx), %zmm24
vfmadd231pd %zmm24, %zmm20, %zmm0 # zmm0 = (zmm20 * zmm24) + zmm0
vfmadd231pd %zmm24, %zmm21, %zmm1 # zmm1 = (zmm21 * zmm24) + zmm1
vfmadd231pd %zmm24, %zmm22, %zmm2 # zmm2 = (zmm22 * zmm24) + zmm2
vfmadd231pd %zmm24, %zmm23, %zmm3 # zmm3 = (zmm23 * zmm24) + zmm3
vbroadcastsd 7688(%rcx), %zmm24
vfmadd231pd %zmm24, %zmm20, %zmm4 # zmm4 = (zmm20 * zmm24) + zmm4
vfmadd231pd %zmm24, %zmm21, %zmm5 # zmm5 = (zmm21 * zmm24) + zmm5
vfmadd231pd %zmm24, %zmm22, %zmm6 # zmm6 = (zmm22 * zmm24) + zmm6
vfmadd231pd %zmm24, %zmm23, %zmm7 # zmm7 = (zmm23 * zmm24) + zmm7
vbroadcastsd 7984(%rcx), %zmm24
vfmadd231pd %zmm24, %zmm20, %zmm8 # zmm8 = (zmm20 * zmm24) + zmm8
vfmadd231pd %zmm24, %zmm21, %zmm9 # zmm9 = (zmm21 * zmm24) + zmm9
vfmadd231pd %zmm24, %zmm22, %zmm10 # zmm10 = (zmm22 * zmm24) + zmm10
vfmadd231pd %zmm24, %zmm23, %zmm11 # zmm11 = (zmm23 * zmm24) + zmm11
vbroadcastsd 8280(%rcx), %zmm24
vfmadd231pd %zmm24, %zmm20, %zmm12 # zmm12 = (zmm20 * zmm24) + zmm12
vfmadd231pd %zmm24, %zmm21, %zmm13 # zmm13 = (zmm21 * zmm24) + zmm13
vfmadd231pd %zmm24, %zmm22, %zmm14 # zmm14 = (zmm22 * zmm24) + zmm14
vfmadd231pd %zmm24, %zmm23, %zmm15 # zmm15 = (zmm23 * zmm24) + zmm15
vbroadcastsd 8576(%rcx), %zmm24
vfmadd213pd %zmm17, %zmm24, %zmm20 # zmm20 = (zmm24 * zmm20) + zmm17
vfmadd213pd %zmm18, %zmm24, %zmm21 # zmm21 = (zmm24 * zmm21) + zmm18
vfmadd213pd %zmm19, %zmm24, %zmm22 # zmm22 = (zmm24 * zmm22) + zmm19
vfmadd231pd %zmm24, %zmm23, %zmm16 # zmm16 = (zmm23 * zmm24) + zmm16
vmovupd %zmm0, 5952(%rax)
vmovupd %zmm1, 6016(%rax)
vmovupd %zmm2, 6080(%rax)
vmovupd %zmm3, 6144(%rax) {%k1}
vmovupd %zmm4, 6200(%rax)
vmovupd %zmm5, 6264(%rax)
vmovupd %zmm6, 6328(%rax)
vmovupd %zmm7, 6392(%rax) {%k1}
vmovupd %zmm8, 6448(%rax)
vmovupd %zmm9, 6512(%rax)
vmovupd %zmm10, 6576(%rax)
vmovupd %zmm11, 6640(%rax) {%k1}
vmovupd %zmm12, 6696(%rax)
vmovupd %zmm13, 6760(%rax)
vmovupd %zmm14, 6824(%rax)
vmovupd %zmm15, 6888(%rax) {%k1}
vmovupd %zmm20, 6944(%rax)
vmovupd %zmm21, 7008(%rax)
vmovupd %zmm22, 7072(%rax)
vmovupd %zmm16, 7136(%rax) {%k1}
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
popq %rbp
vzeroupper
retq
nop
The most striking difference from looking at the assembly is the far higher density of vmul
and vfmadd
instructions. These occur within blocks within loops in Julia code (the blocks being sized to avoid register spills). The Eigen assembly is far harder to follow, looking much more complex. And runs around 4 times slower.