Встроенные функции FMA не работают: аппаратное обеспечение или компилятор?
Я пытаюсь использовать встроенные функции Intel FMA, такие как _mm_fmadd_ps (__m128 a, __m128 b, __m128 c), чтобы повысить производительность моего кода.
Итак, прежде всего, я сделал небольшую тестовую программу, чтобы посмотреть, что она может делать и как я могу их использовать.
#include <stdio.h>
#include <stdlib.h>
#include "xmmintrin.h"
int main()
{
__m128 v1,v2,v3,vr;
v1 = _mm_set_ps (5.0, 5.0, 5.0, 5.0);
v2 = _mm_set_ps (2.0, 2.0, 2.0, 2.0);
v3 = _mm_set_ps (3.0, 3.0, 3.0, 3.0);
vr = _mm_fmadd_ps (v1, v2, v3);
}
и у меня есть эта ошибка:
vr = ошибка: несовместимые типы при назначении типу __m128 из типа int: vr = _mm_fmadd_ps (v1, v2, v3);
Я подумал, что, вероятно, возможности процессора не позволяют использовать такие инструкции, поэтому я посмотрел в интернете свою модель процессора ( процессор Intel® Core™ i7-4700MQ) и обнаружил, что она поддерживает только SSE4.1/4.2, Особенности AVX 2.0, которые были немного странными для меня! Поэтому я посмотрел в файле proc/cpuinfo и в разделе флагов нашел флаг ** fma **. Это запутанная часть об оборудовании.
Что касается программного обеспечения, я использовал эту опцию makefile после некоторого поиска в Интернете, и я надеюсь, что это не проблема.
CC=gcc
CFLAGS=-g -c -Wall -O2 -mavx2 -mfma
И я использую eclipse на Ubuntu 12.04 LTS с версией GCC 4.9.4 Спасибо.
2 ответа
Одна из странностей языка C заключается в том, что язык указывает, что компилятор должен принять символ, которого он раньше не видел, должен вернуть int
если вы называете это как функцию. Поскольку вы не включили заголовок, который фактически определяет подпись для _mm_fmadd_ps
, вы получите странную ошибку при конвертации int
в __m128
,
Первоначальная организация встроенных заголовков состояла в том, чтобы иметь уникальный заголовок для каждого поколения инструкций, поэтому вы имели:
mmintrin.h The original MMX instruction set (deprecated for x64 native)
mm3dnow.h The AMD 3D Now! instruction set (deprecated for x64 native)
emmintrin.h SSE (i.e. single-precision 4-wide SIMD)
xmmintrin.h SSE2 (i.e. double-precision and integer 4-wide SIMD)
После этого они начали использовать кодовые имена архитектуры процессора, где были введены новые инструкции.
pmmintrin.h SSE3 (the p stands for Prescott)
tmmintrin.h Supplemental SSE3 (the t stands for Tejas)
smmintrin.h SSE4.1 (not sure what the s is here for.
They were added for Penryn but p
was already used for Prescott)
nmmintrin.h SSE4.2 (the n stands for Nehalem)
wmmintrin.h AES (the w stands for Westmere)
В наши дни новые наборы инструкций имеют тенденцию заканчиваться ammintrin.h
для вещей, происходящих от AMD (ABM, BMI, LWP, TBM, XOP, FMA4, SSE4a, SSE5) или immintrin.h
для вещей, созданных Intel (AVX, FMA3, F16C, AVX2 и т. д.). AVX-512 находится в zmmintrin.h
,
Старая система не была особенно интуитивной, но не новой. Количество подмножеств инструкций AMD определено в immintrin.h
потому что они одна и та же инструкция. Поиск в документации или в заголовке - это действительно единственный способ узнать, что является внутренним.
Для Intel этот сайт является хорошим справочным материалом. В противном случае вам нужно увидеть руководства разработчика для AMD и / или Intel.
Вы можете найти эту серию моих полезных блогов.
-Mfma может показаться чем-то вроде беспокойства, но это не зря. Результат
_mm_add_ps(_mm_mul_ps(a, b), c)
_mm_fmadd_ps(a, b, c)
На самом деле отличаются. Если вы пишете код, который должен вычислять одинаковые результаты на всех машинах, на которых вы выполняете код (детерминизм), то вам, вероятно, потребуется отключить fma! Именно поэтому вам нужно включить его в сборке с -fma.
Тем не менее, по крайней мере, это не так плохо, как шесть флагов компиляции, которые вам понадобятся для процессоров SkyLake-X с поддержкой avx512:(