Результаты от SIMD __m256i до __m256d - PullRequest
0 голосов
/ 27 февраля 2019

Я пытаюсь привести целочисленную переменную SIMD в двойное число.Но я не вижу, каков будет результат этой операции.Пример:

int arr[8]={12345678,12333333,12344444,12355555,12366666,12377777,12388888,12399999};
__m256i temp = _mm256_load_si256((__m256i *) arr);
__m256d temp2 = _mm256_castsi256_pd (temp);

в результате этой операции, какие члены в моем temp2?

Ответы [ 3 ]

0 голосов
/ 27 февраля 2019

Короткий ответ

Членами в temp2 будут:

{4.014635e-305, 4.062922e-305, 4.111209e-305, 4.159495e-305}

Как получить значения

Просто запишите данные SIMD в doubleверните массив и распечатайте его.

#include <stdio.h>
#include <immintrin.h>

int main(void) {
    int hoge[4]; /* hack that worked on tested environment to avoid Segmentation Fault */
    double result[4];
    int i;

    int arr[8]={12345678,12333333,12344444,12355555,12366666,12377777,12388888,12399999};
    __m256i temp = _mm256_load_si256((__m256i *) arr);
    __m256d temp2 = _mm256_castsi256_pd (temp);

    _mm256_storeu_pd(result, temp2);
    for (i = 0; i < 4; i++) printf("result[%d] = %.6e (%.15a)\n", i, result[i], result[i]);
    return 0;
}

Я запустил этот код в Wandbox и получил следующий вывод:

result[0] = 4.014635e-305 (0x1.c311500bc614e00p-1012)
result[1] = 4.062922e-305 (0x1.c87e300bc5c7c00p-1012)
result[2] = 4.111209e-305 (0x1.cdeb100bcb34a00p-1012)
result[3] = 4.159495e-305 (0x1.d357f00bd0a1800p-1012)

Вы можете записать данные SIMD в double массив через _mm256_storeu_pd().

Исключение может генерироваться, когда адрес, который не выровнен по 32 байта, передается в _mm256_load_si256(), поэтому вам следуетсделать выравнивание.На самом деле произошел сбой сегментации в Wandbox, поэтому я вставил фиктивный массив hoge для выравнивания.

Почему были получены значения

_mm256_castsi256_pd() на самом деле простокопирование байтов и изменение их интерпретации.

Предполагая, что используется little-endian и int имеет длину 4 байта, данные в arr в байтовой памяти выглядят так:

data in arr[8]:
|   12345678|   12333333|   12344444|   12355555|   12366666|   12377777|   12388888|   12399999|
byte data in arr[8] (in little endian):
|4e 61 bc 00|15 31 bc 00|7c 5c bc 00|e3 87 bc 00|4a b3 bc 00|b1 de bc 00|18 0a bd 00|7f 35 bd 00|
data seen as 64-bit hex:
|     0x00bc311500bc614e|     0x00bc87e300bc5c7c|     0x00bcdeb100bcb34a|     0x00bd357f00bd0a18|

Затем, предполагая, что в double используется 64-битная IEEE754 , 64-битные данные состоят из 1-битного знака, 11-битного показателя степени и 52-битного значения и

В качестве первого элемента 0x00bc311500bc614e в качестве примера бит знака равен 0 (плюс / ноль), показатель степени равен 0x00b (11 - 1023 = -1012), а значение равно 0xc311500bc614e.

Это соответствует тому, что напечатано с помощью %.15a в приведенном выше примере кода.(печатаются два дополнительных 0 с, потому что указана печать 15 цифр, а переставляются только данные для 13 цифр, поэтому остаток дополняется 0.) Другие элементы также совпадают следующим образом.

0 голосов
/ 27 февраля 2019

Операция _mm256_castsi256_pd буквально ничего не делает, это реинтерпретация - эквивалентно:

  int v_i;
  double d_i = *((double*)(int*)&v_i).

Использовать __m256d _mm256_cvtepi32_pd (__m128i a), поскольку оно фактически преобразует 4 целых числа в 4 двойных.

alignas(16) int arr[4]={12345678,12333333,12344444,12355555};
__m128i temp = _mm_load_si128((__m128i *) arr);
__m256d temp2 = _mm256_cvtepi32_pd(temp);

Примечание: операции загрузки _mm_load_si128 и _mm256_load_si256 требуют правильного выравнивания адресов.В противном случае используйте версии без выравнивания _mm_loadu_si128 и _mm256_loadu_si256;думал, что невыровненные версии медленнее.

0 голосов
/ 27 февраля 2019

В результате этой операции temp2 будет содержать мусор.Например, первая двойная полоса движения будет 4.0146351468550722e-305.

Это сделано специально._mm256_castsi256_pd intrinsic не преобразует значения, он только повторно интерпретирует биты в регистре как double.

Если вы хотите, чтобы эти двойные константы были в регистре, просто используйте _mm256_setr_pd intrinsic:

// Set double values to the constants
__m256d temp2 = _mm256_setr_pd( 12345678, 12333333, 12344444, 12355555 );

Или, если эти значения не являются постоянными, используйте _mm256_cvtepi32_pd intrinsic, вот полный пример:

alignas( 32 ) int arr[ 8 ] = { 12345678, 12333333, 12344444, 12355555,
    12366666, 12377777, 12388888, 12399999 };
__m256i integers = _mm256_load_si256( ( const __m256i* ) &arr );
// Convert first 4 int32 values to doubles
__m256d lowDoubles = _mm256_cvtepi32_pd( _mm256_castsi256_si128( integers ) );
// Convert last 4 values to doubles
__m256d highDoubles = _mm256_cvtepi32_pd( _mm256_extracti128_si256( integers, 1 ) );

Это на самом деле будет преобразовывать значения, а не приведение битов.

Регистры AVXдержать 256 бит данных.Это 8 значений типа int32 в типе __m256i, 8 значений типа float в типе данных __m256, но только 4 двойных значения в типе __m256d.

PS В вашем коде также есть ошибка выравнивания, лучший способисправить это добавить alignas(32) до int arr[8]

...