#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()
Это преобразует 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).