Является ли _mm_load_ps требованием для 128-битной выровненной структуры?

У меня есть настройка векторной структуры, похожая на эту: она выровнена на 128 бит, как и тип __m128.

struct Vector3
{
    union
    {
        float v[4];
        struct { float x,y,z,w; }
    }
}

Я использую инструкцию продукта SSE 4.1 Dot _mm_dp_ps. Требуется ли использовать _mm_load_ps для моей структуры выше, которая уже выровнена на 128 битов, или я могу напрямую привести свою структуру? Это безопасная практика?

ПРИМЕЧАНИЕ: Использование VS2013 и включить.

Текущий код с использованием _mm_load_ps:

float Vector3::Dot(const Vector3 & v) const
{
    __m128 a = _mm_load_ps(&this->v[0]);
    __m128 b = _mm_load_ps(&v.v[0]);

    __m128 res = _mm_dp_ps(a, b, 0x71);
    return res.m128_f32[0];
}

Код вопроса:

float Vector3::Dot(const Vector3 & v) const
{
    __m128 res = _mm_dp_ps(*(__m128*)&this->v[0], *(__m128*)&v.v[0], 0x71);
    return res.m128_f32[0];
}

Редактировать: закончил некоторое тестирование

Используя этот простой код консольного приложения, я провел 3 разных теста. Первый использует _mm_load_ps, второй переводит структуру к типу __m128 и, наконец, использует тип __m128 из объединения.

union Vector4
{
    Vector4(float x, float y, float z, float w) { a = x; b = y; c = z; d = w; }
    struct {float a, b, c, d;};
    __m128 m;
};

int _tmain(int argc, _TCHAR* argv[])
{
    const Vector4 vector_a = Vector4(1.0f, 2.0f, 3.0f, 4.0f);
    const Vector4 vector_b = Vector4(10.0f, 20.0f, 30.0f, 40.0f);

    unsigned long long start;

    // : Test Using _mm_load_ps :
    start = GetTickCount64();
    for (unsigned long long i = 0; i < 10000000000U; i++)
    {
        __m128 mx = _mm_load_ps((float*)&vector_a);
        __m128 my = _mm_load_ps((float*)&vector_b);

        __m128 res_a = _mm_add_ps(mx, my);
    }
    unsigned long long end_a = GetTickCount64() - start;

    // : Test Using Direct Cast to __m128 type :
    start = GetTickCount64();
    for (unsigned long long i = 0; i < 10000000000U; i++)
    {
        __m128 res_b = _mm_add_ps(*(__m128*)&vector_a, *(__m128*)&vector_b);
    }
    unsigned long long end_b = GetTickCount64() - start;

    // : Test Using __m128 type in Union :
    start = GetTickCount64();
    for (unsigned long long i = 0; i < 10000000000U; i++)
    {
        __m128 res_c = _mm_add_ps(vector_a.m, vector_b.m);
    }
    unsigned long long end_c = GetTickCount64() - start;

    return 0;
}

Результаты были следующими: end_a: 26489 тиков end_b: 19375 тиков end_c: 18767 тиков

Я также прошел через код, и все результаты res_a to res_c были правильными. Таким образом, этот тест показывает, что использование объединения быстрее.

Я понимаю, что тип __m128 по умолчанию является ссылкой на используемые регистры, а не типом, но при включении smmintrin.h __m128 становится объединением, которое определено в xmmintrin.h как

typedef union __declspec(intrin_type) _CRT_ALIGN(16) __m128 {
     float               m128_f32[4];
     unsigned __int64    m128_u64[2];
     __int8              m128_i8[16];
     __int16             m128_i16[8];
     __int32             m128_i32[4];
     __int64             m128_i64[2];
     unsigned __int8     m128_u8[16];
     unsigned __int16    m128_u16[8];
     unsigned __int32    m128_u32[4];
} __m128;

Поэтому я считаю, что инструкции, выполняемые с использованием встроенных включений, не ссылаются на регистры, а ссылаются на определенный тип __m128 из xmmintrin.h.

Итак, чтобы лучше повторить мой вопрос после этого теста: безопасно ли использовать структуру __m128, определенную в xmmintrin.h, внутри структуры для использования с внутренними функциями, доступными в Visual Studio 2013?

0 ответов

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