簡體   English   中英

const_iterators更快嗎?

[英]Are const_iterators faster?

我們的編碼指南更喜歡const_iterator ,因為與普通的iterator相比,它們要快一些。 當您使用const_iterator時,似乎編譯器會優化代碼。

這是真的嗎? 如果是,內部真正發生的事情會使const_iterator更快?

編輯:我寫了一個小測試來檢查const_iterator vs iterator ,發現不同的結果:

為了迭代10,000個對象, const_terator花費了幾毫秒(約16 ms)的時間。 並非總是如此 在迭代中,兩者相等。

如果沒有其他要求,那么const_iterator 會讀起來更好,因為它告訴任何閱讀代碼的人“我只是在對該容器進行迭代,而不會弄亂包含的對象”。

這是一個巨大的勝利,不要介意任何性能差異。

我們使用的准則是:

總是比非const更喜歡const

如果您傾向於使用const對象,則習慣於只對獲得的對象使用常量操作,這與盡可能多地使用const_iterator一樣。

常數具有病毒性。 一旦使用它,它就會傳播到所有代碼。 您的非變異方法變為常量,並且只需要對屬性使用常量操作,並傳遞常量引用,這本身僅強制執行常量操作...

對我來說,使用常量迭代器而不是非常量迭代器(如果有的話)的性能優勢遠沒有代碼本身的改進重要。 旨在(設計)為不變的操作恆定的。

它們適用於非平凡的容器/迭代器。 養成正確的習慣,當事情變得重要時,您就不會失去表現。

此外,無論出於何種原因,都有幾個原因更喜歡const_iterator:

  1. 使用const表示代碼意圖(即只讀,這些對象無突變)。
  2. 使用const(_iterator)可以防止意外修改數據。 (往上看)
  3. 一些庫使用缺乏常量的begin()將數據標記為臟數據(即OpenSG),並在同步時將其發送到其他線程/網絡,因此這對性能有實際影響。
  4. 另外,允許您訪問非const成員函數可能會產生您不希望看到的副作用(與上述方式大致相同),例如,從共享數據中刪除寫時復制容器。 Qt就是這樣做的。

作為上述最后一點的示例,這是Qt中qmap.h的摘錄:

inline iterator begin() { detach(); return iterator(e->forward[0]); }
inline const_iterator begin() const { return const_iterator(e->forward[0]); }

即使iterator和const_iterator實際上是等效的( const除外),如果有兩個或多個使用它的對象, detach()也會創建數據的新副本。 如果您只是要讀取數據(使用const_iterator指示),則這完全沒有用。

當然,還有另一個方向的數據點:

  1. 對於STL容器和許多簡單復制語義的容器,性能無關緊要。 該代碼等效的。 但是,能夠編寫清晰的代碼並避免錯誤的能力勝出。
  2. const具有病毒性,因此,如果您使用的是const實現不佳(或根本沒有實現)的舊代碼庫,則可能必須使用非const迭代器。
  3. 顯然,某些C ++ 0x之前的STL有一個錯誤,即無法使用const_iterators從容器中擦除()元素。

我不明白為什么會這樣-constness是編譯時檢查。 但是顯而易見的答案是編寫測試。

編輯:這是我的測試-它在我的機器上給出了相同的時間:

#include <vector>
#include <iostream>
#include <ctime>
using namespace std;;


int main() {
    vector <int> v;
    const int BIG = 10000000;
    for ( int i = 0; i < BIG; i++ ) {
        v.push_back( i );
    }
    cout << "begin\n";
    int n = 0;
    time_t now = time(0);
    for ( int a = 0; a < 10; a++ ) {
        for( vector <int>::iterator it = v.begin(); it != v.end(); ++it ) {
            n += *it;
        }
    }
    cout << time(0) - now << "\n";
    now = time(0);
    for ( int a = 0; a < 10; a++ ) {
        for( vector <int>::const_iterator cit = v.begin(); cit != v.end(); ++cit ) {
            n += *cit;
        }
    }
    cout << time(0) - now << "\n";;

    return n != 0;

}

