Надлежащий способ включить SSE4 для каждой функции / блока кода?

Для одной из моих программ OS X у меня есть несколько оптимизированных случаев, в которых используются инструкции SSE4.1. На машинах только для SSE3 неоптимизированная ветвь запускается:

// SupportsSSE4_1 returns true on CPUs that support SSE4.1, false otherwise
if (SupportsSSE4_1()) {

    // Code that uses _mm_dp_ps, an SSE4 instruction

    ...

    __m128 hDelta   = _mm_sub_ps(here128, right128);
    __m128 vDelta   = _mm_sub_ps(here128, down128);

    hDelta = _mm_sqrt_ss(_mm_dp_ps(hDelta, hDelta, 0x71));
    vDelta = _mm_sqrt_ss(_mm_dp_ps(vDelta, vDelta, 0x71));

    ...

} else {
    // Equivalent code that uses SSE3 instructions
    ...
}

Чтобы скомпилировать вышесказанное, мне нужно было установить CLANG_X86_VECTOR_INSTRUCTIONS в sse4.1,

Тем не менее, это, кажется, инструктирует Clang, что можно использовать ROUNDSD Инструкция в любом месте моей программы. Следовательно, программа падает на компьютерах только с SSE3 с SIGILL: ILL_ILLOPC,

Какова лучшая практика для включения SSE4.1 только для строк кода внутри истинной ветви SupportsSSE4_1() если блок?

3 ответа

Решение

В настоящее время нет никакого способа нацелить различные расширения ISA на гранулярность блока / функции в clang. Вы можете сделать это только при гранулярности файла (поместите код SSE4.1 в отдельный файл и укажите этот файл для использования -msse4.1). Если это важная функция для вас, пожалуйста, отправьте отчет об ошибке, чтобы запросить его!

Тем не менее, я должен отметить, что на самом деле DPPS довольно мало в большинстве реальных сценариев (и с использованием DPPS даже замедляет некоторые последовательности кода!). Если эта конкретная кодовая последовательность не является критической, и вы тщательно измерили эффект от использования DPPS, это может не стоить особых проблем для SSE4.1, даже если эта функция компилятора доступна.

Вы можете сделать диспетчер процессора. Вы можете сделать это в одном файле, но вам придется компилировать дважды. Сначала с SSE4.1, а затем без, а затем ссылка в объектном файле для SSE4.1. Когда вы в первый раз звоните myfunc это вызывает функцию myfunc_dispatch который определяет набор инструкций и устанавливает указатель на myfunc_SSE41 или же myfunc_SSE3, В следующий раз, когда вы звоните myfunc он переходит прямо к функции для вашего набора команд.

//clang -c -O3 -msse4.1 foo.cpp -o foo_sse41.o
//clang -O3 -msse3 foo.cpp foo_sse41.o   

typedef float MyFuncType(float*);

MyFuncType myfunc, myfunc_SSE41, myfunc_SSE3, myfunc_dispatch;
MyFuncType * myfunc_pointer = &myfunc_dispatch;

#ifdef __SSE4_1__
float myfunc_SSE41(float* a) {
    //SSE41 code
}
#else
float  myfunc_SSE3(float *a) {
    //SSE3 code
}

float myfunc_dispatch(float *a) {
    if(SupportsSSE4_1()) {
        myfunc_pointer = myfunc_SSE41;
    }
    else {
        myfunc_pointer = myfunc_SSE3;
    }
    myfunc_pointer(a);
}

float myfunc(float *a) {
    (*myfunc_pointer)(a);
}
int main() {
    //myfunc(a);
}
#endif

В зависимости от ОС вы можете использовать что-то вроде функции Multiversioning в будущем. Я работаю над этой функцией прямо сейчас, но пройдет некоторое время, прежде чем она будет готова для использования в производственном компиляторе.

См. http://gcc.gnu.org/wiki/FunctionMultiVersioning для получения дополнительной информации.

Другие вопросы по тегам