Побитовая перестановка нескольких 64-битных значений параллельно / в сочетании

этот вопрос не о том, "как я побитовая перестановка" мы теперь как это сделать, то, что мы ищем, это более быстрый способ с меньшим количеством инструкций процессора, вдохновленный реализацией bitslice sboxes в DES

чтобы ускорить некоторый код шифрования, мы хотим уменьшить количество вызовов перестановок. Основные функции шифрования выполняют несколько побитовых перестановок на основе массивов поиска. Как операции перестановки только bitshifts,

наша основная идея это взять несколько входных значений, которые нуждаются в одной и той же перестановке, и сдвинуть их параллельно. Например, если входной бит 1 Необходимо переместить в выходной бит 6.

есть ли способ сделать это? У нас нет примера кода прямо сейчас, потому что нет абсолютно никакого представления о том, как это сделать.

максимальный размер значения мы имеем на наших plattforms 128bit, самое длиннее значение входного сигнала 64bit.Поэтому код должен быть быстрее, а затем выполнять всю перестановку 128 раз.

редактировать

вот простой 8-битный пример перестановки

+---+---+---+---+---+---+---+---+
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | <= Bits
+---+---+---+---+---+---+---+---+
+---+---+---+---+---+---+---+---+
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | <= Input
+---+---+---+---+---+---+---+---+
| 3 | 8 | 6 | 2 | 5 | 1 | 4 | 7 | <= Output
+---+---+---+---+---+---+---+---+

шифр использует несколько входных ключей. Это блочный шифр, поэтому один и тот же шаблон должен применяться ко всем 64-битным блокам ввода.

поскольку перестановки одинаковы для каждого входного блока, мы хотим обработать несколько входных блоков в одном шаг / для объединения операций для нескольких входных последовательностей. Вместо перемещения 128times один бит за вызов, перемещение 1 раз 128 бит сразу.

EDIT2

мы не могли использовать потоки, так как мы должны запускать код во встроенных системах без поддержки потоков. Поэтому у нас также нет доступа к внешним библиотекам, и мы должны держать его простым C.

решение

после тестирования и игры с ответами мы сделали это следующим образом:

  • мы ставим сингл биты 128 64-битных значений в массиве uint128_t[64]*.
  • для перестановки нам нужно просто скопировать указатели
  • после того, как все будет сделано, мы вернем первую операцию и получим 128 перестановочных значений обратно

Да, это действительно так просто. Мы тестировали этот способ в начале проекта, но он был слишком медленным. Кажется, у нас была ошибка в testcode.

спасибо всем, за советы и терпение.

4 ответов


вы можете сделать код Стэна бит за бит быстрее, используя восемь таблиц поиска, отображающих байты в 64-разрядные слова. Чтобы обработать 64-разрядное слово из входных данных, разделите его на восемь байтов и найдите каждое из другой таблицы поиска или результатов. На моем компьютере последний в 10 раз быстрее, чем бит-битный подход для 32-битных перестановок. Очевидно, если ваша встроенная система имеет мало кэша, то 32 КБ 16 кб таблиц поиска может быть проблемой. Если вы процесс 4 бита время, вам нужно только 16 таблиц поиска 16*8=128 байт каждый, т. е. 2 кб таблиц поиска.

EDIT: внутренний цикл может выглядеть примерно так:

void permute(uint64_t* input, uint64_t* output, size_t n, uint64_t map[8][256])
{
    for (size_t i = 0; i < n; ++i) {
        uint8_t* p = (uint8_t*)(input+i);
        output[i] = map[0][p[0]] | map[1][p[1]] | map[2][p[2]] | map[3][p[3]]
            | map[4][p[4]] | map[5][p[5]] | map[6][p[6]] | map[7][p[7]];
    }
}

Я думаю, что вы, возможно, ищете бит-нарезки реализации. Это как самый быстрый де-трескать impelmentations работы. (Или это было до того, как существовали инструкции SSE.)

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

трюк состоит в том, чтобы использовать фактические биты одного регистра для представления один бит из нескольких входных слов.

я проиллюстрирую простой четырехбитной функцией.

предположим, например, вы хотите взять четырехразрядные входы формы:

x3 x2 x1 x0

