транспонировать 64-битные элементы, используя только avx, а не avx2

Я хочу реализовать 64-битную операцию транспонирования, используя только avx, а не avx2. Он должен сделать это:

// in  = Hh Hl Lh Ll
//        |   X   |
// out = Hh Lh Hl Ll

Вот как это будет выглядеть с avx2:

#define SIMD_INLINE inline __attribute__ ((always_inline))

static SIMD_INLINE __m256i
x_mm256_transpose4x64_epi64(__m256i a)
{
  return _mm256_permute4x64_epi64(a, _MM_SHUFFLE(3,1,2,0));
}

Это самый эффективный обходной путь без avx2, который я смог придумать (используя 3 инструкции avx):

static SIMD_INLINE __m256i
x_mm256_transpose4x64_epi64(__m256i a)
{
  __m256d in, x1, x2;
  // in = Hh Hl Lh Ll
  in = _mm256_castsi256_pd(a);
  // only lower 4 bit are used
  // in = Hh Hl Lh Ll
  //       0  1  0  1  = (0,0,1,1)
  // x1 = Hl Hh Ll Lh
  x1 = _mm256_permute_pd(in, _MM_SHUFFLE(0,0,1,1));
  // all 8 bit are used
  // x1 = Hl Hh Ll Lh
  //       0  0  1  1
  // x2 = Ll Lh Hl Hh
  x2 = _mm256_permute2f128_pd(x1, x1, _MM_SHUFFLE(0,0,1,1));
  // only lower 4 bit are used
  // in = Hh Hl Lh Ll
  // x2 = Ll Lh Hl Hh
  //       0  1  1  0 = (0,0,1,2)
  // ret: Hh Lh Hl Ll
  return _mm256_castpd_si256(_mm256_blend_pd(in, x2, _MM_SHUFFLE(0,0,1,2)));
}

Проблема в том, что большинство операций swizzle avx (например, распаковка) выполняются на 128-битных дорожках и не пересекают границу дорожки.

Может ли кто-нибудь создать более эффективную реализацию? Большое спасибо!


person Ralf    schedule 14.06.2016    source источник
comment
codereview.stackexchange.com — на мой взгляд, это более подходящий сайт для таких вопросов.   -  person Gluttton    schedule 14.06.2016
comment
@Gluttton: я не согласен - вопросы оптимизации здесь совершенно в тему - codereview больше подходит для рабочего кода, который можно улучшить идиоматически или стилистически.   -  person Paul R    schedule 14.06.2016
comment
@Ralf: Я сомневаюсь, что вы улучшите свое текущее решение с тремя инструкциями, учитывая все ограничения AVX, но, возможно, кто-то докажет, что я ошибаюсь.   -  person Paul R    schedule 14.06.2016
comment
@PaulR Неконкретные вопросы по оптимизации обсуждаются в Code Review. Однако в заголовке этого вопроса есть конкретный вопрос, поэтому я думаю, что это хороший вопрос о переполнении стека.   -  person 200_success    schedule 14.06.2016
comment
stackoverflow.com/questions/19516585/   -  person Z boson    schedule 15.06.2016


Ответы (1)


Я думаю, что 3 инструкции - это лучшее, что вы можете сделать. _mm256_blend_pd очень дешев (как vblendps и vpblendd), работает на 2 портах в SnB/IvB и на всех 3 портах векторного исполнения в Haswell и более поздних версиях. (т. е. так же дешево, как векторное XOR или AND.) Двум другим обоим нужен порт для перетасовки, и это неизбежно.

У вас будет задержка обхода в 1 цикл на процессорах семейства SnB, когда vblendpd пересылает свои данные из домена FP в целочисленную инструкцию. Хотя с AVX1 нет целочисленных инструкций 256b для пересылки.

(источник: см. таблицы insn Агнера Фога, ссылки на которые приведены в разделе x86 tag wiki. В его руководстве по оптимизации сборки также есть несколько хороших таблиц перетасовок, но он не фокусируется на проблемах AVX/AVX2 на линии.)


Этот шаблон почти достижим с двумя инструкциями, но не совсем.

vshufpd (_mm256_shuffle_pd) позволяет перетасовывать 2 источника на дорожке, но с ограничениями на движение данных. Как и в исходной версии SSE2, каждый целевой элемент может исходить только от фиксированного исходного элемента. В 8-битном непосредственном кодировании есть место для кодирования двух вариантов из четырех исходных элементов, но аппаратное обеспечение остается простым, и для каждого элемента назначения используется только 1-битный селектор. Версия 256b допускает различное перемешивание для каждой дорожки 128b, поэтому 4 бита imm8 имеют значение для vpshufd ymm.

В любом случае, поскольку верхняя дорожка должна брать свой старший элемент из оригинала, а нижняя дорожка должна брать свой старший элемент из вектора perm128, ни выбор порядка src1, ни src2 не может сделать то, что нам нужно.


vshufpd Я думаю, что байт короче для кодирования, чем vpermilpd imm8. Единственный вариант использования непосредственных форм vpermilps / vpermilpd, по-видимому, - это загрузка и перемешивание. (vshufpd работает как полная перетасовка в дорожке, только если оба исходных операнда одинаковы). IDK if vpermildp может потреблять меньше энергии или что-то в этом роде, поскольку у него только один источник.

Конечно, компиляторы могут использовать любые инструкции для выполнения своей работы; им разрешено оптимизировать код с помощью встроенных функций точно так же, как они оптимизируют код с помощью оператора + (который не всегда компилируется в инструкцию add). Clang на самом деле в основном игнорирует попытки выбора инструкций с использованием встроенных функций, поскольку он представляет перетасовки в своем собственном внутреннем формате и оптимизирует их.

person Peter Cordes    schedule 14.06.2016