Разница между complex.real и creal(complex) в Cython

Чтобы отделить реальную часть от сложной части в cython, я обычно использовал complex.real и complex.imag для работы. Однако это генерирует код, окрашенный в слегка красный цвет питона в html-выводе, и я предполагаю, что вместо этого я должен использовать 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)));

где первая строка для использования конструкции (цвета питона) __Pyx_CREAL и __Pyx_CIMAG.

Почему это так, и влияет ли это на производительность «значительно»?


person Mikael Fremling    schedule 22.09.2017    source источник


Ответы (2)


Конечно, стандартная библиотека C (complex.h) подойдет вам. .

Однако кажется, что эта библиотека не даст вам каких-либо значительных улучшений по сравнению с подходом c.real c.imag. Поместив код в блок with nogil:, вы можете убедиться, что ваш код уже не вызывает API Python:

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, у которых нет complex.h во встроенной библиотеке C для компиляторов Visual Studio 9.0 (совместимых с Python 2.7). Из-за этого я создал эквивалентную чистую функцию C, чтобы проверить любые возможные выгоды по сравнению с c.real и c.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.real и c.imag уже достаточно быстро.

person Saullo G. P. Castro    schedule 22.09.2017

На самом деле вы не должны ожидать увидеть какую-либо разницу: __Pyx_CREAL(c) и __Pyx_CIMAG(c) можно найти здесь, и здесь нет ничего сложного или черной магии:

#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(), если это С++.
  2. Расширения GNU __real__ и __imag__, если это компилятор gcc или Intel (некоторое время назад Intel добавила поддержку).

Вы не можете победить его, например. в glibc creal использует __real__ . Остается компилятор Microsoft (CYTHON_CCOMPLEX не определен), для этого используется собственный/специальный класс cython:

#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

Обычно не следует писать свою собственную реализацию комплексного числа, но вы не так уж много можете сделать неправильно, если учитывать только доступ к реальной и мнимой частям. Я бы не ручался за другую функциональность, но не ожидал бы, что она будет намного медленнее, чем обычная реализация Windows (но хотелось бы увидеть результаты, если они у вас есть).

В качестве заключения: нет никакой разницы для компилятора gcc/intel, и я бы не стал слишком долго беспокоиться о различиях для других.

person ead    schedule 22.09.2017