Как получить контрольные суммы для пошаговых узоров - PullRequest
3 голосов
/ 28 февраля 2009

У меня есть 64-битное число (но используются только 42 младших разряда), и мне нужно вычислить сумму 4 битов в n, n+m, n+m*2 и n+m*3 (примечание: все, что может дать сумму> 4, недопустимо) для некоторого фиксированного m и каждого значения n, которое помещает все биты в число

в качестве примера, используя m=3 и учитывая 16-битное число

0010 1011 0110 0001

Мне нужно вычислить

2, 3, 1, 2, 3, 0, 3

У кого-нибудь есть (крутые) идеи о том, как это сделать? Я в порядке с немного вертеться.


В настоящее время моя мысль состоит в том, чтобы сделать сдвинутые по битам копии входных данных для выравнивания суммируемых значений, а затем построить логическое дерево для суммирования 4x 1 бит.

v1 = In;
v2 = In<<3;
v3 = In<<6;
v4 = In<<9;

a1 = v1 ^ v2;
a2 = v1 & v2;
b1 = v3 ^ v4;
b2 = v3 & v4;
c2 = a1 & b1;
d2 = a2 ^ b2;

o1 = a1 ^ b1;
o2 = c2 ^ d2;
o4 = a2 & b2;

Это в конечном итоге приводит к разбросу битов результата по 3 разным целым числам, ну да ладно.

редактировать: как это происходит, мне нужна гистограмма сумм, поэтому счетчик битов из o4, o2&o1, o2 и o1 дает мне то, что я хочу.


второе решение использует идеальную хеш-функцию

arr = [0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4];

for(int i = 0; i < N; i++)
{
   out[i] = arr[(In & 0b1001001001) % 30]; 
   In >>= 1;
}

Это работает, отмечая, что 4 выбранных бита могут принимать только 16 шаблонов и что (путем догадки и проверки) они могут быть хэшированы в 0-15 с использованием мода 30. Оттуда таблица вычисленных значений дает необходимую сумму , Поскольку это происходит только 3 из 4 шагов, мне нужно работать таким образом.


приписка

Правильный козырь быстро. Быстрые козыри ясно. Я ожидаю, что пробежу миллионы раз.

Ответы [ 2 ]

2 голосов
/ 28 февраля 2009

Может быть, я сумасшедший, но мне весело: D Это решение основано на использовании параллелизма данных и имитации векторного процессора без фактического использования встроенных функций SSE или чего-либо подобного.

unsigned short out[64];
const unsigned long long mask      = 0x0249024902490249ul;
const unsigned long long shiftmask = 0x0001000100010001ul;

unsigned long long t = (unsigned short)(in >> 38) | (unsigned long long)(unsigned short)(in >> 39) > 40) > 41) << 48;
t &= mask;
*((unsigned long long*)(out + 38)) = (t & shiftmask) + (t >> 3 & shiftmask) + (t >> 6 & shiftmask) + (t >> 9 & shiftmask);

[... snipsnap ...]

t = (unsigned short)(in >> 2) | (unsigned long long)(unsigned short)(in >> 3) > 4) > 5) << 48;
t &= mask;
*((unsigned long long*)(out + 2)) = (t & shiftmask) + (t >> 3 & shiftmask) + (t >> 6 & shiftmask) + (t >> 9 & shiftmask);

t = (unsigned short)in | (unsigned long long)(unsigned short)(in >> 1) << 16;
t &= mask;
*((unsigned int*)out) = (unsigned int)((t & shiftmask) + (t >> 3 & shiftmask) + (t >> 6 & shiftmask) + (t >> 9 & shiftmask));


Изменяя порядок вычислений, мы можем значительно сократить время выполнения, так как это значительно уменьшает количество загрузок чего-либо в QWORD. Несколько других оптимизаций довольно очевидны и довольно незначительны, но суммируются с другим интересным ускорением.
unsigned short out[64];
const unsigned long long Xmask = 0x249024902490249ull;
const unsigned long long Ymask = 0x7000700070007u;

unsigned long long x = (in >> 14 & 0xFFFFu) | (in >> 20 & 0xFFFFu) > 26 & 0xFFFFu) > 32) << 48;
unsigned long long y;
y = x & Xmask;
y += y >> 6;
y += y >> 3;
y &= Ymask;
out[32] = (unsigned short)(y >> 48);
out[26] = (unsigned short)(y >> 32);
out[20] = (unsigned short)(y >> 16);
out[14] = (unsigned short)(y      );

x >>= 1;
y = x & Xmask;
y += y >> 6;
y += y >> 3;
y &= Ymask;
out[33] = (unsigned short)(y >> 48);
out[27] = (unsigned short)(y >> 32);
out[21] = (unsigned short)(y >> 16);
out[15] = (unsigned short)(y      );

[snisnap]

x >>= 1;
y = x & Xmask;
y += y >> 6;
y += y >> 3;
y &= Ymask;
out[37] = (unsigned short)(y >> 48);
out[31] = (unsigned short)(y >> 32);
out[25] = (unsigned short)(y >> 16);
out[19] = (unsigned short)(y      );

x >>= 1;
x &= 0xFFFF000000000000ul;
x |= (in & 0xFFFFu) | (in >> 5 & 0xFFFFu) > 10 & 0xFFFFu) << 32;
y = x & Xmask;
y += y >> 6;
y += y >> 3;
y &= Ymask;
out[38] = (unsigned short)(y >> 48);
out[10] = (unsigned short)(y >> 32);
out[ 5] = (unsigned short)(y >> 16);
out[ 0] = (unsigned short)(y      );

[snipsnap]

x >>= 1;
y = x & Xmask;
y += y >> 6;
y += y >> 3;
y &= Ymask;
out[ 9] = (unsigned short)(y >> 16);
out[ 4] = (unsigned short)(y      );

Время выполнения для 50 миллионов выполнений в нативном c ++ (все выходные данные проверены на соответствие ^^), скомпилированного на моем компьютере как 64-разрядный двоичный файл:
Решение на основе массива: ~ 5700 мс
Наивное жестко закодированное решение: ~ 4200 мс
Первое решение: ~ 2400 мс
Второе решение: ~ 1600 мс

1 голос
/ 28 февраля 2009

Предположение, что я не хочу сейчас кодировать, - это использовать цикл, массив для хранения частичных результатов и константы для получения битов m за раз.

loop 
   s[3*i] += x & (1 << 0);
   s[3*i+1] += x & (1 << 1);
   s[3*i+2] += x & (1 << 2);
   x >> 3;

Это выберет слишком много битов в каждой сумме. Но вы также можете отслеживать промежуточные результаты и вычитать суммы по мере необходимости, чтобы учесть бит, которого там больше нет.

loop 
   s[3*i] += p[3*i]   = x & (1 << 0);
   s[3*i+1] += p[3*i+1] = x & (1 << 1);
   s[3*i+2] += p[3*i+2] = x & (1 << 2);

   s[3*i] -= p[3*i-10];
   s[3*i+1] -= p[3*i-9];
   s[3*i+2] -= p[3*i-8];
   x >> 3;

с соответствующей проверкой границ, конечно.

Самый быстрый способ - просто жестко закодировать сами суммы.

s[0] = (x & (1<<0)) + (x & (1<<3)) + (x & (1<<6)) + (x & (1<<9));

и т.д.. (Изменения происходят во время компиляции.)

...