【问题标题】:What is the most efficient method to find x contiguous values of y in an array?在数组中找到 y 的 x 个连续值的最有效方法是什么?
【发布时间】:2012-10-09 11:40:27
【问题描述】:

通过 callgrind 运行我的应用程序发现,这条线比其他所有内容都相形见绌约 10,000 倍。我可能会围绕它重新设计,但这让我想知道;有没有更好的方法?

这是我目前正在做的事情:

int i = 1;
while
(
    (
        (*(buffer++) == 0xffffffff && ++i) || 
        (i = 1)
    )
    &&
    i < desiredLength + 1
    &&
    buffer < bufferEnd
);

它在一个 32 位无符号整数数组中寻找期望长度 0xffffffff 值的第一块的偏移量。

它比我想出的任何涉及内部循环的实现都要快得多。但是还是太慢了。

【问题讨论】:

  • 可以优化的一点是该行的可读性...
  • 您是否尝试过使用std::search_n - std::search_n(buffer, bufferEnd, desiredLength, 0xffffffff)?我想它可以(潜在地)利用 SIMD。
  • 假设连续 0xffffffff 的最小数量至少为 2,使用 64 位整数并在循环后进行一些小修正,您可以将速度提高一倍。
  • 你不能跳过desiredLength而不是1,然后从那里搜索吗? IE。如果 desiredLength 为 10,并且 buffer[0] 和 buffer[10] 都不匹配,则不需要查看 buffer[3]。
  • 为了更进一步地使用 64 位整数的想法,您还可以使用 128 位 SSE。

标签: c++ c performance algorithm profiling


【解决方案1】:

我也会接受search_n 的建议,因为我很确定它可以正确执行此操作。这实际上很简单,基本上可以通过所需长度的一个因子来加速。除非目标值在数组中非常密集。

这里的想法是:如果您有一个从位置I 开始的值的连续实例K,那么位置I + K - 1 一定包含该值。所以你先检查一下;如果不是,那么可能包含K个连续值的最早位置是I + K,所以你可以在那里重新开始算法。

另一方面,如果您在 I + K - 1 找到值,则向后扫描直到到达 I(在这种情况下您成功),或者到达某个位置 J - 1包含目标值。在后一种情况下,您知道有从JI + K - 1 的目标值,因此您现在检查J + K - 1。如果可行,您只需向后扫描到I + K。如果不起作用,请在 J + K 处重新启动算法。

大多数时候,您只会查看向量中的每个K'th 位置。对于大的K,这是一个很大的胜利。

【讨论】:

    【解决方案2】:

    您标记了 c++,所以我假设您有可用的 STL 算法:

    std::search_n(buffer, bufferEnd, desiredLength, 0xffffffff);
    

    【讨论】:

      【解决方案3】:

      尝试使用 C 标准库中的memcmp。现代编译器应该对 memxxx 函数进行非常优化的实现,从而最大限度地利用现代 CPU。

      【讨论】:

      • 我不认为 memcmp 在这里适用。
      • @Alastair 使用预定义的长度 - 为什么不呢,它可以比 std::search_n 更快。取决于实施。
      • 但它的作用与 search_n 不同!我认为您需要进一步解释。
      • 当然,它应该与 e.g. 一起使用。 memchr,类似 BigBoss 的建议。
      【解决方案4】:

      只是一个想法,但您一次迭代一个 int 数组,对吗?想一想,如果 *(buffer) != 0xffffffffbuffer[desiredLength-1] != 0xffffffff 那么您可以确定在两者之间进行检查是没有意义的,因此您可以将 buffer 移动 desiredLength 而不是仅仅移动 1,这可能会显着提高您的速度,如果desiredLength 远大于 1。当然,这会使您的功能复杂化,因为:

      1. 如果*(buffer)buffer[desiredLength-1] 等于0xffffffff,那么您不能假设它们之间是连续的,因此您仍然需要检查。
      2. 如果*(buffer) 不等于0xffffffffbuffer[desiredLength-1] 等于0xffffffff,那么您必须跟踪到0xffffffff 序列的开头。
      3. 检查buffer[desiredLength-1]时必须确保不会超出缓冲区

      有点复杂,但它可能会加快速度。希望这是有道理的。

      【讨论】:

      • 获取 cookie 以利用正在搜索的数据属性。
      【解决方案5】:

      如果我想实现它,我会使用memchrmemcmp

      bool found = false;
      std::vector<unsigned char> tmp(desiredLength*sizeof(uint32_t), 0xFF);
      while( true ) {
          void* p = memchr(bufferStart, 0xFF,
              (bufferEnd-bufferStart-desiredLength) * sizeof(uint32_t));
          if( !p ) break;
          if( !memcmp(p, &tmp[0], desiredLength * sizeof(uint32_t)) ) {
              found = true;
              break;
          }
      }
      

      你也可以使用std::search_n,它可能比你自己的代码优化得更好

      【讨论】:

      • desiredLength 是 32 位整数的长度,而不是字符
      【解决方案6】:

      std::search_n 不可用时:

      int i = 1;
      while
      (
          (
              i == 1
              &&
              buffer < bufferEnd
              &&
              (
                  (
                      *buffer == desired
                      &&
                      *(buffer + desiredLength - 1) == desired
                      &&
                      (i = 3)
                  )
                  ||
                  (buffer += desiredLength && (i = 1))
              )
          )
          ||
          (
              i == 2
              &&
              (
                  (
                      buffer > arr
                      &&
                      (*(--buffer) == desired)
                  )
                  ||
                  (i = 3)
              )
          )
          ||
          (
              i >= 3
              &&
              buffer < bufferEnd
              &&
              (
                  (
                      *(buffer++) == desired
                      &&
                      (i++ || true)
                  )
                  ||
                  (i = 1)
              )
              &&
              (
                  i < 3
                  ||
                  i - 3 < desiredLength + 1
              )
          )
      );
      buffer -= i - 4;
      
      if (buffer > bufferEnd - (i-3))
          buffer = bufferEnd;
      

      返回相同的结果只比std:search_n慢一点:

      buffer = std::search_n(buffer, bufferEnd-1, desiredLength, desired);
      if (buffer == bufferEnd-1)
          buffer = bufferEnd;
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-07-02
        • 1970-01-01
        • 2020-09-19
        • 2020-01-28
        • 2012-11-17
        • 2013-08-02
        • 2017-05-10
        相关资源
        最近更新 更多