提问人:Douglas B 提问时间:6/29/2023 更新时间:6/29/2023 访问量:83
这是将 64b 值的缓冲区重新格式化为 16b 的最快方法吗?
Is this the fastest way to reformat a buffer of 64b values to 16b?
问:
我有一个数据流,它将物理上的 64 位值输出到缓冲区。当缓冲区达到一定水平时,需要将其重新格式化为连续的 16 位值。实际值永远不会超过数据流生成的每个值的 64 位中的 24 位,因此这相当于将 24b 值截断为 16b 并重新排列缓冲区,以便这些值现在是连续的。我相信我已经找到了最快的方法来做到这一点,但是我不确定是否有我可能缺少的优化或 C++ 标准实用程序提供的更快方法。下面是一个 MRE,显示了我的重新格式化功能以及一个测试工具,用于生成我遇到的数据并计时重新格式化。
#include <iostream>
#include <chrono>
#include <unistd.h>
int num_samples = 160000;
void fill_buffer(uint8_t** buffer){
*buffer = (uint8_t*)malloc(num_samples * sizeof(uint64_t));
for (int i = 0; i < num_samples; i += 8){
(*buffer)[i] = rand() % 0xFF;
(*buffer)[i + 1] = rand() % 0xFF;
(*buffer)[i + 2] = rand() % 0xFF;
}
}
void reformat_1(uint8_t* buf){
uint64_t* p_8byte = (uint64_t*)buf;
uint16_t* p_2byte = (uint16_t*)buf;
for (int i = 0; i < num_samples; i++){
p_2byte[i] = p_8byte[i] >> 8;
}
}
int main(int argc, char const* argv[]){
uint8_t* buffer = NULL;
fill_buffer(&buffer);
auto start = std::chrono::high_resolution_clock::now();
reformat_1(buffer);
auto stop = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::microseconds>(stop - start);
std::cout << "Time taken by function one: " << duration.count() << " microseconds" << std::endl;
return 0;
}
我也愿意听到关于我的基准测试设置的反馈,我发现有趣的是,我从文件中读取的实际样本数据得到了~130uS,而对于随机生成的数据,我看到接近1800uS,所以这显然不是一个完全具有代表性的例子。-O3
我要注意的另一件事是,我认为会对我的实际时间(与合成时间相比)起作用,但显然不是:虽然这里是一个神奇的数字,但实际上,它是计算出来的,通常是常数(并非总是如此),但不是编译器会用常量替换的东西展开循环等(我认为)。num_samples
答:
1赞
olegarch
6/29/2023
#1
这种微改进将性能提高了 ~10%:
void reformat_2(uint8_t* buf){
uint32_t* p_8byte = (uint32_t*)buf;
uint16_t* p_2byte = (uint16_t*)buf;
uint16_t* p_2end = p_2byte + num_samples;
while(p_2byte < p_2end){
*p_2byte++ = *p_8byte >> 8;
p_8byte += 2;
}
}
为了查看更清晰的数字,我将缓冲区大小增加了 100 倍,达到 16M 个条目。
评论
0赞
Douglas B
6/29/2023
有趣的是,你能解释一下你是怎么走到这一步的/什么推理会让你期望这更快吗?我将不得不查看程序集输出,但希望获得一些见解
1赞
olegarch
6/30/2023
1. 将索引和循环计数器合并在一起。2. 通过 32 位字而不是 64 位字访问 RAM,这需要更少的总线环路。p_2byte
上一个:R 中的快速矩阵运算
评论
fill_buffer
malloc
malloc
? ?C型演员表? 而不是?双指针?此代码可以使用严肃的“现代 C++”更新。rand
NULL
nullptr