...и для каждого входа вычислите четырехбитный выход:

x2 x3 x2^x3 x1^x2

и вы хотите сделать это, скажем, восемь входов. (OK для четырех бит a таблица поиска будет самой быстрой. Но это только для иллюстрации принципа.)

предположим, что ваши восемь входов:

A = a3 a2 a1 a0
B = b3 b2 b1 b0
...
H = h3 h2 h1 h0

здесь a3 a2 a1 a0 представляют четыре бита A input, etc.

во-первых, Закодируйте все восемь входов в четыре байта, где каждый байт содержит один бит от каждого из восьми входов:

X3 =  a3 b3 c3 d3 e3 f3 g3 h3
X2 =  a2 b2 c2 d2 e2 f2 g2 h2
X1 =  a1 b1 c1 d1 e1 f1 g1 h1
X0 =  a0 b0 c0 d0 e0 f0 g0 h0

здесь a3 b3 c3 ... h3 - это восемь битов X3. Он состоит из высоких битов всех восьми входов. X2 - это следующий бит из всех восьми входов. И так далее.

теперь, чтобы вычислить функцию восемь раз параллельно, вы просто делаете:

Y3 = X2;
Y2 = X3;
Y1 = X2 ^ X3;
Y0 = X1 ^ X2;

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

еще лучше, если наш процессор 32-разрядный (или 64-разрядный), мы могли бы вычислить эту функцию на 32 (или 64) входах, все еще используя только четыре инструкции.

кодирование ввода и декодирование вывода в / из представления "битовый срез" занимает некоторое время, конечно. Но для правильной функции этот подход предлагает массовый параллелизм на уровне битов и, следовательно, массовое ускорение.

основное предположение заключается в том, что у вас есть много входов (например, 32 или 64), на которых вы хотите вычислить ту же функцию, и что функция не слишком сложна и не слишком проста для представления в виде группы булевых операций. (Слишком жесткий делает необработанное вычисление медленным; слишком простой делает время, доминирующее над самим кодированием/декодированием битового среза.) Для криптографии, в частности, где (а) данные должны пройти через много "раундов" обработки, (б) алгоритм часто с точки зрения бит уже мунгинга, и (в) вы, например, пробуете много ключей на одних и тех же данных... Это часто работает довольно хорошо.


кажется трудным выполнить перестановку только в одном вызове. Частный случай вашей проблемы, обращая биты в целое число, требует более одного "вызова" (что вы подразумеваете под вызов?). См.бит Twiddling хаки Шон по информации этого примера.

Если ваш шаблон отображения не сложен, возможно, вы можете найти быстрый способ вычислить ответ:) однако я не знаю, нравится ли вам этот прямой способ:

#include <stdio.h>

unsigned char mask[8];

//map bit to position
//0 -> 2
//1 -> 7
//2 -> 5
//...
//7 -> 6
unsigned char map[8] = {
    2,7,5,1,4,0,3,6
};


int main()
{
    int i;

    //input:
    //--------------------
    //bit 7 6 5 4 3 2 1 0
    //--------------------
    //val 0 0 1 0 0 1 1 0
    //--------------------
    unsigned char input = 0x26;

    //so the output should be 0xA1:
    //    1 0 1 0 0 0 0 1
    unsigned char output;

    for(i=0; i<8; i++){ //initialize mask once
        mask[i] = 1<<i;
    }

    //do permutation
    output = 0;
    for(i=0; i<8; i++){
        output |= (input&mask[i])?mask[map[i]]:0;
    }

    printf("output=%x\n", output);
    return 0;
}

лучше всего было бы посмотреть на какой-то тип схемы резьбы ... либо вы можете использовать систему передачи сообщений, где вы отправляете каждый блок фиксированному набору рабочих потоков, либо вы можете настроить конвейер с неблокирующимися одиночными очередями производителя/потребителя, которые выполняют несколько сдвигов "синхронно". Я говорю "синхронный", потому что конвейер на ЦП общего назначения не был бы действительно синхронной операцией конвейера, как на устройстве с фиксированной функцией, но в основном для данного " среза "времени каждый поток будет работать на одном этапе многоступенчатой проблемы одновременно, и вы будете" передавать " исходные данные в конвейер и из него.