Эффективно деперемеживать четные и нечетные биты из целого числа в руке / неоне [дубликат]

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

Самый удобный способ найти все проблемные строки в проекте (не используя полномасштабный статический анализатор кода, который очень точен, но я не знаю никого, что приведет вас к правильному позиция в редакторе сразу) использовала Visual Studio Code, в которой есть хороший PHP-интерфейс, и его функция поиска, которая позволяет искать в Regex. (Конечно, вы можете использовать любой редактор IDE / Code для этого, который выполняет поиск PHP и поиск в Regex.)

С помощью этого регулярного выражения:

^(?!.*function).*(\&\$)

можно выполнить поиск проекта для всей сети &$ только в строках, которые не являются определением функции.

Это все еще вызывает много ложных срабатываний, но это облегчает работу.

Браузер результатов поиска VSCode делает прохождение и поиск оскорбительных линий очень просто: вы просто щелкните через каждый результат, и обратите внимание на те, которые linter подчеркивает красный. Те, которые вам нужно исправить.

3
задан Paul R 2 April 2018 в 07:01
поделиться

3 ответа

Если вам удастся использовать специфические для архитектуры инструкции, вы, скорее всего, сможете ускорить операцию за пределами того, что возможно, используя бит-twiddeling hacks:

Например, если вы пишете код для Intel Haswell и более поздние процессоры вы можете использовать набор команд BMI2, содержащий команды pext и pdep. Они могут (среди других замечательных вещей) использоваться для создания ваших функций.

Вот полный пример (проверенный с помощью GCC):

#include <immintrin.h>
#include <stdint.h>

// on GCC, compile with option -mbmi2, requires Haswell or better.

uint64_t xy_to_morton(uint32_t x, uint32_t y)
{
  return _pdep_u32(x, 0x55555555) | _pdep_u32(y,0xaaaaaaaa);
}

void morton_to_xy(uint64_t m, uint32_t *x, uint32_t *y)
{
  *x = _pext_u64(m, 0x5555555555555555);
  *y = _pext_u64(m, 0xaaaaaaaaaaaaaaaa);
}

Если вам нужно поддерживать более ранние процессоры или платформа ARM не все потеряно. Вы по-прежнему можете получить хотя бы помощь для функции xy_to_morton из инструкций, специфичных для криптографии.

В наши дни многие процессоры имеют поддержку переноса без потерь. В ARM, который будет vmul_p8 из набора команд NEON. На X86 вы найдете его как PCLMULQDQ из набора команд CLMUL (доступного с 2010 года).

Трюк здесь в том, что переносное умножение числа с собой вернет бит- который содержит исходные биты аргумента с чередованием нулевых битов. Таким образом, он идентичен показанному выше _pdep_u32 (x, 0x55555555). Например. он превращает следующий байт:

 +----+----+----+----+----+----+----+----+
 | b7 | b6 | b5 | b4 | b3 | b2 | b1 | b0 |
 +----+----+----+----+----+----+----+----+

В:

 +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+
 | 0  | b7 | 0  | b6 | 0  | b5 | 0  | b4 | 0  | b3 | 0  | b2 | 0  | b1 | 0  | b0 |
 +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+

Теперь вы можете построить функцию xy_to_morton как (здесь показано для набора команд CLMUL):

#include <wmmintrin.h>
#include <stdint.h>

// on GCC, compile with option -mpclmul

uint64_t carryless_square (uint32_t x)
{
  uint64_t val[2] = {x, 0};
  __m128i *a = (__m128i * )val;
  *a = _mm_clmulepi64_si128 (*a,*a,0);
  return val[0];
}

uint64_t xy_to_morton (uint32_t x, uint32_t y)
{
  return carryless_square(x)|(carryless_square(y) <<1);
}

_mm_clmulepi64_si128 генерирует 128-битный результат, из которого мы используем только младшие 64 бит. Таким образом, вы можете даже улучшить версию выше и использовать один _mm_clmulepi64_si128, чтобы выполнить эту работу.

Это так хорошо, как вы можете попасть на основные платформы (например, современные ARM с NEON и x86). К сожалению, я не знаю никакого трюка, чтобы ускорить работу функции morton_to_xy с помощью инструкций по шифрованию, и я пробовал очень много работать в течение нескольких месяцев.

