更快地访问 C++ 数组中的随机元素
faster access to random elements in c++ array
如果事先知道访问模式,那么访问数组中随机(非顺序(元素的最快方法是什么?每一步的访问都是随机的,以满足不同的需求,因此重新排列元素是昂贵的选择。下面的代码表示整个应用程序的重要示例。
#include <iostream>
#include "chrono"
#include <cstdlib>
#define NN 1000000
struct Astr{
double x[3], v[3];
int i, j, k;
long rank, p, q, r;
};
int main ()
{
struct Astr *key;
key = new Astr[NN];
int ii, *sequence;
sequence = new int[NN]; // access pattern is stored here
float frac ;
// create array of structs
// create array for random numbers between 0 to NN to access 'key'
for(int i=0; i < NN; i++){
key[i].x[1] = static_cast<double>(i);
key[i].p = static_cast<long>(i);
frac = static_cast<float>(rand()) / static_cast<float>(RAND_MAX);
sequence[i] = static_cast<int>(frac * static_cast<float>(NN));
}
// part to check and improve
// =========================================Random=======================================================
std::chrono::high_resolution_clock::time_point TstartMain = std::chrono::high_resolution_clock::now();
double tmp;
long rnk;
for(int j=0; j < 1000; j++)
for(int i=0; i < NN; i++){
ii = sequence[i];
tmp = key[ii].x[1];
rnk = key[ii].p;
key[ii].x[1] = tmp * 1.01;
key[ii].p = rnk * 1.01;
}
std::chrono::high_resolution_clock::time_point TendMain = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::microseconds>( TendMain - TstartMain );
double time_uni = static_cast<double>(duration.count()) / 1000000;
std::cout << "n Random array access " << time_uni << "s n" ;
// ==========================================Sequential======================================================
TstartMain = std::chrono::high_resolution_clock::now();
for(int j=0; j < 1000; j++)
for(int i=0; i < NN; i++){
tmp = key[i].x[1];
rnk = key[i].p;
key[i].x[1] = tmp * 1.01;
key[i].p = rnk * 1.01;
}
TendMain = std::chrono::high_resolution_clock::now();
duration = std::chrono::duration_cast<std::chrono::microseconds>( TendMain - TstartMain );
time_uni = static_cast<double>(duration.count()) / 1000000;
std::cout << " Sequential array access " << time_uni << "s n" ;
// ================================================================================================
delete [] key;
delete [] sequence;
}
正如预期的那样,顺序访问更快;答案在我的机器上 -
Random array access 21.3763s
Sequential array access 8.7755s
主要问题是是否可以更快地进行随机访问。 代码改进可能是在容器本身方面(例如列表/向量而不是数组(。软件预取可以实施吗?
理论上,可以帮助引导预取器加速随机访问(好吧,在那些支持它的CPU上 - 例如_mm_prefetch用于Intel/AMD(。然而,在实践中,这通常完全是浪费时间,而且通常会减慢你的代码速度。
一般理论是,在使用值之前,将指向_mm_prefetch的指针传递到一两次循环迭代。但是,这存在问题:
- 您最终可能会调整 CPU 的代码。在其他平台上运行相同的代码时,您可能会发现不同的 CPU 缓存布局/大小意味着您的预取优化现在实际上会降低性能。
- 额外的预取指令最终会占用更多的指令缓存,很可能还会占用您的 uop 缓存。您可能会发现仅此一项就会减慢代码速度。
- 这假设 CPU 实际上关注_mm_prefetch指令。这只是一个提示,所以没有保证它会受到 CPU 的尊重。
如果您想加快随机内存访问的速度,有比预取恕我直言更好的方法。
- 减小数据的大小(即使用 shorts/float16s 代替 int/float,消除结构中的任何错误填充等(。通过减小结构的大小,您可以读取的内存更少,因此速度会更快!(简单的压缩方案也不是一个坏主意! 对数据进行
- 排序,以便按顺序处理数据,而不是进行随机访问。
除了这两个选项之外,最好的选择是不要管预取,编译器会通过随机访问来做这件事(唯一的例外:您正在为 ~2001 奔腾 4 优化代码,其中基本上需要预取(。
为了举例说明@robthebloke所说的内容,以下代码使我的机器改进了~15%:
#include <immintrin.h>
void do_it(struct Astr *key, const int *sequence) {
for(int i = 0; i < NN-8; ++i) {
_mm_prefetch(key + sequence[i+8], _MM_HINT_NTA);
struct Astr *ki = key+sequence[i];
ki->x[1] *= 1.01;
ki->p *= 1.01;
}
for(int i = NN-8; i < NN; ++i) {
struct Astr *ki = key+sequence[i];
ki->x[1] *= 1.01;
ki->p *= 1.01;
}
}
相关文章:
- 从C++数组中选择一个随机元素
- 用随机元素填充矢量
- 将集合的随机元素添加到列表中,然后将其从原始集合中移除
- 从长(且合理)稀疏向量中选择随机元素的最有效方法是什么?
- 更快地访问 C++ 数组中的随机元素
- 从容器中获取随机元素,该容器在恒定时间内没有严格的元素顺序
- 选择C++地图中随机元素的百分比
- C++将字符从静态数组复制到动态数组会添加一堆随机元素
- 如何有效地从std::集中选择随机元素
- 如何在数组中拾取一些随机元素
- 为多映射中的特定键选择随机元素
- 如何从向量中删除随机元素而不重复它们并保持元素顺序?C++
- 如何在 c++ 中从枚举返回随机元素
- 如何从填充.txt文件名的向量中选取随机元素C++
- 在unordered_map中选择随机元素
- 算法-统一随机元素链表
- 从具有印地语字符的数组中生成随机元素
- 获取随机元素并将其移除
- 这是从容器中获取随机元素的OK范围的方法吗
- 如何在小于O(n)的时间内选择std::集合中的一个随机元素