Arhn - архитектура программирования

Можно ли адресовать выходной регистр SIMD с помощью входного регистра

Можно ли использовать скалярные значения входного вектора для индексации выходного вектора? Я пытаюсь реализовать следующую функцию в SIMD, но не могу найти решения.

 void shuffle(unsigned char * a,    // input a
              unsigned char * r){   // output r
     for (i=0; i < 16; i++)
            r[i] = 0;
     for (i=0; i < 16; i++)
            r[a[i] % 16] = 1;
 }

Пример входного/выходного вектора будет выглядеть так

unsigned char * a = {0, 0, 0, 10, 0, 0, 0, 2, 0, 0, 0, 0, 3, 1, 0, 0 };
... do SIMD magic
//                   0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15
unsigned char * r = {1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 };

Мне не удалось найти подходящей инструкции, которая могла бы динамически обращаться к левой части присваивания. Может быть, эту функцию можно реализовать с помощью операций сдвига? Кто-нибудь реализовывал что-то подобное?

29.04.2015

  • Нет, вы не можете легко сделать это. Ближе всего будет _mm_shuffle_epi8, это перестановка общего назначения, но я не вижу очевидного способа применить ее здесь. Вам действительно нужна инверсия этой инструкции, которой не существует. 30.04.2015
  • В вашем примере элемент r[0] не должен также быть 1 ? 30.04.2015
  • Да, я думал, можно ли использовать инструкцию _mm_shuffle_epi8. Но я не мог придумать решение. Я боялся услышать это. Спасибо за быстрый ответ. И да, r[0] должно быть равно 1. 30.04.2015
  • FWIW вы можете сделать это с помощью цикла, который не более эффективен, чем исходная скалярная версия, но если это будет смешано с кучей другого SIMD-кода, то это может быть целесообразно, чтобы избежать скалярного кода в середина вашего потока инструкций SIMD. 30.04.2015
  • Думаете о сдвиге регистра 16 раз и сравнении с индексной позицией? 30.04.2015
  • Да, что-то в этом роде, хотя вы можете выйти раньше после обработки последнего индекса, поэтому в общем случае это может быть меньше 16 итераций, в зависимости от того, как обычно выглядят ваши данные. 30.04.2015

Ответы:


1

Кажется, что _mm_shuffle_epi8 действительно является ключом к решению. Идея состоит в том, чтобы установить отдельные биты в соответствии со значениями входного вектора a. Эти биты распределены по (горизонтальное ИЛИ) байтам 128-битного регистра.

#include <stdio.h>
#include <immintrin.h>
/*  gcc -O3 -Wall -mavx test4.c                                  */
/*  gcc -O3 -Wall -msse2 -mssse3 -msse4.1 test4.c                */

int print_char128(__m128i * x);

int print_char128(__m128i * x){
  unsigned char v_x[16];
  _mm_storeu_si128((__m128i *)v_x,*x);
  printf("%4u %4u %4u %4u | %4u %4u %4u %4u | %4u %4u %4u %4u | %4u %4u %4u %4u  \n",
  v_x[0],  v_x[1],  v_x[2],  v_x[3],  v_x[4],  v_x[5],  v_x[6],  v_x[7],
  v_x[8],  v_x[9],  v_x[10], v_x[11], v_x[12], v_x[13], v_x[14], v_x[15] );
  return 0;
}


int main()
{
unsigned char  a_v[] = {0, 0, 0, 10, 0, 0, 0, 2, 0, 0, 0, 0, 3, 1, 0, 0 };
/*unsigned char  a_v[] = {13, 30, 0, 10, 0, 6, 0, 2, 0, 0, 7, 0, 3, 11, 0, 0 };*/
  __m128i t0, t1, t2, t3;
  __m128i a, r, msk0, msk1, msk0_1, zero, bin_ones, one_epi8;

  /* set some constants */
  unsigned char  msk0_v[] ={1, 2, 4, 8, 16, 32, 64, 128, 0, 0, 0, 0, 0, 0, 0, 0};
  msk0=_mm_loadu_si128((__m128i *)msk0_v);
  msk1=_mm_shuffle_epi32(msk0,0b01001110);
  msk0_1=_mm_blend_epi16(msk0,msk1,0b11110000);
  zero=_mm_setzero_si128();
  bin_ones=_mm_cmpeq_epi32(zero,zero);
  one_epi8=_mm_sub_epi8(zero,bin_ones);

  /* load indices */
  a=_mm_loadu_si128((__m128i *)a_v);

  /* start of 'SIMD magic'                                            */
  /* index a_i sets the a_i -th bit within a byte of t0 if 0<=a_i<8  */
  /* or set (a_i-8)-th bit within a byte of t1 if 8<=a_i<16          */
  t0=_mm_shuffle_epi8(msk0,a);
  t1=_mm_shuffle_epi8(msk1,a);
  /* horizontal OR of the bytes in t0 and t1: */
  t2=_mm_blend_epi16(t0,t1,0b11110000);
  t3=_mm_alignr_epi8(t1,t0,8);
  t0=_mm_or_si128(t2,t3);
  t1=_mm_shuffle_epi32(t0,0b10110001);
  t0=_mm_or_si128(t0,t1);
  t1=_mm_slli_si128(t0,2);
  t0=_mm_or_si128(t0,t1);
  t1=_mm_slli_si128(t0,1);
  t0=_mm_or_si128(t0,t1);
  t0=_mm_shuffle_epi32(t0,0b11110101);  /* end of horizontal OR */
  /* filter out the relevant bits */
  t0=_mm_and_si128(t0,msk0_1);
  t0=_mm_cmpeq_epi8(t0,zero);
  r=_mm_andnot_si128(t0,one_epi8);      /* the result is in r */
  print_char128(&r);

  return 0;
}

Это должно работать довольно быстро: помимо инструкций по установке констант и загрузке данных, это всего 15 инструкций SSEx. На современных процессорах все эти инструкции имеют задержку всего в 1 такт. (Взаимная) производительность еще меньше: 1/2 или 1/3 такта. Внутренний _mm_blend_epi16 — SSE4.1, некоторые другие — SSSE3.

01.06.2015
  • вау, ваше решение выглядит великолепно. Большое спасибо, что поделились этим. Я все еще пытаюсь понять все детали. 02.06.2015
  • Вам не нужны нули в качестве входных данных для _mm_cmpeq для генерации всех единиц. Любой вход равен самому себе. ЦП даже использует это преимущество, генерируя uop, который не зависит от предыдущего значения регистра, когда оба операнда одинаковы. (т. е. это распознается как инструкция, разрушающая зависимости.) 25.06.2015
  • NVM, похоже, вы используете свой zero reg в алгоритме. В противном случае вы могли бы получить one_epi8 от _mm_sign_epi8(bin_ones, bin_ones). Я думал, что есть простая инструкция инвертирования вектора, но, возможно, это не так. 25.06.2015
  • Новые материалы

    Коллекции публикаций по глубокому обучению
    Последние пару месяцев я создавал коллекции последних академических публикаций по различным подполям глубокого обучения в моем блоге https://amundtveit.com - эта публикация дает обзор 25..

    Представляем: Pepita
    Фреймворк JavaScript с открытым исходным кодом Я знаю, что недостатка в фреймворках JavaScript нет. Но я просто не мог остановиться. Я хотел написать что-то сам, со своими собственными..

    Советы по коду Laravel #2
    1-) Найти // You can specify the columns you need // in when you use the find method on a model User::find(‘id’, [‘email’,’name’]); // You can increment or decrement // a field in..

    Работа с временными рядами спутниковых изображений, часть 3 (аналитика данных)
    Анализ временных рядов спутниковых изображений для данных наблюдений за большой Землей (arXiv) Автор: Рольф Симоэс , Жильберто Камара , Жильберто Кейрос , Фелипе Соуза , Педро Р. Андраде ,..

    3 способа решить квадратное уравнение (3-й мой любимый) -
    1. Методом факторизации — 2. Используя квадратичную формулу — 3. Заполнив квадрат — Давайте поймем это, решив это простое уравнение: Мы пытаемся сделать LHS,..

    Создание VR-миров с A-Frame
    Виртуальная реальность (и дополненная реальность) стали главными модными терминами в образовательных технологиях. С недорогими VR-гарнитурами, такими как Google Cardboard , и использованием..

    Демистификация рекурсии
    КОДЕКС Демистификация рекурсии Упрощенная концепция ошеломляющей О чем весь этот шум? Рекурсия, кажется, единственная тема, от которой у каждого начинающего студента-информатика..