簡體   English   中英

std :: vector如何比普通數組更快?

[英]How is std::vector faster than a plain array?

在對循環緩沖區進行基准測試時,我偶然發現了這一點。 任何人都可以解釋一下std :: vector如何在這個實例中勝過普通數組?

#include <iostream>
#include <vector>

struct uint_pair {
    unsigned int a, b;
    uint_pair (unsigned int x = 0, unsigned int y = 0) : a(x), b(y) {}
};

struct container {
    unsigned int pos;

#ifdef USE_VECTOR
    std::vector<uint_pair> data;
    container() : pos(0) { data.resize(16); }
#else
    uint_pair data[16];
    container() : pos(0) {}
#endif

    void add(uint_pair val) {
        data[++pos % 16] = val;
    }
};

int main() {
    container c;
    for (unsigned int i = 0; i < 1000000000; i++) c.add(uint_pair{i, i});
    std::cout << c.data[0].a << " " << c.data[0].b << std::endl;
}

這些是我使用GCC的結果(類似於Clang):

g++ -o bench -std=c++0x -Os main.cpp -D'USE_VECTOR'
real    0m8.757s
user    0m8.750s
sys     0m0.002s

g++ -o bench -std=c++0x -Os main.cpp
real    0m9.215s
user    0m9.209s
sys     0m0.002s

這是你如何消除差異。 而不是add ,使用這樣的函數:

void set(unsigned int x, unsigned int y) {
    ++pos;
    data[pos % 16].a = x;
    data[pos % 16].b = y;
}

像這樣叫:

for (unsigned int i = 0; i < 1000000000; i++) c.set(i, i);

這與您的完全相同,但它避免了語義上創建臨時對象。 看起來當你使用向量時,編譯器能夠更好地優化臨時。

$ g++-4.8 -o bench -std=c++11 -Os main.cpp -DUSE_VECTOR
$ time ./bench 
999999999 999999999

real    0m0.635s
user    0m0.630s
sys 0m0.002s

$ g++-4.8 -o bench -std=c++11 -Os main.cpp
$ time ./bench 
999999999 999999999

real    0m0.644s
user    0m0.639s
sys 0m0.002s

在我的機器上, setadd方法都會產生與向量相同的性能。 只有陣列顯示出差異。 為了進一步提高優化,如果使用-O0進行編譯,那么數組方法會稍快一些(但是比使用-Os慢10倍)。

這並不能解釋為什么編譯器會以不同的方式處理這兩者。 畢竟,矢量是由數組支持的。 此外, std::array行為與C風格的數組完全相同。

一個問題是在結構中放置“pos”成員。

對於c-array,請記住它是連續存儲在與“pos”成員相鄰的內存中。 當數據被推入c-數組時,必須發出額外的指令以偏移到通過“pos”成員的結構中。 但是,寫入向量不會產生這樣的限制,因為它的內存位於其他位置。

要擠出更多性能,請確保最熱門的數據位於緩存行的前面。

編輯:

為了使c數組的執行速度與向量一樣快,必須在64位機器上的8字節邊界上分配c數組。 所以類似於:

uint_pair* data;
unsigned int pos;

container() : pos(0) {
    std::size_t bufSize = sizeof(uint_pair) * 17;
    void* p = new char[bufSize];
    p = std::align(8, sizeof(uint_pair), p, bufSize);
    data = reinterpret_cast<uint_pair*>(p);
}

稍加修改的添加功能:

void add(unsigned int x, unsigned int y) {
    auto& ref = data[pos++ % 16];
    ref.a = x;
    ref.b = y;
}

c陣列現在時代:

real    0m0.735s
user    0m0.730s
sys     0m0.002s

和std :: vector:

real    0m0.743s
user    0m0.736s
sys     0m0.004s

標准庫實現者正在為您提供全程服務:)

由於operator =(rvalue reference),C ++ 11編譯器似乎為vector生成了更好的代碼。 首先,在C ++ 03編譯器中,普通數組比矢量快兩倍。 其次,如果使用Adam建議的void set(unsigned int x,unsigned int y),則tehre沒有區別。

向量的匯編代碼

.L49:
leal    (%rdi,%rax), %esi
andl    $15, %esi
leaq    (%rdx,%rsi,8), %rsi
movl    %eax, (%rsi)
movl    %eax, 4(%rsi)
incq    %rax
cmpq    $1000000000, %rax
jne .L49

對於普通數組

.L3:
movl    12(%rsp), %edx
incl    %edx
movl    %edx, 12(%rsp)
andl    $15, %edx
leaq    12(%rsp,%rdx,8), %rdx
movl    %eax, 4(%rdx)
movl    %eax, 8(%rdx)
incl    %eax
cmpl    $1000000000, %eax
jne .L3

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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