簡體   English   中英

std::vector 與普通數組

[英]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 中修復的幾件事:

  1. 可以放置到位的最小項目數是 2,因此當在 99.99% 的情況下使用 1 個項目時,會有相當大的開銷
  2. 通常使用 swap() 來釋放 memory ( SmallVector().swap(vec) ) 並不能釋放 memory,所以我不得不自己實現

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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM