【问题标题】:Optimizing bit array accesses优化位数组访问
【发布时间】:2010-11-18 00:38:18
【问题描述】:

我正在使用 Dipperstein 的 bitarray.cpp 类来处理双层(黑白)图像,其中图像数据在本地存储为一个像素一位。

我需要遍历每一位,每张图像大约 4--9 兆像素,超过数百张图像,使用 for 循环,类似于:

for( int i = 0; i < imgLength; i++) {
    if( myBitArray[i] == 1 ) {
         //  ... do stuff ...
    }
}

性能可用,但并不惊人。我通过 gprof 运行程序,发现有大量时间和数百万次调用 std::vector 方法,如迭代器和开始。以下是采样率最高的函数:

Flat profile:

Each sample counts as 0.01 seconds.
  %   cumulative   self              self     total           
 time   seconds   seconds    calls   s/call   s/call  name    
 37.91      0.80     0.80        2     0.40     1.01  findPattern(bit_array_c*, bool*, int, int, int)
 12.32      1.06     0.26 98375762     0.00     0.00  __gnu_cxx::__normal_iterator<unsigned char const*, std::vector<unsigned char, std::allocator<unsigned char> > >::__normal_iterator(unsigned char const* const&)
 11.85      1.31     0.25 48183659     0.00     0.00  __gnu_cxx::__normal_iterator<unsigned char const*, std::vector<unsigned char, std::allocator<unsigned char> > >::operator+(int const&) const
 11.37      1.55     0.24 49187881     0.00     0.00  std::vector<unsigned char, std::allocator<unsigned char> >::begin() const
  9.24      1.75     0.20 48183659     0.00     0.00  bit_array_c::operator[](unsigned int) const
  8.06      1.92     0.17 48183659     0.00     0.00  std::vector<unsigned char, std::allocator<unsigned char> >::operator[](unsigned int) const
  5.21      2.02     0.11 48183659     0.00     0.00  __gnu_cxx::__normal_iterator<unsigned char const*, std::vector<unsigned char, std::allocator<unsigned char> > >::operator*() const
  0.95      2.04     0.02                             bit_array_c::operator()(unsigned int)
  0.47      2.06     0.01  6025316     0.00     0.00  __gnu_cxx::__normal_iterator<unsigned char*, std::vector<unsigned char, std::allocator<unsigned char> > >::__normal_iterator(unsigned char* const&)
  0.47      2.06     0.01  3012657     0.00     0.00  __gnu_cxx::__normal_iterator<unsigned char*, std::vector<unsigned char, std::allocator<unsigned char> > >::operator*() const
  0.47      2.08     0.01  1004222     0.00     0.00  std::vector<unsigned char, std::allocator<unsigned char> >::end() const
... remainder omitted ...

我对 C++ 的 STL 不是很熟悉,但是谁能解释一下为什么 std::vector::begin() 被调用了几百万次?当然,我是否可以做些什么来加快速度?

编辑:我只是放弃并优化了搜索功能(循环)。

【问题讨论】:

  • 如果您发布了一些代码,可能更容易为您提供加速代码的指示;最好是循环的内容。
  • 您能否提供更多关于myBitArrayimgLength 的信息?比如myBitArray的类型是什么?是imgLength == myBitArray.size()?该配置文件暗示myBitArray 可能是std::vector&lt;unsigned char&gt;...
  • 如果您共享代码,则更容易帮助您编写代码。如果可能,请尝试在循环中处理多个位。既然你知道它是一个二进制图像,你可以通过一次读取 32 位而不是只读取一个来处理每个循环至少 32 位。这将减少您必须增加指针以访问下一个字节的次数......但这只是推测。请分享你的代码:)

标签: c++ optimization stl vector bitarray


【解决方案1】:

您在配置文件输出中看到大量内联函数这一事实意味着它们没有被内联——也就是说,您没有在打开优化的情况下进行编译。因此,优化代码最简单的方法是使用 -O2 或 -O3。

分析未优化代码很少值得,因为优化和未优化代码的执行配置文件可能完全不同。33

