【问题标题】:Computing minimum and maximum excess of byte over binary function计算二进制函数上字节的最小和最大过量
【发布时间】:2013-11-07 07:43:12
【问题描述】:

我有一个函数PI (input 0 or 1),它给出了PI[0] = -1, PI[1] = 1

给定一个字节 B,我想要一个函数计算从左到右超过 PI 的最小超量。同样,我需要一个函数来计算从左到右超过 PI 的最大超出量。示例:

PI_MIN[0] = -8, PI_MAX[0] = -1

PI_MIN[1] = -7, PI_MAX[1] = -1

PI_MIN[2] = -6, PI_MAX[2] = -1

PI_MIN[3] = -6, PI_MAX[3] = -1

目前我预先计算函数值,将它们存储在通用表中,并在运行时访问它。或者,我天真地计算结果(for loop over bits)。对于PI_MINPI_MAX,我们有:

static constexpr int8_t PI_MIN[] { -8, -7, -6, -6, -6, -5, -5, -5, -6, -5, -4, -4, -4, -4, -4, -4, -6, -5, -4, -4, -4, -3, -3, -3, -4, -3, -3, -3, -3, -3, -3, -3, -6, -5, -4, -4, -4, -3, -3, -3, -4, -3, -2, -2, -2, -2, -2, -2, -4, -3, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -6, -5, -4, -4, -4, -3, -3, -3, -4, -3, -2, -2, -2, -2, -2, -2, -4, -3, -2, -2, -2, -1, -1, -1, -2, -1, -1, -1, -1, -1, -1, -1, -4, -3, -2, -2, -2, -1, -1, -1, -2, -1, -1, -1, -1, -1, -1, -1, -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -6, -5, -4, -4, -4, -3, -3, -3, -4, -3, -2, -2, -2, -2, -2, -2, -4, -3, -2, -2, -2, -1, -1, -1, -2, -1, -1, -1, -1, -1, -1, -1, -4, -3, -2, -2, -2, -1, -1, -1, -2, -1, 0, 0, 0, 0, 0, 0, -2, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -4, -3, -2, -2, -2, -1, -1, -1, -2, -1, 0, 0, 0, 0, 0, 0, -2, -1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, -2, -1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 };

static constexpr int8_t PI_MAX[] { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, -1, -1, -1, -1, -1, -1, -1, 0, -1, -1, -1, 0, 0, 0, 1, 2, -1, -1, -1, -1, -1, -1, -1, 0, -1, -1, -1, 0, 0, 0, 1, 2, 0, 0, 0, 0, 0, 0, 1, 2, 1, 1, 1, 2, 2, 2, 3, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 0, 0, 0, 0, 0, 0, 1, 2, 1, 1, 1, 2, 2, 2, 3, 4, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 2, 2, 2, 3, 4, 2, 2, 2, 2, 2, 2, 3, 4, 3, 3, 3, 4, 4, 4, 5, 6, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 2, 2, 2, 3, 4, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 2, 2, 2, 3, 4, 2, 2, 2, 2, 2, 2, 3, 4, 3, 3, 3, 4, 4, 4, 5, 6, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 4, 2, 2, 2, 2, 2, 2, 3, 4, 3, 3, 3, 4, 4, 4, 5, 6, 3, 3, 3, 3, 3, 3, 3, 4, 3, 3, 3, 4, 4, 4, 5, 6, 4, 4, 4, 4, 4, 4, 5, 6, 5, 5, 5, 6, 6, 6, 7, 8 };

不幸的是,我找不到我需要使用的所有功能的模式(例如PI_MIN, PI_MAX,但还有更多)。问题是:如何找出是否存在可以以非天真的方式计算它的函数(即,输入字节中没有从左到右的 for 循环)。我的目标是达到最大性能,因为这个函数在一个更大的程序的最内层循环中。

感谢任何提示!

【问题讨论】:

  • 最大性能将是您的表查找,不是吗?而且你对 PI_MIN 和 PI_MAX 的定义我一点都不清楚。
  • 缓存未命中时,表查找很糟糕,我需要一个函数来避免内存访问。 PI_MAX 是 PI 的最大和,你可以得到一个字节,PI_MIN 是最小和。将输入视为字节,即 00000000、00000001、00000010、00000011 等
  • “最小超出 PI”是什么意思?
  • 给定一个字节 B,其位为 b0、b1、..、b7,其中 b0 是最高有效位,计算总和 PI[b0],然后计算 PI[b0]+PI[b1] ,然后是 PI[b0]+PI[b1]+PI[b2],等等,直到你有 PI[b0]+..+PI[b7]。你得到的最大值是 PI_MAX[B],你得到的最小值是 PI_MIN[B]。例如。对于 B = 0,PI_MIN[0] = -8,因为 PI[b0] + PI[b1] + .. + PI[b7] 小于您可能在 B 上计算的所有其他总和
  • @user46317,这非常清楚。谢谢。那样的话,你不是只计算字节中的 0 和 1 吗?

