Когда вы включаете оптимизацию, код не-SSE полностью исключается, тогда как код SSE остается там, поэтому этот случай тривиален. Более интересная часть - когда оптимизации отключены: в этом случае SSE-код все еще медленнее, тогда как код циклов тот же.
Код не-SSE тела самого внутреннего цикла:
movl $0x3dcccccd, %eax
movl %eax, -80(%rbp)
movl $0x3dcccccd, %eax
movl %eax, -76(%rbp)
movl $0x3dcccccd, %eax
movl %eax, -72(%rbp)
movl $0x3dcccccd, %eax
movl %eax, -68(%rbp)
movss -80(%rbp), %xmm1
movss -48(%rbp), %xmm0
mulss %xmm1, %xmm0
movss %xmm0, -80(%rbp)
movss -76(%rbp), %xmm1
movss -44(%rbp), %xmm0
mulss %xmm1, %xmm0
movss %xmm0, -76(%rbp)
movss -72(%rbp), %xmm1
movss -40(%rbp), %xmm0
mulss %xmm1, %xmm0
movss %xmm0, -72(%rbp)
movss -68(%rbp), %xmm1
movss -36(%rbp), %xmm0
mulss %xmm1, %xmm0
movss %xmm0, -68(%rbp)
SSE код тела самого внутреннего цикла:
movl $0x3dcccccd, %eax
movl %eax, -64(%rbp)
movl $0x3dcccccd, %eax
movl %eax, -60(%rbp)
movl $0x3dcccccd, %eax
movl %eax, -56(%rbp)
movl $0x3dcccccd, %eax
movl %eax, -52(%rbp)
leaq -48(%rbp), %rax
leaq -64(%rbp), %rdx
movaps (%rax), %xmm0
mulps (%rdx), %xmm0
movaps %xmm0, (%rdx)
Я не уверен в этом, но вот мое предположение:
Как видите, компилятор просто сохраняет 4 плавающих значения в 4 32-битных хранилищах. Это затем считывается 16-байтовой загрузкой. Это приводит к задержке при пересылке в магазин, что дорого обходится, когда это происходит. Вы можете посмотреть это в руководствах Intel. В скалярной версии этого не происходит, и это влияет на производительность.
Чтобы сделать это быстрее, вам нужно убедиться, что этот останов не произойдет. Если вы используете постоянный массив из 4 чисел с плавающей запятой, сделайте его константным и сохраните результаты в другом выровненном массиве. Таким образом, мы надеемся, что компилятор не сделает ненужные 4-байтовые перемещения перед загрузкой. Или, если вам нужно заполнить результирующий массив, выполните это с помощью 16-байтовой команды хранилища. Если вы не можете избежать этих 4-байтных перемещений, вам нужно сделать что-то еще после хранилища, но до загрузки (например, вычислить что-то еще).