這取決於您使用的容器和實現。

是的, const_iterator 可能更好。

對於某些容器,構造迭代器和可變迭代器的實現可能有所不同 我可以想到的第一個示例是SGI的STL繩索容器 可變迭代器具有指向父繩索的附加指針,以支持更新。 這意味着浪費了更多資源來浪費參考引用更新+指向父繩索的指針的內存。 請參閱此處實施說明

但是,正如其他人所說,編譯器不能const使用const進行優化。 const只是授予對引用對象的只讀訪問權限,而不是說它是不可變的。 對於像std::vector這樣的容器,其迭代器通常實現為簡單的指針,不會有任何區別。

我們的編碼指南說更喜歡const_iterator

這里瀏覽Scott Meyers的這篇文章 他解釋了為什么人們應該比const_iterator更喜歡迭代器。

它們應相同,因為constness是編譯時檢查。

為了證明自己沒有古怪之處,我采用了anon的代碼,將其修改為使用clock_gettime ,添加了一個外循環以避免緩存偏差,並運行了很多次。 令人驚訝的是,結果上下波動了20%(沒有可用的空閑框),但是iteratorconst_iterator 最短時間實際上是相同的

然后,我得到了編譯器(GCC 4.5.2 -O3)來生成程序集輸出,並直觀地比較了兩個循環: 相同 (除了幾個寄存器加載的順序顛倒了)

iterator循環

    call    clock_gettime
    movl    56(%esp), %esi
    movl    $10, %ecx
    movl    60(%esp), %edx
    .p2align 4,,7
    .p2align 3
.L35:
    cmpl    %esi, %edx
    je  .L33
    movl    %esi, %eax    .p2align 4,,7
    .p2align 3
.L34:
    addl    (%eax), %ebx
    addl    $4, %eax
    cmpl    %eax, %edx
    jne .L34
.L33:
    subl    $1, %ecx
    jne .L35
    leal    68(%esp), %edx
    movl    %edx, 4(%esp)
    leal    56(%esp), %esi
    movl    $1, (%esp)

const_iterator循環:

    movl    60(%esp), %edx
    movl    $10, %ecx
    movl    56(%esp), %esi
    .p2align 4,,7
    .p2align 3
.L38:
    cmpl    %esi, %edx
    je  .L36
    movl    %esi, %eax
    .p2align 4,,7
    .p2align 3
.L37:
    addl    (%eax), %ebx
    addl    $4, %eax
    cmpl    %eax, %edx
    jne .L37
.L36:
    subl    $1, %ecx
    jne .L38
    leal    68(%esp), %edx
    movl    %edx, 4(%esp)
    leal    56(%esp), %esi
    movl    $1, (%esp)

當您對其中任何一個進行基准測試時,請確保使用適當的優化級別-使用“ -O0”與“ -O”之類的代碼會獲得截然不同的時序。

container<T>::const_iterator::operator*返回const T&而不是T& ,因此編譯器可以對const對象進行通常的優化。

像訪問限制(公共,受保護,私有)一樣,“常量性”對程序員的好處大於對優化的幫助。
出於多種原因(例如const_cast,可變數據成員,指針/引用別名),編譯器實際上無法針對人們所想到的盡可能多的情況進行優化。 不過,這里最相關的原因是,僅僅因為const_iterator不允許修改其引用的數據,並不意味着該數據不能通過其他方式更改。 而且,如果編譯器無法確定數據是只讀的,那么它的優化程度就不會超過非常量迭代器的情況。
有關更多信息和示例,請參見: http://www.gotw.ca/gotw/081.htm

根據我的經驗,使用const迭代器時,編譯器不會進行任何可衡量的優化。 盡管語句“可能”是正確的,並且在標准中未將引用定義為指針。

但是,您可以對同一對象有兩個引用,因此一個可以是const,一個可以是非const。 然后,我猜想該線程中關於限制指針的答案適用:編譯器無法知道對象是由另一個線程(例如,還是由某些中斷處理代碼)更改的。

暫無
暫無

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

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