标签: c++ function binary pattern-matching


【解决方案1】:

pi_min 的非分支版本(假设循环已展开)。

/*
  Calculate:
    min(
      pi(b7),
      pi(b7)+pi(b6),
      pi(b7)+pi(b6)+pi(b5),
      pi(b7)+pi(b6)+pi(b5)+pi(b4),
      pi(b7)+pi(b6)+pi(b5)+pi(b4)+pi(b3),
      pi(b7)+pi(b6)+pi(b5)+pi(b4)+pi(b3)+pi(b2),
      pi(b7)+pi(b6)+pi(b5)+pi(b4)+pi(b3)+pi(b2)+pi(b1),
      pi(b7)+pi(b6)+pi(b5)+pi(b4)+pi(b3)+pi(b2)+pi(b1)+pi(b0))

  Where,
    pi(b) = b ? 1 : -1
  and bits in byte b are numbered with the least significant bit (LSB) as 0.

  This problem is essentially one of counting leading zeros where a string
  of leading zeros may be interrupted by a one if it is eventually followed
  by a zero. What happens if there are no leading zeros, then the count is -1.

  The algorithm uses two stacks, "c0" and "c1". c0 is the leading zero count
  and c1 is a stack of potentially intervening 1's.

  foreach bit (following 4 cases are mutually exclusive, only 1 will execute)
    0: if the '1' stack is empty => push a '0' onto the '0' stack
    0: if the '1' stack is not empty => pop a '1' 
    1: if the first bit is a '1' => put the '0' stack in underflow state
    1: if it is not the first bit => push a '1' onto the '1' stack
  return -c0 because zeros actually count as -1
*/
int pi_min(uint8_t byte) {
  int c0 = 0;
  int c1 = 0;

  for (int i = 0; i < 8; ++i) {
    uint8_t b = !!(byte & (1 << (7-i)));
    c0 -= (b & (i == 0));
    c0 += ((!b) & (0 >= c1));
    c1 -= ((!b) & (0 < c1));
    c1 += (b & (i != 0));
  }
  return -c0;
}

int pi_max(uint8_t byte) { return -pi_min(~byte); }

// The obvious version for comparison.
int pi(uint8_t bit) { return bit ? 1 : -1; }

int pi_min_simple(uint8_t byte) {
  int sum = 0;
  int m = 9;

  for (int i = 0; i < 8; ++i) {
    uint8_t b = byte & (1 << (7-i));
    sum += pi(b);
    m = std::min(m, sum);
  }
  return m;
}

【讨论】:

  • 非常感谢您的方法,我会尽快尝试!计算最大和的函数将是相似的或类似的,不是吗?
  • 是的。计数 1 而不是 0。答案更新为pi_max。让我们知道您的绩效衡量结果如何。
【解决方案2】:

抱歉耽搁了,我现在已经测量了不同方法的性能。

http://s12.postimg.org/v400xibxp/prefix_Sums.png

我很高兴看到 Adam Burry 提出的解决方案非常有效(黄线)。如您所见,对于最小和最大前缀和计算,即使是简单的算法也比查表(绿色和棕色线)略快,这确实非常相似......最令人惊讶的事情(至少对我来说)是maxExcess 的糟糕表现(正如 Adam Burry 建议的那样,它只是返回了 -pi_min(~byte),其中 pi_min 是代表黄线的函数)。我想这与计算每个被分析字节的二进制补码的额外开销有关,所以我将切换到原始算法(pi_min)并返回 -c1 来实现 pi_max。

【讨论】:

  • 这个版本更清晰,最小和最大前缀计算使用表而不是使用表(Adam Burrys 方法代替,返回 -p1 用于最大变体):s22.postimg.org/wnzm5yjlt/prefix_Sums.png
  • 您使用的是什么编译器、编译器版本和标志?我的 pi_max 单行版本的结果也让我非常惊讶。应该没有函数调用开销,因为我希望它是内联的。
  • g++ -c -fno-builtin -fno-rtti -std=gnu++11 -march=native -msse -msse2avx -DNDEBUG ...,我使用的g++版本是4.8.1。在我的情况下没有函数调用开销,我已经验证该函数已内联在生成的代码中。如果我有时间,我这周会尝试 icc
猜你喜欢
  • 2015-10-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-08-17
  • 2012-04-14
  • 1970-01-01
相关资源
最近更新 更多