加,减和比较压缩整数

时间:2018-07-26 22:09:44

标签: c++ arrays bit-manipulation

我需要在相等的小整数数组上进行大量简单的代数运算。该操作仅由三种组成:(i)按元素加数组和(ii)按元素逐个删除数组,以及(iii)比较一个数组中的所有元素是否都小于或大于另一个数组中的对应元素。

为了提高缓存的局部性和计算速度,我将每个数组的小整数逐位填充为一定数量的64位整数。使用的64位整数的数量由分配给数组元素的位数确定。让a[j]表示一个数组元素。我为a[j]设计的位包括:(i)可以容纳最大绝对值a[j]的位,在计算过程中可能会击中{strong} ,(ii)一个符号位,和(iii )放在符号位的左侧。最左边的位保留右边可能的进位,并在加法或减法后归零。

下面是一个玩具示例,其中添加,减去和比较两个64位整数,每个整数都包含5个小整数:前10位,后5位,后10位,后13位和接下来的20位。其余位无效,设置为0。

// leftmostBitMask = 
// 0b0111111111011110111111111011111111111101111111111111111111000000
//   ^         ^    ^         ^            ^                  
// leftmost


std::size_t add(std::size_t x, std::size_t y, std::size_t leftmostBitMask)
{
  return (x + y) & leftmostBitMask;
}


std::size_t minus(std::size_t x, std::size_t y, std::size_t leftmostBitMask)
{
  return (x - y + ((~leftmostBitMask) << 1)) & leftmostBitMask;
}


bool notAllGreaterEqual(std::size_t x, std::size_t y, std::size_t leftmostBitMask)
{
  // return (minus(x, y, leftmostBitMask) & (leftmostBitMask >> 1)) == 0;
  return (x - y) & ((~leftmostBitMask) >> 1);
}

我的算法似乎很复杂,尤其是比较功能。有没有更快的解决方案?

谢谢!

顺便说一句,SIMD不是我要描述的。我的问题是比SIMD低一级的优化。

更多背景:这个想法在多维空间中提供了一种非常复杂的搜索算法。我们观察到在不同维度上的值大小之间存在较大差异。例如,在计算一个重要的6维测试用例时,一个维的绝对值可能达到50000,而其他所有维的绝对值都大大低于1000。如果没有整数压缩,则每个对象都需要一个大小为6的32位数组,而整数压缩会减小维度为1(64位整数)。这样的减少促使我考虑填​​充整数。

1 个答案:

答案 0 :(得分:1)

经过仔细的思考和全面的模拟,问题中列出的算法在很大程度上被过度设计。不需要用于接收进位的最左边的位。下面的代码有效:

// signBitMask = 
// 0b1000000000100001000000000100000000000010000000000000000000000000
//   ^         ^    ^         ^            ^                  
// sign bit


std::size_t add(std::size_t x, std::size_t y)
{
  return x + y;
}


std::size_t subtract(std::size_t x, std::size_t y)
{
  return x - y;
}


bool notAllGreaterEqual(std::size_t x, std::size_t y, std::size_t signBitMask)
{
  return (x - y) & signBitMask != 0;
}

这里的关键因素是在两个数组上进行的每个比较都是基于AND的。只要notAllGreaterEqual()中的一个元素整数低于x中的对应元素,我们就要求y返回true。乍看之下,上面的解决方案几乎是不正确的:将一个负元素整数添加到一个正整数中,结果保持正值会怎样?符号位必须有一个进位。在这种情况下,连续元素整数不被污染吗?答案是肯定的,但没关系notAllGreaterEqual()仍将完全实现其目的。不用逐位思考,就可以轻松证明notAllGreaterEqual()用基本代数是正确的。仅当我们想从那些64位缓冲区中恢复整数数组时,问题才会出现。

创建64位缓冲区包括:(i)将整数强制转换为std::size_t,(ii)将整数移位预先计算的位,以及(iii)添加移位的整数。如果一个负数向右移动,则1必须在其左侧填充。