Conjugating a complex number much faster if number has python-native complex type
Asked Answered
G

1

3

Conjugating a complex number appears to be about 30 times faster if the type() of the complex number is complex rather than numpy.complex128, see the minimal example below. However, the absolute value takes about the same time. Taking the real and the imaginary part is only about 3 times faster.

Why is the conjugate slower by that much? When I take a from a large complex-valued array, it seems I should cast it to complex first (the complex conjugation is part of a larger code which has many (> 10^6) iterations).

import numpy as np

np.random.seed(100)

a = (np.random.rand(1) + 1j*np.random.rand(1))[0]
b = complex(a)

%timeit a.conjugate() # 2.95 µs ± 24 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
%timeit a.conj()      # 2.86 µs ± 14.2 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
%timeit b.conjugate() # 82.8 ns ± 1.28 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
%timeit abs(a)        # 112 ns ± 1.7 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
%timeit abs(b)        # 99.6 ns ± 0.623 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
%timeit a.real        # 145 ns ± 0.259 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
%timeit b.real        # 54.8 ns ± 0.121 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
%timeit a.imag        # 144 ns ± 0.771 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
%timeit b.imag        # 55.4 ns ± 0.297 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
Gish answered 20/7, 2022 at 14:38 Comment(0)
D
0

Calling NumPy routines always comes at a fixed cost, which in this case is more expensive than cost of the Python-native routine.

As soon as you start processing more than one number (possibly millions) at once NumPy will be much faster:

import numpy as np

N = 10
a = np.random.rand(N) + 1j*np.random.rand(N)
b = [complex(x) for x in a]

%timeit a.conjugate()              # 481 ns ± 1.39 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)
%timeit [x.conjugate() for x in b] # 605 ns ± 6.11 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)
Daggerboard answered 20/7, 2022 at 15:7 Comment(7)
I understand. However, I am surprised that the ratio is that large for conjugate(), but significantly smaller for abs(), a.real or a.imag. As mentioned in my question, I am reading a complex number from a large array and need to conjugate it. However, the large array changes between iterations of my code, so I cannot pre-compute the conjugates at once. Then it appears that casting to complex (about ~190 ns on my machine), followed by conjugation (~80 ns) is faster by a factor of more than 10 compared to direct conjugation (~2.8 mus).Gish
Maybe you can slice your problem in a different way that you can still process more than one number at once to speed up your algorithm. If you really can't then it seems doing it this way will be the fastest.Daggerboard
I get similar time ratios with a.real-a.imag*1jWatermark
@NilsWerner: I don't think I can. The code this part is used in is a nonlinear iterative algorithm, where in each iteration the array changes in an a priori unknown manner, so I need to read out the respective single element in each iteration separately.Gish
@hpaulj: In my case according to the timings, it seems that would take 2 x 145 = 290 ns, plus the time for the subtraction, so > 300 ns...Gish
Most of the time is spent in various layers of function calls, not in actual calculations (flops). Without reading the C code it is hard to reason our way around this. Even timings leave us guessing. numpy is meant for array operations. In my tests conj of a (600,) shape array is faster than one np.complex128. In your test, the [0] step that unboxes a array scalar from a (1,) shape array actually slows things down.Watermark
But if I am not mistaken, the unboxing is already done (and the result saved into a) so that does not influence the conjugate(), does it?Gish

© 2022 - 2025 — McMap. All rights reserved.