У меня есть шейдер, который мне нужно оптимизировать (с большим количеством векторных операций), и я экспериментирую с инструкциями SSE, чтобы лучше понять проблему.
У меня есть очень простой пример кода. С USE_SSE
определить это использует явные SSE-свойства; без этого я надеюсь, что GCC сделает всю работу за меня. Авто-векторизация кажется немного привередливой, но я надеюсь, что это спасет меня от волос.
Компилятор и платформа: gcc 4.7.1 (tdm64), target x86_64-w64-mingw32 и Windows 7 на Ivy Bridge.
Вот тестовый код:
/*
Include all the SIMD intrinsics.
*/
#ifdef USE_SSE
#include <x86intrin.h>
#endif
#include <cstdio>
#if defined(__GNUG__) || defined(__clang__)
/* GCC & CLANG */
#define SSVEC_FINLINE __attribute__((always_inline))
#elif defined(_WIN32) && defined(MSC_VER)
/* MSVC. */
#define SSVEC_FINLINE __forceinline
#else
#error Unsupported platform.
#endif#ifdef USE_SSE
typedef __m128 vec4f;
inline void addvec4f(vec4f &a, vec4f const &b)
{
a = _mm_add_ps(a, b);
}
#else
typedef float vec4f[4];
inline void addvec4f(vec4f &a, vec4f const &b)
{
a[0] = a[0] + b[0];
a[1] = a[1] + b[1];
a[2] = a[2] + b[2];
a[3] = a[3] + b[3];
}
#endif
int main(int argc, char *argv[])
{
int const count = 1e7;
#ifdef USE_SSE
printf("Using SSE.\n");
#else
printf("Not using SSE.\n");
#endif
vec4f data = {1.0f, 1.0f, 1.0f, 1.0f};
for (int i = 0; i < count; ++i)
{
vec4f val = {0.1f, 0.1f, 0.1f, 0.1f};
addvec4f(data, val);
}
float result[4] = {0};
#ifdef USE_SSE
_mm_store_ps(result, data);
#else
result[0] = data[0];
result[1] = data[1];
result[2] = data[2];
result[3] = data[3];
#endif
printf("Result: %f %f %f %f\n", result[0], result[1], result[2], result[3]);
return 0;
}
Это скомпилировано с:
g++ -O3 ssetest.cpp -o nossetest.exe
g++ -O3 -DUSE_SSE ssetest.cpp -o ssetest.exe
Кроме явной SSE-версии, которая немного быстрее, нет никакой разницы в выводе.
Вот сборка для цикла, сначала явного SSE:
.L3:
subl $1, %eax
addps %xmm1, %xmm0
jne .L3
Это встроенный вызов. Хорошо, более или менее просто прямо _mm_add_ps
,
Версия массива:
.L3:
subl $1, %eax
addss %xmm0, %xmm1
addss %xmm0, %xmm2
addss %xmm0, %xmm3
addss %xmm0, %xmm4
jne .L3
Он хорошо использует математику SSE, но для каждого члена массива. Не очень желательно.
Мой вопрос, как я могу помочь GCC, чтобы он мог лучше оптимизировать версию массива vec4f
?
Любые специфические советы для Linux тоже полезны, вот где будет работать настоящий код.
это LockLess
статья о Авто-векторизация с gcc 4.7 Это лучшая статья, которую я когда-либо видел, и я потратил некоторое время на поиск хороших статей на подобные темы. У них также есть много других статьи что вы можете найти очень полезными на подобные темы, касающиеся всех способов разработки программного обеспечения низкого уровня.
Вот несколько советов, основанных на вашем коде для того, чтобы сделать автоматическую векторизацию gcc:
удалить inline
ключевое слово. если код помечен как встроенный, GCC не может знать, выровнена ли начальная точка массива без межпроцессного анализа, который не будет включен -O3
,
Итак, чтобы ваш код векторизовал, ваш addvec4f
Функция должна быть изменена следующим образом:
void addvec4f(vec4f &a, vec4f const &b)
{
int i = 0;
for(;i < 4; i++)
a[i] = a[i]+b[i];
}
КСТАТИ:
-ftree-vectorizer-verbose=2
большее число будет иметь больше выходной информации, в настоящее время значение может быть 0
,1
,2
,Вот это документация этого флага и некоторых других связанных флагов.bus error
если данные не выровнены. Вот это причина.