【问题标题】:Fast Popcount instruction or Hamming distance for binary array?二进制数组的快速 Popcount 指令或汉明距离?
【发布时间】:2012-07-05 01:07:43
【问题描述】:

我在 Visual Studio 2010 C++ 上实现

我有两个二进制数组。例如,

array1[100] = {1,0,1,0,0,1,1, .... }
array2[100] = {0,0,1,1,1,0,1, .... }

要计算array1array2 之间的Hamming distancearray3[100] 存储array1array2xor 结果。

然后我必须计算array31 的位数。为此,我知道我可以使用__popcnt 指令。

现在,我正在做如下的事情:

popcnt_result = 0;
for (i=0; i<100; i++) {
    popcnt_result = popcnt_result + __popcnt(array3[i]);
}

它显示了一个很好的结果,但速度很慢。我怎样才能让它更快?

【问题讨论】:

  • int 的那些数组吗?它们只包含01 的值吗?
  • 您是否可以仅用 1 位(而不是字节)来表示每个“数组条目”?
  • @Blastfurnace 是的,我有二进制整数数组,所以只有 0 或 1
  • @reuben 是的,我想这样做..

标签: c++ performance bit-manipulation hamming-distance


【解决方案1】:

array3 似乎有点浪费,您正在访问一个不需要的额外 400 字节内存。我会尝试将您拥有的内容与以下内容进行比较:

for (int i = 0; i < 100; ++i) {
    result += (array1[i] ^ array2[i]);   // could also try != in place of ^
}

如果这有帮助,那么我将其作为练习留给读者如何应用此更改黄昏。

【讨论】:

  • 并让循环展开游戏开始真正挤出该算法的所有最后性能:-)
【解决方案2】:

在实施时,__popcnt 调用没有帮助。它实际上让你放慢了速度。

__popcnt 计算其参数中设置的位数。您只传入一个元素,看起来它保证为 0 或 1,因此结果(也是 0 或 1)没有用。这样做会稍微快一些:

popcnt_result += array3[i];

根据阵列的布局方式,您可能会也可能不会以更聪明的方式使用__popcnt。具体来说,如果您的数组包含一个字节的元素(例如,charboolint8_t 或类似的),您可以一次对四个元素执行填充计数:

for(i = 0; i < 100; i += 4) {
    uint32_t *p = (uint32_t *) &array3[i];
    popcnt_result += __popcnt(*p);
}

(请注意,这取决于 100 可以被 4 整除的事实。否则,您必须为最后几个元素添加一些特殊情况处理。)

如果数组包含较大的值,例如 int,那么您就很不走运了,并且仍然不能保证这会比上面的幼稚实现更快。

【讨论】:

  • 提议的更改不违反严格的别名吗?
  • 是的,确实如此——不过,它正在做一些从根本上“恶心”的事情,所以我不相信有任何解决办法。
  • 解决它的一种方法是uint32_t u; memcpy(&amp;u, array3+i, sizeof u);,然后检查优化器是否足够聪明,可以消除u,只需将数组中的 4 个字节加载到寄存器中进行弹出计数。我不知道 MSVC 是否是,但因为你没有违反严格的别名,它不会做任何令人讨厌的事情,比如在循环之前切换 array,因为它认为它没有使用,并将内存用于其他用途.请注意,我也不知道 MSVC 是否足够聪明,可以在您违反严格别名时做任何令人讨厌的事情......
【解决方案3】:

如果您的数组仅包含两个值(01),则汉明距离只是对应值不同的位置数。这可以使用标准库中的std::inner_product 一次性完成。

#include <iostream>
#include <functional>
#include <numeric>

int main()
{
    int array1[100] = { 1,0,1,0,0,1,1, ... };
    int array2[100] = { 0,0,1,1,1,0,1, ... };

    int distance = std::inner_product(array1, array1 + 100, array2, 0, std::plus<int>(), std::not_equal_to<int>());

    std::cout << "distance=" << distance << '\n';

    return 0;
}

【讨论】:

    猜你喜欢
    • 2015-12-20
    • 2016-12-16
    • 2017-04-03
    • 2014-09-25
    • 2011-06-14
    • 2011-07-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多