gpt4 book ai didi

c++ - 转换位数组以更快地设置

转载 作者:搜寻专家 更新时间:2023-10-30 23:53:47 25 4
gpt4 key购买 nike

输入是存储在连续内存中的位数组,每 1 位内存对应 1 位位数组。

输出是位数组的设置位的索引数组。

例子:

bitarray: 0000 1111 0101 1010
setA: {4,5,6,7,9,11,12,14}
setB: {2,4,5,7,9,10,11,12}

获得 A 组或 B 组都可以。该集合存储为 uint32_t 数组,因此集合中的每个元素都是数组中的无符号 32 位整数。

如何在单个 cpu 内核上将速度提高约 5 倍?

当前代码:

#include <iostream>
#include <vector>
#include <time.h>

using namespace std;

template <typename T>
uint32_t bitarray2set(T& v, uint32_t * ptr_set){
uint32_t i;
uint32_t base = 0;
uint32_t * ptr_set_new = ptr_set;
uint32_t size = v.capacity();
for(i = 0; i < size; i++){
find_set_bit(v[i], ptr_set_new, base);
base += 8*sizeof(uint32_t);
}
return (ptr_set_new - ptr_set);
}

inline void find_set_bit(uint32_t n, uint32_t*& ptr_set, uint32_t base){
// Find the set bits in a uint32_t
int k = base;
while(n){
if (n & 1){
*(ptr_set) = k;
ptr_set++;
}
n = n >> 1;
k++;
}
}

template <typename T>
void rand_vector(T& v){
srand(time(NULL));
int i;
int size = v.capacity();
for (i=0;i<size;i++){
v[i] = rand();
}
}

template <typename T>
void print_vector(T& v, int size_in = 0){
int i;

int size;
if (size_in == 0){
size = v.capacity();
} else {
size = size_in;
}
for (i=0;i<size;i++){
cout << v[i] << ' ';
}
cout << endl;
}

int main(void){
const int test_size = 6000;
vector<uint32_t> vec(test_size);
vector<uint32_t> set(test_size*sizeof(uint32_t)*8);
rand_vector(vec);
//for (int i; i < 64; i++) vec[i] = -1;
//cout << "input" << endl;
print_vector(vec);
//cout << "calculate result" << endl;

int i;
int rep = 10000;
uint32_t res_size;

struct timespec tp_start, tp_end;
clock_gettime(CLOCK_MONOTONIC, &tp_start);
for (i=0;i<rep;i++){
res_size = bitarray2set(vec, set.data());
}
clock_gettime(CLOCK_MONOTONIC, &tp_end);
double timing;
const double nano = 0.000000001;

timing = ((double)(tp_end.tv_sec - tp_start.tv_sec )
+ (tp_end.tv_nsec - tp_start.tv_nsec) * nano) /(rep);

cout << "timing per cycle: " << timing << endl;
cout << "print result" << endl;
//print_vector(set, res_size);
}

结果(用icc -O3 code.cpp -lrt编译)

...
timing per cycle: 0.000739613 (7.4E-4).
print result

0.0008 秒将 768000 位转换为设置。但是每个周期至少有10,000个768,000位的数组。即每个周期 8 秒。那很慢。

cpu有popcnt指令和sse4.2指令集。

谢谢。

更新


template <typename T>
uint32_t bitarray2set(T& v, uint32_t * ptr_set){
uint32_t i;
uint32_t base = 0;
uint32_t * ptr_set_new = ptr_set;
uint32_t size = v.capacity();
uint32_t * ptr_v;
uint32_t * ptr_v_end = &(v[size]);
for(ptr_v = v.data(); ptr_v < ptr_v_end; ++ptr_v){
while(*ptr_v) {
*ptr_set_new++ = base + __builtin_ctz(*ptr_v);
(*ptr_v) &= (*ptr_v) - 1; // zeros the lowest 1-bit in n
}
base += 8*sizeof(uint32_t);
}
return (ptr_set_new - ptr_set);
}

这个更新版本使用了 rhashimoto 提供的内部循环。我不知道内联是否真的使函数变慢(我从没想过会发生这种情况!)。新时序为 1.14E-5(由 icc -O3 code.cpp -lrt 编译,并以随机 vector 为基准)。

警告:

我刚刚发现保留而不是调整 std::vector 的大小,然后通过原始指向直接写入 vector 的数据是一个坏主意。先调整大小然后使用原始指针是可以的。在 Resizing a C++ std::vector<char> without initializing data 查看 Robᵩ 的回答我将只使用 resize 而不是 reserve 并且不再担心通过调用 vector 的每个元素的构造函数来调整大小浪费的时间......至少 vector 实际上使用连续的内存,就像一个普通数组(Are std::vector elements guaranteed to be contiguous?)

最佳答案

我注意到当您可能打算使用 .size() 时,您使用了 .capacity()。这可能会让你做额外的不必要的工作,并给你错误的答案。

find_set_bit() 中的循环遍历单词中的所有 32 位。您可以改为仅迭代每个设置位并使用 BSF 指令来确定最低位的索引。 GCC 有一个内在的功能 __builtin_ctz()生成 BSF 或等效的 - 我认为英特尔编译器也支持它(如果不支持,你可以内联汇编)。修改后的函数如下所示:

inline void find_set_bit(uint32_t n, uint32_t*& ptr_set, uint32_t base){
// Find the set bits in a uint32_t
while(n) {
*ptr_set++ = base + __builtin_ctz(n);
n &= n - 1; // zeros the lowest 1-bit in n
}
}

在我的 Linux 机器上,使用 g++ -O3 进行编译,替换该函数会将报告的时间从 0.000531434 降低到 0.000101352。

this question 的答案中有很多方法可以找到位索引.不过,我确实认为 __builtin_ctz() 将是您的最佳选择。我不相信有一个合理的 SIMD 方法来解决您的问题,因为每个输入词都会产生可变数量的输出。

关于c++ - 转换位数组以更快地设置,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38339395/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com