按位异或整数数组的移位

时间:2009-11-05 13:32:54

标签: c algorithm performance micro-optimization

假设大小为M的比特序列和大小为N的另一比特序列,其中M>> N. M和N都可以保存在整数数组中:如果N的长度为30,则需要一个只有一个整数的数组,但如果N的长度为300,那么将需要一个包含10个整数的数组来存储它。

我要做的是将N移到M内,并且对于M内的每个可能位置k,找到N和M(k)之间的差异数(通过异或)。如果M具有10000位且N具有100位,则存在10000-100 = 9900个位置,其中将执行XOR比较。

您是否知道可以执行此操作的库或可能提出算法?我知道它可以通过许多其他方式完成,但我相信最快的方法是这里提出的方法。如果你能想到一个更快的方式,那么我愿意接受建议!

我更喜欢C或C ++中的东西,但其他语言,甚至伪代码也是可以接受的。

提前致谢。

3 个答案:

答案 0 :(得分:1)

简单方法:

while N < M * (original N) do
  compute and tally up M xor N
  multiply each word (unsigned) in N by 2,   // i.e. shift left 1 bit
    and add in the carry (= overflow) from the previous word.

现代CPU足够强大,即使是10,000和100位,这也只需要几毫秒。

要“计算和计算M xor N”,

sum = 0
for (i=0; i<M/8; i++)
   if M[i] != 0
      w = M[i]
      while w != 0
      if ((w & 1) != 0) sum++   // test LSB
      w /= 2                    // shift right 1 bit

有很多方法可以优化它。有很多数字在大多数情况下都是0,你可以识别并忽略这些......但上面的算法应该让你开始。

答案 1 :(得分:1)

这是一个完整且有效的解决方案。作为读者的练习,留下了轻微的邋。:)

#include "stdio.h"
#include "stdlib.h"
#include "string.h"

#define M_SIZE 100
#define N_SIZE 25
#define bitsToBytes(n) ((n + 7)/8)

typedef unsigned char byte;

void dumpBytes(byte *arr, size_t size) {
   int b;
   for (b=0; b<size; b++) {
      printf("%02x", *arr++);
   }
   printf("\n");
}

int main(int argc, char *argv[]) {

   byte M[bitsToBytes(M_SIZE)], N[bitsToBytes(N_SIZE)];

   /* Fill M and N with random bits */

   int b;

   for (b=0; b<sizeof(M); b++) {
      M[b] = 0xff & rand();
   }
   for (b=0; b<sizeof(N); b++) {
      N[b] = 0xff & rand();
   }

   /* Create a couple of arrays big enough for M_SIZE + N_SIZE, 
      to allow shifting N all the way before the left of M. */
   #define MN_SIZE (M_SIZE + N_SIZE)
   #define MN_BYTES (bitsToBytes(MN_SIZE))
   byte MM[MN_BYTES], NN[MN_BYTES];

   /* Zero out MM, NN, then copy M, N there (right justified). */
   int offset = sizeof(MM) - sizeof(M);
   memset (MM, 0, sizeof(MM)); memcpy(MM+offset, M, sizeof(M));
   offset = sizeof(NN) - sizeof(N);
   memset (NN, 0, sizeof(NN)); memcpy(NN+offset, N, sizeof(N));

   dumpBytes(MM, MN_BYTES);
   /* Count up "difference bits" until NN has been left-shifted into oblivion. */
   int s;
   for (s=0; s<MN_SIZE; s++) {
      int sum = 0;
      for (b=0; b<MN_BYTES; b++) {
  int xor = MM[b] ^ NN[b];
  while (xor != 0) {
     sum += (xor & 1);
     xor >>= 1;
  }
      }
      dumpBytes(NN, MN_BYTES);
      printf("Shift: %4d; bits: %3d.\n", s, sum);
      /* shift NN one bit to the left */
      for (b=0; b<MN_BYTES; b++) {
  NN[b] <<= 1;
  if (b < (MN_BYTES - 1) && ((NN[b+1] & 0x80) != 0)) NN[b] |= 1;
      }
   }
}

答案 2 :(得分:1)

你可以将N向上移动超过M或将M向下移动到N.当移动N时,如果输入与字大小不对齐,你还需要移动掩模。这些移位可以缓存到一个字长大小的数组中,但是如果每个字移位多个字是1个指令(如果你使用RCR指令),则可能不值得挽救L1缓存的风险。 / p>

除非您可以依靠带有POPCNT指令的Core i7处理器,否则最重要的部分将是位计数。有关位计数的快速实现,请参阅this page

对于较小的N长度(在机器字中),通过内圈的特殊套管可以大大提高速度。对于具有SSE4.2的处理器上的N <= 192位,它应该能够运行两个最内层的循环并将所有内容保持在寄存器中。我生锈的ASM向我显示了14个实时寄存器,其中最内层循环(移位64位位置)长度为20条指令,另外5条用于读取输入中的下一个字。)