【问题标题】:How can I strength reduce division by 2^n + 1?我怎样才能将除法强度减少 2^n + 1?
【发布时间】:2010-10-25 17:32:18
【问题描述】:

我需要在我的代码的热路径中执行一些整数除法。我已经通过分析和循环计数确定整数除法正在花费我。我希望我可以做一些事情来加强将师减少到更便宜的东西。

在这条路径中,我除以 2^n+1,其中 n 是可变的。本质上我想优化此功能以删除除法运算符:

unsigned long compute(unsigned long a, unsigned int n)
{
    return a / ((1 << n) + 1);
}

如果我除以 2^n,我只需将 div 替换为右移 n。如果我除以一个常数,我会让编译器的强度降低那个特定的除法,很可能把它变成一个乘法和一些移位。

是否有适用于 2^n+1 的类似优化?

编辑:这里的 a 可以是任意 64 位整数。 n 只取 10 到 25 之间的几个值。我当然可以为每个 n 预先计算一些值,但不能为 a。

【问题讨论】:

  • 对a和n的值有什么限制吗?
  • 你调用函数的上下文是什么?
  • @JR,我看不出转变是问题所在——这是一条指令。这是需要时间的分裂
  • 如果 a 是一个 64 位整数,你应该这样声明它。 unsigned long 只保证有 32 位。使用uint64_tuint_least64_t。对于1 &lt;&lt; n,如果您的n 可能会转到31,您可能会有未定义的行为。请改用UINT64_C(1) &lt;&lt; n

标签: c division integer-division strength-reduction


【解决方案1】:

由于您只能将int 移位这么多位,因此您可以将所有这些情况放入一个常数的多个除法之一中:

unsigned long compute(unsigned long a, unsigned int n)
{
    // assuming a 32-bit architecture (making this work for 64-bits 
    // is left as an exercise for the reader):
    switch (n) {
        case  0: return a / ((1 << 0) + 1);
        case  1: return a / ((1 << 1) + 1);
        case  2: return a / ((1 << 2) + 1);

            // cases 3 through 30...

        case 31: return a / ((1 << 31) + 1);
    }
}

所以现在每个除法都是一个常数,编译器通常会将其简化为一系列乘法/移位/加法指令(如问题所述)。详情请见Does a c/c++ compiler optimize constant divisions by power-of-two value into shifts?

【讨论】:

  • 有趣的想法。或许我可以编写这段代码,然后将强度降低参数提取到一个表格中。
  • 值得一试,但您要在不可预知的跳跃与移位+除法指令和等效的强度降低除法之间的差异之间进行权衡。当这是一个很好的权衡时有什么想法吗?
  • +1,有道理;尽管您可能希望对此进行基准测试以确认实现 switch() 所需的间接和/或条件分支实际上比整数除法要快...
  • 您甚至可以设置一个默认值以使用一般情况。
  • @J.S.:a 可以是什么无关紧要 - 因为n 被限制为小于unsigned int 中的位数,所以除数的数量是有限的。
【解决方案2】:

您可以将整数除法替换为常数、乘法(模字大小)与幻数和移位。

可以预先计算已知常数的幻数。

因为 n 不能取很多值,例如0..31 为所有 n 预先计算这些幻数并将其存储在具有 32 个元素的表中是“容易的”。

Javascript Page for calculating the magic numbers

如果除数在编译时是常数,一个好的编译器可以计算幻数并用乘法和移位代替整数除法。根据其余代码是如何围绕性能关键代码构建的,您可以使用宏或内联技巧来展开所有可能的 n 值,并让编译器完成查找幻数的工作(类似于答案使用开关,但我会在常量区域中放置更多代码,否则可能是不值得的交易 - 分支也会降低您的性能

详细的描述和计算幻数的代码可以在 Henry S. Warren, Jr. 的“Hackers Delight”一书中提供资金(强烈推荐必须拥有这本书!)第 180 页。

相关章节的 Google 图书链接:

Chapter 10-9 Unsigned Division by Divisors >= 1

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-01-12
    • 2022-10-23
    • 1970-01-01
    • 1970-01-01
    • 2021-08-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多