std::向量与法线数组
std::vector vs normal array
我正在创建一个需要超快速的程序。它使用CUDA在GPU上运行一些东西,然后在CPU上进行一些计算。为此,我需要将高度优化的GPU数据结构转换为可以在CPU上轻松使用的结构。我的数据基本上是一张网格图。目前,我使用std::vector作为CPU部分。因为我知道如果我做很多push_back()
s会有相当大的开销,而且我至少知道,因为我知道我的图中有多少个顶点,所以我现在使用以下代码:
new_graph.resize(blockSize * blockSize);
for (unsigned long long y = 0; y < blockSize; y++) {
for (unsigned long long x = 0; x < blockSize; x++) {
int idx = y * blockSize + x;
new_graph[idx] = Vertex(x, y);
}
}
然后我添加边缘。不幸的是,我不知道每个顶点有多少边,但我知道它永远不会大于8。因此,我在用于边的每个std::向量中reserve()
8。
然而,这两者似乎都非常缓慢。如果我对图本身使用普通数组(因此基本上取代了外部std::vector),那么该部分的速度提高是巨大的(大约10倍)。
对于图来说,这是可行的,但对于边来说,这不是真的,因为我在这些边上做了一些后期处理,为此,我真的需要一些类似std::vector的东西,它有点动态(我添加了一些边)。
目前,将数据转换为std::vector的速度比在GPU上运行我的算法(这是一种智能MST算法)慢10倍。这不是我真正想要的,因为现在开销太大了。
有人知道发生了什么吗?或者我该如何解决这个问题?
p.s.我用-O2编译,因为我已经发现这会产生很大的不同。也试过用-O3,没有真正的区别。
顶点定义如下:
struct Pos {
int x, y;
Pos() {
x = 0;
y = 0;
}
Pos(int x, int y) {
this->x = x;
this->y = y;
}
};
struct Vertex {
Pos pos;
bool hidden;
unsigned long long newIdx;
Vertex() {
this->pos = Pos();
this->hidden = false;
this->numEdges = 0;
this->numRemovedEdges = 0;
}
Vertex(Pos &pos) {
this->pos = pos;
this->hidden = false;
this->numEdges = 0;
this->numRemovedEdges = 0;
}
Vertex(int x, int y) {
this->pos = Pos(x, y);
this->hidden = false;
this->numEdges = 0;
this->numRemovedEdges = 0;
}
int numEdges;
int numRemovedEdges;
std::vector<Edge> edges;
std::vector<bool> removed;
std::vector<bool> doNotWrite;
};
也许您正在为vector
为其元素保留空间而进行的动态内存分配付费?
即使以最佳方式reserve
,每个Vertex
也至少有3个内存分配(一个用于edges
,一个用于removed
,一个为doNotWrite
)。相对于您在这里尝试做的高性能工作,动态内存分配可能会非常昂贵。
要么使用保证足够大的普通旧阵列(可能会浪费空间),要么使用专门的内存分配器和vector
,以满足您的特定需求。
此外,您是否按内存顺序访问元素?你的例子似乎表明了这一点,但你在所有情况下都这样做吗?
此外,您甚至需要Vertex.pos
吗?难道不能从Vertex
在网格中的位置推断出吗?
由于动态内存分配的数量、不必要的分配操作以及每个顶点的总体大小,CPU数据结构的效率极低。在考虑优化该结构之前,最好了解CPU数据结构和GPU数据结构之间的数据流,因为这两种格式之间的转换可能需要花费大量时间。这就引出了一个问题,为什么CPU端没有使用GPU结构?
如果你只是从CPU方面来看,并且你想维护一个AoS数据结构,那么1.简化"顶点"数据结构。2.删除所有动态内存分配。每个std::矢量将执行一个dynb3.替换已移除的和doNotWrite到std::bitset<8> 。4.移除numRemoveEdges。这已被删除。count()。5.如果Edge很小,那么您可能会发现声明Edge edges的速度更快[8]。6.如果您决定继续使用vector,那么可以考虑使用池分配器。7.按大小重新排列"顶点"中的数据元素,以减小"顶点"的大小。
所有这些建议很可能不是与GPU共享数据的最佳解决方案。如果你确实使用了池分配器,并且你使用了UVA(CUDALinux),你可以简单地用一个内存副本将数据复制到GPU。
我最近在类似情况下使用了另一种解决方案。在llvm包中有一个SmallVector类。它提供的接口与std::vector非常相似,但它允许保持一定数量的元素在一行中(因此,除非vector增长到初始限制以上,否则不会发生额外的内存分配)。如果SmallVector试图增长到初始大小以上,则分配内存块,并将所有项目移动到那里——所有这些都在一个透明的步骤中完成。
我必须在这个SmallVector:中修复的一些事情
- 可以放置的最小项目数是2,因此当在例如99.99%的情况下使用1个项目时,会产生相当大的开销
- 通常使用swap()来释放内存(SmallVector().swap(vec))不会释放内存,所以我不得不自己实现它
只需查找SmallVector类的源代码的最新版本llvm
难道不能创建一个Vertex对象,将x和y值记忆到其中(这样就不必为每个循环调用构造函数),然后将整个Vertex记忆到std::向量中吗?向量的内存保证像常规数组一样布局,因此您可以绕过所有抽象,直接操作内存。不需要复杂的东西。此外,也许你可以对从GPU返回的数据进行布局,这样你就可以一次记住整个块,为你节省更多。
- 将数组/向量发送到 c++ 脚本的 cython 示例
- C++:在多个线程中访问同一数组/向量的不同单元格是否会产生数据竞赛?
- 将指向多维数组向量中的单个项的指针传递给函数 c++
- 数组向量的文件 I/O
- 在一行中初始化和引入数组向量的正确方法?
- C++ 返回具有最小硬币的数组/向量以获得价值动态规划
- 将整数向量转换为字节数组向量,然后调用每个字节数组
- 我可以在不循环访问数组/向量的情况下检查数组/向量中的单词吗?
- (C )创建可以从功能访问的动态全局数组/向量
- 提升是否具有用于数组向量的智能指针
- 将函数应用于数组向量的元素
- 数组向量的内存布局是什么
- 获取数组/向量末尾地址的未定义行为
- "Empty"数组\向量成员 C++
- 将字符串向量转换为双2-D数组向量
- 排序多维数组/向量
- 如何创建模板化对象的数组/向量
- 如何在数组向量上使用 std::find
- 无法删除C++中的数组/向量
- 具有元素自定义构造函数的对象数组/向量