Сравнительный анализ инструкций SSE

Я сравниваю некоторый код SSE (умножающий 4 числа с плавающей запятой на 4 числа с плавающей запятой) с традиционным кодом C, выполняющим тоже самое. Я думаю, что мой тестовый код должен быть в чем-то неправильным, потому что он, кажется, говорит, что код без SSE быстрее, чем SSE, в 2-3 раза.

Может ли кто-нибудь сказать мне, что не так с кодом бенчмаркинга ниже? И, возможно, предложите другой подход, который точно показывает скорости как для кода SSE, так и для кода без SSE.

#include <time.h>
#include <string.h>
#include <stdio.h>

#define ITERATIONS 100000

#define MULT_FLOAT4(X, Y) ({ \
asm volatile ( \
    "movaps (%0), %%xmm0\n\t" \
    "mulps (%1), %%xmm0\n\t" \
    "movaps %%xmm0, (%1)" \
    :: "r" (X), "r" (Y)); })

int main(void)
{
    int i, j;
    float a[4] __attribute__((aligned(16))) = { 10, 20, 30, 40 };
    time_t timer, sse_time, std_time;

    timer = time(NULL);
    for(j = 0; j < 5000; ++j)
        for(i = 0; i < ITERATIONS; ++i) {
            float b[4] __attribute__((aligned(16))) = { 0.1, 0.1, 0.1, 0.1 };

            MULT_FLOAT4(a, b);

        }
    sse_time = time(NULL) - timer;

    timer = time(NULL);
    for(j = 0; j < 5000; ++j)
        for(i = 0; i < ITERATIONS; ++i) {
            float b[4] __attribute__((aligned(16))) = { 0.1, 0.1, 0.1, 0.1 };

            b[0] *= a[0];
            b[1] *= a[1];
            b[2] *= a[2];
            b[3] *= a[3];

    }
    std_time = time(NULL) - timer;

    printf("sse_time %d\nstd_time %d\n", sse_time, std_time);

    return 0;
}

person horseyguy    schedule 14.11.2009    source источник


Ответы (1)


Когда вы включаете оптимизацию, не-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-байтовых перемещений, вам нужно сделать что-то еще после сохранения, но до загрузки (например, вычислить что-то еще).

person ypsu    schedule 14.11.2009
comment
спасибо за Ваш ответ. :) Однако мне действительно нужен очень быстрый 4-кратный множитель с плавающей запятой, использующий SSE для обработки изображений. Если метод, который я использую, имеет какие-то недостатки, можете ли вы предложить другой способ сделать это, который использует мощь SSE, но не приводит к таким замедлениям. Я читал, что SSE на самом деле был разработан для обработки изображений, которую я имею в виду, поэтому наверняка должен быть способ сделать то, что я хочу? (Мне нужен быстрый множитель 4float для таких операций, как альфа-смешивание и тому подобное) - person horseyguy; 14.11.2009
comment
спасибо, я поищу "магазин переадресации" и попытаюсь понять, что здесь происходит - person horseyguy; 14.11.2009
comment