【问题标题】:Are C/C++ library functions and operators the most optimal ones?C/C++ 库函数和运算符是最优化的吗?
【发布时间】:2016-10-27 15:28:10
【问题描述】:

所以,在分治法课程中,我们学到了:

  1. Karatsuba 乘法
  2. 快速求幂

现在,给定 2 个正整数 a 和 b 是 operator::*karatsuba(a,b) 快或比 pow(a,b)

int fast_expo(int Base, int exp)
{
    if (exp == 0) {
        return 1;
    }
    if (exp == 1) {
        return Base
    }
    if (exp % 2 == 0) {
        return fast_expo(Base, exp / 2) * fast_expo(Base, exp / 2);
    }
    else {
        return base * fast_expo(Base, exp / 2) * fast_expo(Base, exp / 2);
    }
}

我问这个是因为我想知道它们是否只是为了教学目的,或者它们已经在 C/C++ 语言中实现了基础

【问题讨论】:

  • ints 不够高,不足以让这些东西有用。 int 乘法中的疯狂优化都是由英特尔等公司的人员进行的电气工程。
  • 所以它们只有在我需要使用非常大的数字时才有用?
  • 这是教授算法和边界条件的一个很好的例子。编译器的实际输出针对运行代码的 CPU 架构进行了高度优化。
  • 没有语言 C/C++。如果您指的是两种不同的语言 C 和 C++,请选择 一种 (我改为 C++,因为您使用 C++ 表示法作为运算符)并澄清您的问题。并正确格式化这个烂摊子!
  • “最佳”在不了解上下文(即正在解决的问题)的情况下并没有真正的意义。

标签: c++ divide-and-conquer


【解决方案1】:

Karatsuba 乘法是一种用于大整数的特殊技术。它无法与内置的 C++ * 运算符相乘,后者将 intdouble 等基本类型的操作数相乘。

要利用 Karatsuba,您必须使用至少由大约 8 个单词组成的多精度整数。 (512 位,如果这些是 64 位字)。根据this question 的公认答案,Karatsuba 变得有利的收支平衡点在 8 到 24 个机器字之间。

pow 函数与一对double 类型的浮点操作数一起使用,与您的fast_expo 无法相比,后者与int 类型的操作数一起使用。它们是具有不同要求的不同功能。使用pow,您可以计算 5 的立方根:pow(5, 1/3.0)。如果这就是你想要计算的,那么fast_expo 不管多快都没用。

无法保证您的编译器或 C 库的 pow 绝对是您的机器对两个双精度浮点数求幂的最快方法。

浮点中的优化声明可能很棘手,因为“相同”函数的多个实现通常不会给出完全相同的结果,直到最后一位。您可能可以编写一个快速的my_pow,它只能达到五位小数的精度,并且在您的应用程序中,这种近似值可能绰绰有余。你打过图书馆吗?几乎不;您的快速函数不符合将其作为库中 pow 替代品的要求。

【讨论】:

    【解决方案2】:

    operator::* 和其他标准运算符通常映射到硬件提供的原语。如果这些原语不存在(例如 IA32 上的 64 位 long long),编译器会以性能损失为代价模拟它们(gcc 在 libgcc 中这样做)。

    std::pow 也一样。它是标准库的一部分,并不强制要求以某种方式实现。 GNU libc 将pow(a,b) 实现为exp(log(a) * b)explog 很长,编写时考虑到了 IEEE754 浮点的最佳性能。


    至于你的建议:

    小数的 Karatsuba 乘法不值得。处理器提供的乘法机器指令已经针对所使用的标准数据类型的速度和功耗进行了优化。更大的数字,10-20倍的寄存器容量,it starts to pay off

    GNU MP Bignum Library中,曾经有一个默认值 KARATSUBA_THRESHOLD非模乘法高达32 (也就是说,当n>=32w 通常使用w=32 时使用 Karatsuba); 模幂运算的最佳阈值趋向于 明显更高。在现代 CPU 上,软件中的 Karatsuba 倾向于 对 P-256 上的 ECDSA(n=256w=32w=64),但可以想象它对于 RSA 中使用的更宽的模数很有用。

    这里是list with the multiplication algorithms,GNU MP 使用和它们各自的阈值。

    快速求幂不适用于非整数幂,因此无法与pow 相比。

    【讨论】:

      【解决方案3】:

      检查操作速度的一个好方法是测量它。如果您将计算运行十亿次左右,并查看执行所需的时间,您就会得到答案。

      有一点需要注意。我被引导相信 % 相当昂贵。有一种更快的方法来检查某个东西是否可以被 2 整除:

      check_div_two(int number)
      {
          return ((number>>1) & 0x01);
      }
      

      这样,您刚刚完成了位移并与掩码进行了比较。我认为这是一个更便宜的操作。

      【讨论】:

      • 不应该只是 (number & 1) == 0 吗?您正在丢弃 lsb,因此奇数仍然未经测试
      • % 在处理未知值时通常可能会更昂贵,但每个编译器都足够聪明,可以识别x % 2 == 0,并将其优化为与(x & 1) == 1 完全相同的代码。
      • (number>>1) & 0x01 测试第二位是否为1,其中“可被二除”(偶数与奇数)必须是对第一位0 的测试。
      【解决方案4】:

      用于内置类型的* 运算符几乎肯定会被实现为单个 CPU 乘法指令。所以最终这是一个硬件问题,而不是语言问题。在没有直接硬件支持的情况下,可能会生成更长的代码序列,也许是函数调用。

      可以肯定的是,芯片制造商(英特尔、AMD 等)花费了大量精力使算术运算尽可能高效。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-12-19
        • 2021-03-29
        • 2013-07-31
        • 2015-02-04
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多