Cython 中 complex.real 和 creal(complex) 的区别

Difference between complex.real and creal(complex) in Cython

为了在 cython 中将真实部分与复杂部分分开,我通常使用 complex.realcomplex.imag 来完成这项工作。然而,这确实会在 html- 输出中生成颜色为 "python red" 的代码,我想我应该使用 creal(complex)cimag(complex) 来代替。

考虑以下示例:

cdef double complex myfun():
    cdef double complex c1,c2,c3
    c1=1.0 + 1.2j
    c2=2.2 + 13.4j
    c3=c2.real + c1*c2.imag
    c3=creal(c2) + c1*c2.imag
    c3=creal(c2) + c1*cimag(c2)
    return c2

c3 的分配给出:

__pyx_v_c3 = __Pyx_c_sum(__pyx_t_double_complex_from_parts(__Pyx_CREAL(__pyx_v_c2), 0), __Pyx_c_prod(__pyx_v_c1, __pyx_t_double_complex_from_parts(__Pyx_CIMAG(__pyx_v_c2), 0)));

__pyx_v_c3 = __Pyx_c_sum(__pyx_t_double_complex_from_parts(creal(__pyx_v_c2), 0), __Pyx_c_prod(__pyx_v_c1, __pyx_t_double_complex_from_parts(__Pyx_CIMAG(__pyx_v_c2), 0)));

__pyx_v_c3 = __Pyx_c_sum(__pyx_t_double_complex_from_parts(creal(__pyx_v_c2), 0), __Pyx_c_prod(__pyx_v_c1, __pyx_t_double_complex_from_parts(cimag(__pyx_v_c2), 0)));

其中第一行使用(python 彩色)构造 __Pyx_CREAL__Pyx_CIMAG.

这是为什么,它会影响性能吗"significantly"?

肯定是默认的C库 (complex.h) 会起作用 给你。

但是,这个库似乎不会给你任何显着的改进 与 c.real c.imag 方法相比。通过将代码放在 with nogil: 块,你可以检查你的代码是否已经没有调用 Python API:

cdef double complex c1, c2, c3
with nogil:
    c1 = 1.0 + 1.2j
    c2 = 2.2 + 13.4j
    c3 = c2.real + c1*c2.imag

我使用 Windows 7 和 Python 2.7,它们在 Visual Studio 编译器 9.0 的内置 C 库(与 Python 2.7 兼容)。 因此,我创建了一个 equivalet 纯 C 函数来检查任何可能的 与 c.realc.imag 相比的收益:

cdef double mycreal(double complex dc):
    cdef double complex* dcptr = &dc
    return (<double *>dcptr)[0]

cdef double mycimag(double complex dc):
    cdef double complex* dcptr = &dc
    return (<double *>dcptr)[1]

在运行之后有以下两个测试函数:

def myfun1(double complex c1, double complex c2):
    return c2.real + c1*c2.imag

def myfun2(double complex c1, double complex c2):
    return mycreal(c2) + c1*mycimag(c2)

得到时间:

In [3]: timeit myfun1(c1, c2)
The slowest run took 17.50 times longer than the fastest. This could mean that a
n intermediate result is being cached.
10000000 loops, best of 3: 86.3 ns per loop

In [4]: timeit myfun2(c1, c2)
The slowest run took 17.24 times longer than the fastest. This could mean that a
n intermediate result is being cached.
10000000 loops, best of 3: 87.6 ns per loop

确认 c.realc.imag 已经够快了。

实际上你不应该期望看到任何区别:__Pyx_CREAL(c)__Pyx_CIMAG(c) 可以查找 here,并且不涉及火箭 science/black 魔法:

#if CYTHON_CCOMPLEX
  #ifdef __cplusplus
    #define __Pyx_CREAL(z) ((z).real())
    #define __Pyx_CIMAG(z) ((z).imag())
  #else
    #define __Pyx_CREAL(z) (__real__(z))
    #define __Pyx_CIMAG(z) (__imag__(z))
  #endif
#else
    #define __Pyx_CREAL(z) ((z).real)
    #define __Pyx_CIMAG(z) ((z).imag)
#endif

它基本上是说,这些是定义并导致无开销的调用:

  1. std::complex<>.real()std::complex<>.imag()如果是 c++。
  2. GNU extensions__real____imag__,如果是gcc或者intel编译器(intel前段时间加入了支持)。

你打不过它,例如在 glibc, creal uses __real__. What is left, is Microsoft compiler (CYTHON_CCOMPLEX is not defined), for that an own/special cython-class 中使用:

#if CYTHON_CCOMPLEX
  ....
#else
    static CYTHON_INLINE {{type}} {{type_name}}_from_parts({{real_type}} x, {{real_type}} y) {
      {{type}} z;
      z.real = x;
      z.imag = y;
      return z;
    }
#endif

通常情况下,不应该his/her自己编写复数实现,但是如果只考虑访问实部和虚部,那么您不会犯太多错误。我不会保证其他功能,但不希望它比通常的 windows 实现慢很多(但如果你有的话,我想看看结果)。

作为结论: gcc/intel 编译器没有区别,我不会为其他编译器的区别担心太久。