【问题标题】:What's the fastest way to extract non-zero indices from a byte array in C++从 C++ 中的字节数组中提取非零索引的最快方法是什么
【发布时间】:2012-09-22 16:32:20
【问题描述】:

我有一个字节数组

unsigned char* array=new unsigned char[4000000];
 ...

我想获取数组中所有非零元素的索引。

当然,我可以这样做

for(int i=0;i<size;i++)
{
    if(array[i]!=0) somevector.push_back(i);
}

还有比这更快的算法吗?

更新 1 我可以看到大多数人的答案是否定的。我希望有一些我不知道的神奇位操作。有些人建议进行排序,但在这种情况下不可行。但是非常感谢您的所有回答。

更新 2 在发布此问题 4 年零 4 个月后,@wim 建议 this answer that looks promising

【问题讨论】:

  • 除非您有特殊限制,否则没有比查看每个元素以检查其是否为零更快的方法了。
  • 示例拉取所有正面元素
  • 怎么会比 O(n) 快?如果不检查每个元素,您将无法分辨。
  • 嗯,您可以做的一件事是避免浮点到整数的转换。 IE。比较 0.f 而不是 0
  • @David,那么我会考虑使用字节数组来存储值,然后进行 ULONG 比较,以便一次比较 4 个字节(32 位架构)。然后如果有一个非零,确定哪些字节是非零。这应该会在一次比较 4 字节的速度上产生显着差异。

标签: c++ algorithm


【解决方案1】:

除非您的向量是有序的,否则如果您使用单线程程序,这是执行您想做的最有效的算法。您可以尝试优化要存储结果的数据结构,但及时这是您能做的最好的。

【讨论】:

    【解决方案2】:

    对于一个大部分为零的字节数组,作为一个稀疏数组,您可以通过一次比较 4 个字节来利用 32 位 CPU。实际比较一次完成 4 个字节,但是如果任何字节不为零,那么您必须确定 unsigned long 中的哪些字节不为零,这样会花费更多的精力。如果数组真的很稀疏,那么通过比较节省的时间可以补偿确定哪些字节非零的额外工作。

    最简单的方法是将 unsigned char 数组的大小设置为 4 个字节的倍数,这样您就不必担心循环完成后的最后几个字节。

    我建议对此进行时序研究,因为这纯粹是推测性的,并且会有一个数组变得不够稀疏,以至于这比简单循环需要更多时间。

    我想问的一个问题是,你对数组的非零元素的偏移量向量做了什么,以及你是否可以取消向量。另一个问题是如果您需要向量,是否可以在将元素放入数组时构建向量。

    unsigned char* array=new unsigned char[4000000];
    ......
    unsigned long *pUlaw = (unsigned long *)array;
    
    for ( ; pUlaw < array + 4000000; pUlaw++) {
        if (*pUlaw) {
            // at least one byte is non-zero
            unsigned char *pUlawByte = (unsigned char *)pUlaw;
            if (*pUlawByte)
                somevector.push_back(pUlawByte - array);
            if (*(pUlawByte+1))
                somevector.push_back(pUlawByte - array + 1);
            if (*(pUlawByte+2))
                somevector.push_back(pUlawByte - array + 2);
            if (*(pUlawByte+3))
                somevector.push_back(pUlawByte - array + 3);
        }
    }
    

    【讨论】:

      【解决方案3】:

      如果非零值相对较少,您可以使用的一个技巧是哨兵值:

      unsigned char old_value = array[size-1];
      array[size-1] = 1; // make sure we find a non-zero eventually
      
      int i=0;
      
      for (;;) {
        while (array[i]==0) ++i; // tighter loop
        if (i==size-1) break;
        somevector.push_back(i);
        ++i;
      }
      
      array[size-1] = old_value;
      if (old_value!=0) {
        somevector.push_back(size-1);
      }
      

      这避免了每次迭代都检查索引和值。

      【讨论】:

        【解决方案4】:

        提高速度的唯一方法就是使用并发。

        【讨论】:

        • 可能不会有太大帮助 - 这里的瓶颈将是 RAM-CPU 流量
        • 是的,数据已经在缓存中了,否则会变慢。
        • 不在多通道板上,两个或多个内核可以独立地从 RAM 流式传输。如果您有双通道或三通道,您仍然可以将速度提高一倍或三倍(忽略同步结果的成本)。
        【解决方案5】:

        这并不是您问题的真正答案,但我试图想象您要解决什么问题。

        有时在对矩阵执行运算(在数学意义上)时,当您知道绝大多数矩阵元素将为零(稀疏矩阵)时,可以改进运算。您根本不使用大数组,而是简单地存储指示非零元素的对 {index, value} 来进行这样的优化。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2020-04-07
          • 1970-01-01
          • 1970-01-01
          • 2011-01-31
          • 1970-01-01
          • 2014-07-12
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多