簡體   English   中英

boost :: dynamic_bitset比std :: bitset慢,除非重置std :: bitset

[英]boost::dynamic_bitset slower than std::bitset unless std::bitset is reset

我最近遇到了位集模板,並非常想在我的當前項目中使用它們。 繼續閱讀,我看到std::bitset模板必須具有在編譯時確定的大小。 許多人建議使用boost::dynamic_bitset減輕此要求。

為了比較兩者,我決定對setflipcount方法進行速度比較。

結果很奇怪……我想知道是否有人可以為我闡明一下。

代碼在帖子的結尾,但是我將在這里解釋我在做什么。 我有一個std::bitset對象(稱為bs )和一個boost::dynamic_bitset對象(稱為dynbs )。 每個都有n=1000000位。 對於上面給定的方法,依次對n位中的每個位調用該方法,並重復此R=10000次。

使用std::chrono庫,以下是每個std::chrono的時間(以納秒為單位):

set
        bitset:              267 nsecs
    dyn bitset:      18603174546 nsecs

flip
        bitset:               73 nsecs
    dyn bitset:      18842352867 nsecs

count
        bitset:               77 nsecs
    dyn bitset:               51 nsecs

對於setflipboost::dynamic_bitset似乎要慢得多。

更有趣的是,如果在運行這些測試之前在兩個對象上調用了reset方法,那么時間是可比的。 他們來了:

set
        bitset:      19397779399 nsecs
    dyn bitset:      18472863864 nsecs

flip
        bitset:      18599248629 nsecs
    dyn bitset:      18376267939 nsecs

count
        bitset:               68 nsecs
    dyn bitset:               61 nsecs

現在,兩個容器都聲稱將所有位初始化為0 ,因此調用reset不應更改任何位。 reset前后none轉儲none的輸出會確認這一點。

所以畢竟,我有兩個問題:

1)為什么在調用setflipboost::dynamic_bitsetstd::bitset慢得多?

2)為什么調用resetstd::bitset的速度有巨大的負面影響?

這是我的代碼:

#include <iostream>
#include <iomanip>
#include <bitset>
#include <boost/dynamic_bitset.hpp>
#include <vector>
#include <chrono>
#include <ctime>

using namespace std;
using namespace chrono;
using namespace boost;

int main(){
  const unsigned int n=1000000;
  bitset< n > bs;
  dynamic_bitset< > dynbs(n);
  // bs.reset();
  // dynbs.reset();

  unsigned int i,r,R=10000;
  high_resolution_clock::time_point tick,tock;


  ////////////////////////////////////////////////////////////
  // Method: set

  std::cout << "set" << std::endl;

  tick=high_resolution_clock::now();

  for(r=0; r<R; r++)
    for(i=0; i<n; i++)
      bs.set(i);

  tock=high_resolution_clock::now();
  cout << setw(16) << "bitset: " 
       << setw(16) << duration_cast<nanoseconds>(tock-tick).count() << " nsecs" 
       << std::endl;

  tick=high_resolution_clock::now();

  for(r=0; r<R; r++)
    for(i=0; i<n; i++)
      dynbs.set(i);

  tock=high_resolution_clock::now();
  cout << setw(16) << "dyn bitset: " 
       << setw(16) << duration_cast<nanoseconds>(tock-tick).count() << " nsecs"
       << std::endl << std::endl;


  ////////////////////////////////////////////////////////////
  // Method: flip

  std::cout << "flip" << std::endl;

  tick=high_resolution_clock::now();

  for(r=0; r<R; r++)
    for(i=0; i<n; i++)
      bs.flip(i);

  tock=high_resolution_clock::now();
  cout << setw(16) << "bitset: " 
       << setw(16) << duration_cast<nanoseconds>(tock-tick).count() << " nsecs"
       << std::endl;

  tick=high_resolution_clock::now();

  for(r=0; r<R; r++)
    for(i=0; i<n; i++)
      dynbs.flip(i);

  tock=high_resolution_clock::now();
  cout << setw(16) << "dyn bitset: " 
       << setw(16) << duration_cast<nanoseconds>(tock-tick).count() << " nsecs"
       << std::endl << std::endl;


  ////////////////////////////////////////////////////////////
  // Method: count

  std::cout << "count" << std::endl;

  tick=high_resolution_clock::now();

  for(r=0; r<R; r++)
    for(i=0; i<n; i++)
      bs.count();

  tock=high_resolution_clock::now();
  cout << setw(16) << "bitset: " 
       << setw(16) << duration_cast<nanoseconds>(tock-tick).count() << " nsecs"
       << std::endl;

  tick=high_resolution_clock::now();

  for(r=0; r<R; r++)
    for(i=0; i<n; i++)
      dynbs.count();

  tock=high_resolution_clock::now();
  cout << setw(16) << "dyn bitset: " 
       << setw(16) << duration_cast<nanoseconds>(tock-tick).count() << " nsecs"
       << std::endl;


  return 0;
}

我用

g++ -O3 -std=c++11 bitset.cpp -o bitset

其中bitset.cpp是上面插入的代碼。

1)為什么在調用set和flip時boost::dynamic_bitsetstd::bitset慢得多?

由於std::bitset不使用動態分配,並且您的bitset是局部變量,因此編譯器可以輕松確定所有setflip以及count都沒有外部可見的效果。 結果,它優化了它們 ,並且您的代碼基本上最終成為一堆計時和打印調用。

請注意,在上述程序集中,它甚至沒有為該位集分配堆棧空間。 整個事情基本上消失了無影無蹤。

boost::dynamic_bitsetnew動態分配其緩沖區,最終調用::operator new() ,后者可以是在不同轉換單元中定義的任意重載版本。 因此,對於編譯器來說,很難證明返回的緩沖區的更改在外部不可見。

std::bitsetboost::dynamic_bitset count循環都已優化,因為編譯器可以輕松地看到count()不會更改位集中的任何內容,並且您不使用返回值。

2)為什么調用reset對std :: bitset的速度有巨大的負面影響?

我檢查了GCC中reset的源代碼,它調用了編譯器固有的__builtin_memset ,並向其傳遞了指向緩沖區的指針。 當您將指向堆棧變量的指針傳遞給外部函數時,編譯器在可刪除內容方面受到了更大的限制,因為該變量的更改現在可以從外部觀察到(例如,調用的函數可能已經存儲了一個副本)。某個地方的指針,以后便可以窺視它)。

好吧,看來TC有了解釋。

您對位集的所有設置和翻轉(和計數)都已完全優化。

匯編代碼提供了一個鏈接來顯示此內容: 匯編代碼

通過從三種不同方法返回值並將它們添加到其他變量中,就成功了,現在看來似乎是一場公平的戰斗(如TC所建議)。

暫無
暫無

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

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