SSE2 двойное умножение медленнее, чем при стандартном умножении - PullRequest
0 голосов
/ 03 июля 2011

Мне интересно, почему следующий код с инструкциями SSE2 выполняет умножение медленнее, чем стандартная реализация C ++.Вот код:

        m_win = (double*)_aligned_malloc(size*sizeof(double), 16);
        __m128d* pData = (__m128d*)input().data;
        __m128d* pWin = (__m128d*)m_win;
        __m128d* pOut = (__m128d*)m_output.data;
        __m128d tmp;
        int i=0;
        for(; i<m_size/2;i++)
            pOut[i] = _mm_mul_pd(pData[i], pWin[i]);

Память для m_output.data и input().data была выделена с помощью _aligned_malloc.

Время выполнения этого кода для массива 2 ^ 25 совпадает со временем для этого кода (350 мс):

for(int i=0;i<m_size;i++)
    m_output.data[i] = input().data[i] * m_win[i];

Как это возможно?Теоретически это должно занять только 50% времени, верно?Или издержки на передачу памяти из регистров SIMD в массив m_output.data так дороги?

Если заменить строку из первого фрагмента

pOut[i] = _mm_mul_pd(pData[i], pWin[i]);

на

tmp = _mm_mul_pd(pData[i], pWin[i]);

где __m128d tmp; тогда коды выполняются молниеносно быстро, меньше, чем разрешение моей функции таймера.Это потому, что все хранится в регистрах, а не в памяти?

И еще более удивительно, если я скомпилирую в режиме отладки, код SSE займет всего 93 мс , тогда как стандартное умножение 309ms .

  • ОТЛАДКА: 93 мс (SSE2) / 309 мс (стандартное умножение)
  • РЕЛИЗ: 350 мс (SSE2) / 350 (стандартное умножение)

Что происходитздесь ???

Я использую MSVC2008 с QtCreator 2.2.1 в режиме выпуска.Вот мои переключатели компилятора для RELEASE:

cl -c -nologo -Zm200 -Zc:wchar_t- -O2 -MD -GR -EHsc -W3 -w34100 -w34189

, и они для DEBUG:

cl -c -nologo -Zm200 -Zc:wchar_t- -Zi -MDd -GR -EHsc -W3 -w34100 -w34189

EDIT Относительно проблемы RELEASE vs DEBUG: я просто хотелобратите внимание, что я профилировал код, а код SSE работает медленнее в режиме выпуска! Это просто как-то подтверждает гипотезу о том, что VS2008 каким-то образом не может должным образом обрабатывать встроенные функции с оптимизатором.Intel VTune дает мне 289 мсек для цикла SSE в режиме отладки и 504 мсек в режиме RELEASE.Вау ... просто вау ...

Ответы [ 2 ]

3 голосов
/ 03 июля 2011

Прежде всего, VS 2008 - плохой выбор для intrisincs, поскольку он имеет тенденцию добавлять гораздо больше перемещений регистров, чем необходимо, и в целом не очень хорошо оптимизируется (например, он имеет проблемы с анализом переменных индукции цикла, когда выполняются инструкции SSE).присутствует.)

Итак, мое дикое предположение состоит в том, что компилятор генерирует mulss инструкции, которые ЦПУ может тривиально переупорядочивать и выполнять параллельно (без зависимостей между итерациями), в то время как интрицинки приводят к множеству перемещений регистров /сложный код SSE - он может даже взорвать кэш трассировки на современных процессорах.VS2008 печально известен тем, что выполняет все свои вычисления в регистрах, и я предполагаю, что есть некоторые опасности, которые ЦП не может пропустить (например, xor reg, move mem-> reg, xor, mov mem-> reg, mul, mov mem-> reg, которыйявляется цепочкой зависимостей, в то время как скалярный код может быть перемещен mem-> reg, mul с mem operand, mov.) Вы должны обязательно взглянуть на сгенерированную сборку или попробовать VS 2010, который имеет намного лучшую поддержку встроенных функций.

Наконец, и самое важное: ваш код вообще не связан с вычислениями , никакое количество SSE не сделает его значительно быстрее.На каждой итерации вы читаете четыре двойных значения и записываете два, что означает, что FLOP не является вашей проблемой.В этом случае вы находитесь во власти подсистемы кеш / памяти, и это, вероятно, объясняет разницу, которую вы видите.Умножение отладки не должно быть быстрее, чем выпуск;и если вы видите, что это быстрее, чем вы должны делать больше прогонов и проверять, что еще происходит (будьте осторожны, если ваш процессор поддерживает турбо-режим, это добавляет еще 20% вариаций.) Переключения контекста, который очищает кэш, может быть достаточно вэто случай.

Итак, в целом, тест, который вы сделали, в значительной степени бессмысленен и просто показывает, что для случаев с привязкой к памяти нет разницы, использовать SSE или нет.Вы должны использовать SSE, если на самом деле есть код, который является плотно вычисляемым и параллельным, и даже тогда я потратил бы много времени с профилировщиком, чтобы определить точное место для оптимизации.Простой точечный продукт не подходит для улучшения производительности SSE.

1 голос
/ 04 июля 2011

Несколько баллов:

  • Как уже указывалось, MSVC генерирует довольно плохой код для SSE
  • ваш код почти наверняка ограничен пропускной способностью памяти, так как вы выполняете только одну операцию между загрузками и хранилищами
  • большинство современных процессоров x86 имеют два ALU с плавающей запятой, поэтому использование SSE для математики с плавающей запятой двойной точности может быть мало выгодным, даже если вы не ограничены в пропускной способности
...