Для тех, кто, как и я, читает это, потому что им нужно обновить гигантский проект наследия до 5.6: как показывают ответы здесь, нет быстрого решения: вам действительно нужно найти каждое возникновение проблемы вручную, и исправить.
Самый удобный способ найти все проблемные строки в проекте (не используя полномасштабный статический анализатор кода, который очень точен, но я не знаю никого, что приведет вас к правильному позиция в редакторе сразу) использовала Visual Studio Code, в которой есть хороший PHP-интерфейс, и его функция поиска, которая позволяет искать в Regex. (Конечно, вы можете использовать любой редактор IDE / Code для этого, который выполняет поиск PHP и поиск в Regex.)
С помощью этого регулярного выражения:
^(?!.*function).*(\&\$)
можно выполнить поиск проекта для всей сети &$
только в строках, которые не являются определением функции.
Это все еще вызывает много ложных срабатываний, но это облегчает работу.
Браузер результатов поиска VSCode делает прохождение и поиск оскорбительных линий очень просто: вы просто щелкните через каждый результат, и обратите внимание на те, которые linter подчеркивает красный. Те, которые вам нужно исправить.
Если вам удастся использовать специфические для архитектуры инструкции, вы, скорее всего, сможете ускорить операцию за пределами того, что возможно, используя бит-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 с помощью инструкций по шифрованию, и я пробовал очень много работать в течение нескольких месяцев.
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);
}
Наивный код будет таким же, независимо от количества бит. Если вам не нужна сверхскоростная версия с двойным сверлом, это будет делать
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);