SSE извлекает целые числа из __m128 для индексации массива

#c #sse

#c #sse

Вопрос:

В некотором коде, который я преобразовал в SSE, я предварительно выполняю некоторую трассировку лучей, отслеживая 4 луча одновременно, используя типы данных __m128.

В методе, где я определяю, какие объекты попадают первыми, я перебираю все объекты, проверяю на пересечение и создаю маску, представляющую, какие лучи пересеклись раньше, чем было найдено ранее .

Мне также нужно поддерживать данные об идентификаторах объектов, которые соответствуют наилучшему времени попадания. Я делаю это, поддерживая тип данных __m128 с именем objectNo, и я использую маску, определенную по времени пересечения, для обновления objectNo следующим образом:

 objectNo = _mm_blendv_ps(objectNo,_mm_set1_ps((float)pobj->getID()),mask);
  

Где pobj->getId() вернет целое число, представляющее идентификатор текущего объекта. Создание этого приведения и использование blend, казалось, были наиболее эффективным способом обновления objectNo для всех 4 лучей.

После того, как все пересечения протестированы, я пытаюсь извлечь objectNo по отдельности и использовать их для доступа к массиву для регистрации пересечения. Чаще всего я пробовал это:

 int o0 = _mm_extract_ps(objectNo, 0);
prv_noHits[o0]  ;
  

Однако это приводит к сбою с EXC_BAD_ACCESS, поскольку извлечение float со значением 1.0 преобразуется в int со значением 1065353216.

Как мне правильно распаковать __m128 в целые числа, которые можно использовать для индексации массива?

Ответ №1:

Есть два встроенных преобразования SSE2, которые, похоже, делают то, что вы хотите:

  • _mm_cvtps_epi32()
  • _mm_cvttps_epi32()

http://software.intel.com/sites/products/documentation/studio/composer/en-us/2011/compiler_c/intref_cls/common/intref_sse2_int_conversion.htm

Это преобразует 4 FP с одинарной точностью в 4 32-разрядных целых числа. Первый делает это с округлением. Во втором используется усечение.

Таким образом, их можно использовать следующим образом:

 int o0 = _mm_extract_epi32(_mm_cvtps_epi32(objectNo), 0);
prv_noHits[o0]  ;
  

РЕДАКТИРОВАТЬ: Основываясь на том, что вы пытаетесь сделать, я чувствую, что это можно лучше оптимизировать следующим образом:

 __m128i ids = _mm_set1_epi32(pobj->getID());

//  The mask will need to change
objectNo = _mm_blend_epi16(objectNo,ids,mask);

int o0 = _mm_extract_epi32(objectNo, 0);
prv_noHits[o0]  ;
  

Эта версия избавляется от ненужных преобразований. Но вам нужно будет использовать другой вектор маски.

РЕДАКТИРОВАТЬ 2: Вот способ, чтобы вам не пришлось менять свою маску:

 __m128 ids = _mm_castsi128_ps(_mm_set1_epi32(pobj->getID()));

objectNo = _mm_blendv_ps(objectNo,ids,mask);

int o0 = _mm_extract_ps(objectNo, 0);
prv_noHits[o0]  ;
  

Обратите внимание, что _mm_castsi128_ps() встроенная функция не отображает никаких инструкций. Это просто побитовое преобразование типов данных из __m128i в __m128 , чтобы обойти «типизированность» в C / C .

Комментарии:

1. Большое вам спасибо. Да, второй способ действительно кажется оптимальным, однако единственный способ, которым он будет действительно оптимальным, — это если я смогу преобразовать маску __m128 непосредственно в маску __m128i.

2. используя решение EDIT 2, я смог добиться ускорения в 3,9 по сравнению с 2,3, которое я видел при использовании первого решения. Урок стоимости приведения (_mm_cvtps_epi32).