【讨论】:

    【解决方案2】:

    bitarray.cpp 代码的快速峰值显示:

    bool bit_array_c::operator[](const unsigned int bit) const
    {
        return((m_Array[BIT_CHAR(bit)] & BIT_IN_CHAR(bit)) != 0);
    }
    

    m_Array 是 std::vector 类型

    STL 向量上的 [] 运算符具有恒定的复杂性,但它可能实现为对 vector::begin 的调用以获取数组的基地址,然后计算偏移量以获取所需的值。由于 bitarray.cpp 在 EVERY BIT ACCESS 上调用 [] 运算符,因此您会收到很多调用。

    根据您的用例,我将创建一个包含在 bitarray.cpp 中的功能的自定义实现,并针对您的顺序、逐位访问模式对其进行调整。

    • 不要使用无符号字符,使用 32 或 64 位值来减少所需的内存访问次数。
    • 我会使用普通数组,而不是向量来避免查找开销
    • 创建一个不执行所有查找的顺序访问函数 nextbit()。存储一个指向当前“值”的指针,您需要在 32/64 位边界上递增它,边界之间的所有访问都是简单的掩码/移位操作,应该非常快。

    【讨论】:

      【解决方案3】:

      如果没有看到您的代码,很难就如何加快您的工作速度做出具体的 cmet。但是,vector::begin() 用于将迭代器返回到向量中的第一个元素 - 这是遍历向量时的标准例程。

      我实际上建议使用更现代的分析器,例如OProfile,这将为您提供有关程序花费时间的更细粒度的信息 - 直至实际的 C++ 行,甚至是单独的 asm 指令,具体取决于您的运行方式。

      顺便说一句 - 你为什么选择使用 bitarray.cpp 而不是原版的 std::vector&lt;bool&gt;?我自己没有使用过它,但是快速浏览一下上面的链接表明bitarray.cpp 支持比std::vector&lt;bool&gt; 更多的功能,如果你不使用它,与 STL 向量类相比可能会增加开销。 .

      【讨论】:

      • 小心向量,这个向量并不是真正的向量:informit.com/guides/content.aspx?g=cplusplus&seqNum=98
      • 来自 17 of 26 的文章:“vector&lt;bool&gt; 集中体现了优化的第一条规则:'过早的优化是邪恶的。'这种优化带来了沉重的代价:即古怪的界面、速度开销以及与标准库的不兼容。这种优化不是可选的;它是强加给程序员的。速度是这里的问题,看起来这不是一个成功的选择。
      【解决方案4】:

      您可以通过使用指针/迭代器来提高性能(我不确定 bitarray.cpp 到底为您做了什么),如下所示:

      for (bool *ptr = myBitArray, int i = 0; i != imgLength; ++i, ++ptr)
      {
         if (*myBitArray == 1)
         {
             //handle
         }
      }
      

      我在这里只使用 int i 是因为我不确定你的位数组是否会被空终止,在这种情况下你的条件可能只是

      *myBitArray != '\0';
      

      或者你可以破解一个更好的结束条件。最好使用 std::iterator,但我怀疑你的 bitarray 会支持它。

      编辑:

      通常这将是一个微优化,但如果你循环了足够多的东西,它可能会稍微提高性能。

      【讨论】:

      • 我认为 myBitArray 可能是 std::vector&lt;char&gt; 在这种情况下类似的建议是使用迭代器,而不是指针。
      • 我的帖子指出迭代器会是更好的选择。
      【解决方案5】:

      如果性能足够重要以至于您不得不担心访问单个位,那么您可能应该并行化您的代码。由于您将其描述为图像处理,因此位 i 的状态很可能不会影响您处理位 i+1 到 i+6 的方式,因此您可能可以重写代码以一次对字节和字进行操作。仅仅能够将计数器增加 8 到 64 倍的频率就可以提供可衡量的性能提升,并且还可以使编译器更容易优化您的代码。

      【讨论】:

        猜你喜欢
        • 2010-11-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-01-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多