(Связано: Как быстро посчитать биты в отдельные ячейки в серии целых чисел на Sandy Bridge? является более ранним дубликатом этого, с некоторыми другими ответами. Примечание редактора: ответы здесь, вероятно, лучше.
Кроме того, AVX2-версия аналогичной проблемы, с множеством бинов для целого ряда битов, намного шире, чем один uint64_t
: Улучшите алгоритм подсчета количества столбцов)
Я работаю над проектом на C, где мне нужно пройти через десятки миллионов масок (типа ulong (64-бит)) и обновить массив (называемый target
) из 64 коротких целых чисел (uint16) на основе простого правило:
// for any given mask, do the following loop
for (i = 0; i < 64; i++) {
if (mask & (1ull << i)) {
target[i]++
}
}
Проблема в том, что мне нужно выполнить описанные выше циклы на десятках миллионов масок, и мне нужно закончить менее чем за секунду. Интересно, есть ли способ ускорить его, например, использовать какую-то специальную инструкцию по сборке, которая представляет вышеуказанный цикл.
В настоящее время я использую gcc 4.8.4 в Ubuntu 14.04 (i7-2670QM, поддерживающий AVX, а не AVX2) для компиляции и запуска следующего кода, и это заняло около 2 секунд. Хотелось бы, чтобы он работал под 200 мс.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <sys/stat.h>
double getTS() {
struct timeval tv;
gettimeofday(&tv, NULL);
return tv.tv_sec + tv.tv_usec / 1000000.0;
}
unsigned int target[64];
int main(int argc, char *argv[]) {
int i, j;
unsigned long x = 123;
unsigned long m = 1;
char *p = malloc(8 * 10000000);
if (!p) {
printf("failed to allocate\n");
exit(0);
}
memset(p, 0xff, 80000000);
printf("p=%p\n", p);
unsigned long *pLong = (unsigned long*)p;
double start = getTS();
for (j = 0; j < 10000000; j++) {
m = 1;
for (i = 0; i < 64; i++) {
if ((pLong[j] & m) == m) {
target[i]++;
}
m = (m << 1);
}
}
printf("took %f secs\n", getTS() - start);
return 0;
}
Заранее спасибо!