在 C 中调用常数复数的最快方法

fastest way to call constant complex numbers in C

我在 C 中使用 GSL 来处理复数。

我不得不多次使用像 +-1、0、+-i 这样的复数(跨不同的函数),我想像 10^9 这样的东西(也许更多,还不知道),所以我需要一种非常快速的方式来调用它们。

在gsl_complex_math.h中它们是这样定义的:

#define GSL_COMPLEX_ONE (gsl_complex_rect(1.0,0.0))

其中

gsl_complex_rect (double x, double y)
{                               /* return z = x + i y */
  gsl_complex z;
  GSL_SET_COMPLEX (&z, x, y);
  return z;
}

#define GSL_SET_COMPLEX(zp,x,y) do {(zp)->dat[0]=(x); (zp)->dat[1]=(y);} while(0)

为了我的目的,这看起来像是大量的代码和临时变量声明,但我在评估代码效率方面的经验完全为零。

如果我在像 global.h 这样的 header 中声明一个全局变量会怎样:

#if defined MAIN_PROGRAM
    #define EXTERN
#else
    #define EXTERN extern
#endif
EXTERN const gsl_complex C_U = {.dat[0] = 1., .dat[1] = 0.}

1) 我应该期待性能提升吗? 2)代码是否足够干净?我要进入任何陷阱了吗? 3)有更好的方法吗?

  1. 过早的优化是一件坏事。在您有一个可以进行基准测试的工作实现之后(启用编译器优化!)

  2. ,这个小改动很容易实现
  3. a) 将全局常量用于公共值是(适度)一种干净的编码方式。然而,如果库本身已经提供了这样的常量(或者在这种情况下,看起来是常量的函数),那么使用它们通常会更干净(进入项目的新程序员将立即看到发生了什么,而无需遵循您的自定义定义)

    b) 全局常量的潜在问题是:

    • 变量是被复制还是被引用? (每个人在他们自己的情况下可能会更快,通常取决于结构的大小与指针的大小和取消引用的成本)
    • 这个值真的是常量吗? (一个行为不佳的函数可能会抛弃 const-ness 并尝试更改它,这可能会导致一些扩展的调试会话)
    • 你最终可能会得到更糟糕的引用局部性(函数使用的值在内存中的距离会更远,导致缓存命中率降低,可能会降低性能)
  4. 对于这么小的对象,构造开销如此之低,没有比您已经知道的两个选项更好的方法了。对于需要更多努力来构建的更大对象,具有缓存的工厂模式可能会变得合适,但这里不适合。


详细说明(1):

库使用的代码将比您想象的更优化;

  • do...while 是宏定义中的常见模式,任何体面的编译器都会将其完全删除。这只是一种确保多个命令在无大括号 if 以及其他地方
  • 中使用时仍能正常运行的方法
  • 该函数可能对其返回值应用了复制省略(即没有临时值,也没有复制),甚至是完全内联的(没有临时值,没有复制,也没有函数调用)。由于 C 的假设规则
  • ,两者都被允许

这意味着优化后的总成本将是堆栈上的内存 space 和 2 个赋值(如果有一个好的编译器,如果 2 个赋值可以优化为包含以下内容的单个赋值,我不会感到惊讶两个值)。对于较低级别的优化,它还可能涉及函数调用。

所以最后的比较是这样的:

  • 就地构建与复制的全局常量:完全优化后,它们是相同的(使用 Clang 3.7 进行测试和确认)。
  • 就地构建与指向全局常量的指针:根据环境中指针的大小,复制指针可能比创建新对象更快,但由于引用的局部性问题,它可能会执行更糟