Skip to content

Commit

Permalink
Speed up complex.pow when the exponent is 2.0 or 0.5 (nim-lang#23237)
Browse files Browse the repository at this point in the history
This PR speeds up the calculation of the power of a complex number when
the exponent is 2.0 or 0.5 (i.e the square and the square root of a
complex number). These are probably two of (if not) the most common
exponents. The speed up that is achieved according to my measurements
(using the timeit library) when the exponent is set to 2.0 or 0.5 is >
x7, while there is no measurable difference when using other exponents.

For the record, this is the function I used to mesure the performance:

```nim
import std/complex
import timeit

proc calculcatePows(v: seq[Complex], factor: Complex): seq[Complex] {.noinit, discardable.} =
  result = newSeq[Complex](v.len)
  for n in 0 ..< v.len:
    result[n] = pow(v[n], factor)

let v: seq[Complex64] = collect:
  for n in 0 ..< 1000:
    complex(float(n))

echo timeGo(calculcatePows(v, complex(1.5)))
echo timeGo(calculcatePows(v, complex(0.5)))
echo timeGo(calculcatePows(v, complex(2.0)))
```

Which with the original code got:

> [177μs 857.03ns] ± [1μs 234.85ns] per loop (mean ± std. dev. of 7
runs, 1000 loops each)
> [128μs 217.92ns] ± [1μs 630.93ns] per loop (mean ± std. dev. of 7
runs, 1000 loops each)
> [136μs 220.16ns] ± [3μs 475.56ns] per loop (mean ± std. dev. of 7
runs, 1000 loops each)

While with the improved code got:

> [176μs 884.30ns] ± [1μs 307.30ns] per loop (mean ± std. dev. of 7
runs, 1000 loops each)
> [23μs 160.79ns] ± [340.18ns] per loop (mean ± std. dev. of 7 runs,
10000 loops each)
> [19μs 93.29ns] ± [1μs 128.92ns] per loop (mean ± std. dev. of 7 runs,
10000 loops each)

That is, the new optimized path is 5.6 (23 vs 128 us per loop) to 7.16
times faster (19 vs 136 us per loop), while the non-optimized path takes
the same time as the original code.
  • Loading branch information
AngelEzquerra authored Jan 20, 2024
1 parent 7200219 commit 83f2708
Showing 1 changed file with 5 additions and 1 deletion.
6 changes: 5 additions & 1 deletion lib/pure/complex.nim
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ func abs2*[T](z: Complex[T]): T =
## that is the squared distance from (0, 0) to `z`.
## This is more efficient than `abs(z) ^ 2`.
result = z.re * z.re + z.im * z.im

func sgn*[T](z: Complex[T]): Complex[T] =
## Returns the phase of `z` as a unit complex number,
## or 0 if `z` is 0.
Expand Down Expand Up @@ -253,6 +253,10 @@ func pow*[T](x, y: Complex[T]): Complex[T] =
result = x
elif y.re == -1.0 and y.im == 0.0:
result = T(1.0) / x
elif y.re == 2.0 and y.im == 0.0:
result = x * x
elif y.re == 0.5 and y.im == 0.0:
result = sqrt(x)
else:
let
rho = abs(x)
Expand Down

0 comments on commit 83f2708

Please sign in to comment.