【问题标题】:How to fast set 9 bits after all '1's in an 64 bit integer如何在 64 位整数中的所有 1 之后快速设置 9 位
【发布时间】:2019-07-12 06:30:02
【问题描述】:

我正在编写一个 C++ 程序,并且需要一个在所有现有 '1' 之后将所有 9 位设置为 1 的函数。

也就是说,我要编写一个函数void set10BitsFull(int64_t& n),它对于整数“int64_t n = 0b...1000000000...”,set10BitsFull(n)n 转换为“0b...1111111111...”。

(更新)输入整数的位被稀疏设置为 1,并且两个 1 之间至少有 10 位距离。对于样本输入0x20000200,预期输出为0x3FF003FF。在最后一个 1 之后至少会有 9 位 0。最左边的 10 位将始终为零。

这是我对这个函数的实现

/**
 * Inline function that set 10 bits to 1 after each set 1
 * i.e.,
 * ......1000000000...... -> ......1111111111.......
 *
 * @param n
 *      pointer of input number
 */
inline void set10BitFull(int_fast64_t *n) {
    // n = 1000000000
    *n |= (*n >> 1); // n = 1100000000
    *n |= (*n >> 2) | (*n >> 4) | (*n >> 6) | (*n >> 8); // n = 1111111111
}

在程序的主循环中,这两行代码会被频繁调用,在之前的测试中,计算成本非常高。因此,我想寻求一种计算开销更少(计算的 cpu 周期更少)的方法,可能的解决方案可能包括:

  • 使用预先计算的掩码
  • 内联汇编
  • x86/gcc 内置内在 ...

【问题讨论】:

  • 您的问题需要对预期输出与输入的比较进行一些改进。 “在所有 1 之后设置 9 位”没有多大意义。你能澄清一下吗?否则,我想知道为什么您的实现不是n = n | 0x1ff
  • 您希望每个位都向右复制 9 次?如果输入是 0b100 怎么办?如果输入是 0b1001001001001001001 怎么办?等等。正如其他人所提到的,您需要更好地定义您想要实现的目标。
  • 另外:对有符号整数类型进行位移时要小心。 Implementation defined and even undefined behavior are just around the corner。您可能应该使用无符号整数类型。
  • @selbie:“在每个设置 1 之后将 10 位设置为 1 的内联函数”。这对我来说很清楚。
  • @Federico :操作员要求一种优化的方法来做某事。准确了解预期的行为将对此有很大帮助。例如,也许提到的一些可能的输入是永远不可能的。也许只有一个位设置。也许这种行为并不完全是你所理解的。任何这些知识都可以提供一种更快的方法。

标签: c++ bit-manipulation


【解决方案1】:

你可以这样做:

constexpr uint_fast64_t set10BitFull(uint_fast64_t n) {
    return (n << 1) - (n >> 9);
}

这应该适用于您描述的所有输入,其中每 1 位后至少有 9 个 0 位。

【讨论】:

    【解决方案2】:

    首先,你需要摆脱指针,访问内存是处理器会做的最慢的操作。 其次,您可以通过不断重复 1 的数量来减少操作次数。

    即像这样:

     n |= n >> 1;  // will porduce 1100000000
     n |= n >> 2;  // will produce 1111000000
     n |= n >> 4;  // will produce 1111111100
     n |= n >> 2;  // will produce 1111111111
    

    【讨论】:

    • 优化编译器会将*n放入一个寄存器,因此原始代码不会访问所有*n的内存。当然,减少操作次数可能会有所帮助。
    • Godbolt 不同意存在任何性能差异(提示:它使用寄存器!)godbolt.org/z/lDXS7v
    • 但是它仍然在函数开始时从内存中读取值并在函数结束时写入内存,所以我猜如果经常调用它,将其重构为 inline int_fast64_t set10BitFull(int_fast64_t n) 会快得多。
    • @sklott 这并不完全正确。您可以在函数体本身中保存几个 mov,以便在 DSO 导出该方法时 可能 为真。但是,在几乎所有可能使用该方法的地方,编译器都会修改它们以使它们相同。 godbolt.org/z/bd21vI
    • 如果数据在一级缓存中,速度非常快(绝对不是“最慢的操作处理器会做”。整数除法要慢得多。)。此外,优化编译器将删除无关的内存访问(这是内联函数,所以如果可能的话,它肯定会被删除)。更不用说,对于多个 ABI,将 n 作为参数传递也会访问内存(如果它是通过堆栈而不是寄存器传递)。
    猜你喜欢
    • 2014-01-09
    • 2012-07-07
    • 1970-01-01
    • 2023-03-07
    • 2011-01-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多