[英]std::vector vs normal array
我正在創建一個需要超快的程序。 它使用 CUDA 在 GPU 上運行一些東西,然后在 CPU 上進行一些計算。 為此,我需要將高度優化的 GPU 數據結構轉換為可以在 CPU 上輕松使用的數據結構。 我的數據基本上是一個布置在網格中的圖表。 目前我在 CPU 部分使用 std::vector 。 因為我知道如果我做很多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::vector 中reserve()
8。
但是,這兩者似乎都非常慢。 如果我為圖形本身使用普通數組(因此基本上替換了外部 std::vector),那部分的速度提升是巨大的(大約 10 倍)。
對於圖形,這是可行的,但對於邊緣來說,這不是真的,因為我在這些邊緣上做了一些后處理,為此我真的需要像 std::vector 這樣的東西,它有點動態(我添加了一些邊緣)。
目前將數據轉換為 std::vector's 比在 GPU(這是一種智能 MST 算法)上運行我的算法慢 10 倍。 這不是我真正想要的,因為現在開銷太大了。
有人知道發生了什么事或我該如何解決這個問題嗎?
ps 我用 -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
為其元素保留空間而進行的動態 memory 分配付費?
即使您以最佳方式reserve
,您也會為每個Vertex
分配至少 3 個 memory (一個用於edges
,一個用於removed
和一個用於doNotWrite
)。 動態 memory 分配相對於您在此處嘗試執行的高性能操作而言可能很昂貴。
要么使用保證足夠大(可能會浪費空間)的普通舊 arrays,要么使用專門的 memory 分配器和vector
,根據您的特定需求量身定制。
另外,您是否訪問 memory 順序中的元素? 您的示例似乎表明如此,但您是否在所有情況下都這樣做?
另外,你甚至需要Vertex.pos
嗎? 不能從網格中的Vertex
的 position推斷出來嗎?
我最近在類似情況下使用了另一種解決方案。 在 llvm package 中有 SmallVector class。它提供與 std::vector 非常相似的接口,但它允許保持一些固定數量的元素在線(因此除非向量增長超過初始限制,否則不會發生額外的 memory 分配)。 如果 SmallVector 試圖增長到超過該初始大小,則會分配 memory 塊,並將所有項目移動到那里 - 所有這些都在一個透明的步驟中進行。
我必須在此 SmallVector 中修復的幾件事:
SmallVector的源碼查找最新版的llvm即可 class
由於動態 memory 分配的數量、不必要的分配操作以及每個 Vertex 的總體大小,CPU 數據結構效率極低。 在考慮優化此結構之前,最好了解 CPU 數據結構和 GPU 數據結構之間的數據流,因為這兩種格式之間的轉換可能需要很長時間。 這就引出了一個問題,為什么CPU端沒有使用GPU結構呢?
如果您只是從 CPU 端看這個並且您想要維護 AoS 數據結構,那么 1. 簡化 Vertex 數據結構。 2.刪除所有動態memory分配。 每個 std::vector 將執行一個 dynb 3. 將 removed 和 doNotWrite 替換為 std::bitset<8>。 4. 刪除 numRemoveEdges。 這是 removed.count()。 5. 如果 Edge 很小,那么您可能會發現聲明 Edge edges[8] 更快。 6. 如果您決定繼續使用 vector,請考慮使用池分配器。 7. 將Vertex 中的數據元素按大小重新排序以減小Vertex 的大小。
所有這些建議很可能不是與 GPU 共享數據的最佳解決方案。如果您確實使用池分配器並且使用 UVA (CUDA Linux),您可以簡單地將數據復制到 GPU,並使用單個 memory 副本。
你不能創建一個頂點 object,將 x 和 y 值 memcpy 到其中(這樣你就不必為每個循環調用構造函數),然后將整個頂點 memcpy 到你的 std::vector 中嗎? vector的memory保證像普通數組一樣布局,所以可以繞過所有抽象,直接操作memory。 不需要復雜的東西。 此外,也許您可以對從 GPU 返回的數據進行布局,以便您可以一次 memcpy 整個塊,從而節省更多。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.