9
ответ дан Pavel 15 August 2018 в 19:47
поделиться
  • 1
    Действительно здорово. Цените. – Dawid Szymański 30 May 2015 в 00:45
  • 2
    @ DawidSzymański Если вы хотите больше, я предлагаю вам проверить этот блог: bitmath.blogspot.de и прочитать о tesseral арифметике (это делает арифметику с номерами, хранящимися в последовательности morton без кодирования / декодирования). Я уверен, что вы можете использовать его для заполнения пробелов. – Nils Pipenbrinck 30 May 2015 в 00:52
  • 3
    Мне нравится этот clmul трюк, я не понял, что это будет делать это – harold 30 May 2015 в 14:09
  • 4
    @harold, забавный факт: нам нравится математический odditiy бит-twiddeling степени операции x * x в GF (2'm). Однако крипто-людям нравится иметь быстрый sqrt (x) в GF (2'm). Они уже узнали о том, что речь идет о разделении даже с нечетными битами, но они еще не знают бит-трюки. Я думаю, что каждый может учиться на этом! – Nils Pipenbrinck 31 May 2015 в 20:20
  • 5
    @NilsPipenbrinck ударил этот ответ после столь долгого времени, любознательный факт, существуют ли они для 3D-пространства? скажем, кодирование х, у, z в порядке Z и наоборот. – datapanda 7 May 2018 в 00:20
void xy2d_morton(uint64_t x, uint64_t y, uint64_t *d)
{
    x = (x | (x << 16)) & 0x0000FFFF0000FFFF;
    x = (x | (x << 8)) & 0x00FF00FF00FF00FF;
    x = (x | (x << 4)) & 0x0F0F0F0F0F0F0F0F;
    x = (x | (x << 2)) & 0x3333333333333333;
    x = (x | (x << 1)) & 0x5555555555555555;

    y = (y | (y << 16)) & 0x0000FFFF0000FFFF;
    y = (y | (y << 8)) & 0x00FF00FF00FF00FF;
    y = (y | (y << 4)) & 0x0F0F0F0F0F0F0F0F;
    y = (y | (y << 2)) & 0x3333333333333333;
    y = (y | (y << 1)) & 0x5555555555555555;

    *d = x | (y << 1);
}

// morton_1 - extract even bits

uint64_t morton_1(uint64_t x)
{
    x = x & 0x5555555555555555;
    x = (x | (x >> 1)) & 0x3333333333333333;
    x = (x | (x >> 2)) & 0x0F0F0F0F0F0F0F0F;
    x = (x | (x >> 4)) & 0x00FF00FF00FF00FF;
    x = (x | (x >> 8)) & 0x0000FFFF0000FFFF;
    x = (x | (x >> 16)) & 0xFFFFFFFFFFFFFFFF;
    return x;
}

void d2xy_morton(uint64_t d, uint64_t *x, uint64_t *y)
{
    *x = morton_1(d);
    *y = morton_1(d >> 1);
}
7
ответ дан Dawid Szymański 15 August 2018 в 19:47
поделиться
  • 1
    В morton_1 не должно быть последнее значение 0x00000000FFFFFFFF? – Todd Lehman 31 July 2015 в 01:34
  • 2
    постскриптум morton_1 может вернуть uint32_t. – Todd Lehman 31 July 2015 в 01:35

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

uint32_t x;
uint32_t y;
uint64_t z = 0;

for (int i = 0; i < sizeof(x) * 8; i++)
{
  z |= (x & (uint64_t)1 << i) << i | (y & (uint64_t)1 << i) << (i + 1);
}

. Если вам нужно ускорить бит, то это должно работать. Заметим, что x и y должны быть 64-битными переменными.

uint64_t x;
uint64_t y;
uint64_t z = 0;

x = (x | (x << 16)) & 0x0000FFFF0000FFFF;
x = (x | (x << 8)) & 0x00FF00FF00FF00FF;
x = (x | (x << 4)) & 0x0F0F0F0F0F0F0F0F;
x = (x | (x << 2)) & 0x3333333333333333;
x = (x | (x << 1)) & 0x5555555555555555;

y = (y | (y << 16)) & 0x0000FFFF0000FFFF;
y = (y | (y << 8)) & 0x00FF00FF00FF00FF;
y = (y | (y << 4)) & 0x0F0F0F0F0F0F0F0F;
y = (y | (y << 2)) & 0x3333333333333333;
y = (y | (y << 1)) & 0x5555555555555555;

z = x | (y << 1);
2
ответ дан Sami Kuhmonen 15 August 2018 в 19:47
поделиться
  • 1
    Больше интересуетесь быстрым способом и наоборот? – Dawid Szymański 29 May 2015 в 22:38
Другие вопросы по тегам:

Похожие вопросы: