Skip to content

Commit 4da3de6

Browse files
committed
new speed tests
1 parent 66c2823 commit 4da3de6

2 files changed

Lines changed: 200 additions & 0 deletions

File tree

tests/test_field_speed.py

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
"""Benchmark near-field runtime for the single field implementation."""
2+
3+
import os
4+
from time import perf_counter
5+
6+
import numpy as np
7+
import pytest
8+
import miepython as mie
9+
from miepython import field
10+
11+
RUN_BENCH = os.environ.get("MIEPYTHON_RUN_FIELD_SPEED", "0") == "1"
12+
13+
14+
def _median_runtime(func, repeats=5):
15+
"""Return median runtime and final function output."""
16+
timings = []
17+
last = None
18+
for _ in range(repeats):
19+
t0 = perf_counter()
20+
last = func()
21+
timings.append(perf_counter() - t0)
22+
return float(np.median(timings)), last
23+
24+
25+
@pytest.mark.skipif(not RUN_BENCH, reason="Set MIEPYTHON_RUN_FIELD_SPEED=1 to run field speed benchmark")
26+
def test_field_2d_eh_near_cartesian_speed():
27+
"""Benchmark typical 2D near-field evaluation and print useful speed levers."""
28+
lambda0 = 0.6328
29+
d_sphere = 1.2
30+
m_sphere = 1.5 - 0.05j
31+
n_env = 1.33
32+
33+
# Typical 2D slice through and around the sphere.
34+
axis = np.linspace(-1.2, 1.2, 101)
35+
x_grid, z_grid = np.meshgrid(axis, axis, indexing="ij")
36+
y_grid = np.zeros_like(x_grid)
37+
38+
x = np.pi * d_sphere * n_env / lambda0
39+
m_rel = m_sphere / n_env
40+
abcd = np.array(mie.coefficients(m_rel, x, internal=True))
41+
42+
def run_cached():
43+
return field.eh_near_cartesian(
44+
lambda0,
45+
d_sphere,
46+
m_sphere,
47+
n_env,
48+
x_grid,
49+
y_grid,
50+
z_grid,
51+
True,
52+
0,
53+
abcd,
54+
)
55+
56+
def run_nocache():
57+
return field.eh_near_cartesian(
58+
lambda0,
59+
d_sphere,
60+
m_sphere,
61+
n_env,
62+
x_grid,
63+
y_grid,
64+
z_grid,
65+
True,
66+
0,
67+
None,
68+
)
69+
70+
def run_split_cached():
71+
e_xyz = field.e_near_cartesian(
72+
lambda0,
73+
d_sphere,
74+
m_sphere,
75+
n_env,
76+
x_grid,
77+
y_grid,
78+
z_grid,
79+
True,
80+
0,
81+
abcd,
82+
)
83+
h_xyz = field.h_near_cartesian(
84+
lambda0,
85+
d_sphere,
86+
m_sphere,
87+
n_env,
88+
x_grid,
89+
y_grid,
90+
z_grid,
91+
True,
92+
0,
93+
abcd,
94+
)
95+
return e_xyz, h_xyz
96+
97+
t_cached, out_cached = _median_runtime(run_cached, repeats=5)
98+
t_nocache, out_nocache = _median_runtime(run_nocache, repeats=3)
99+
t_split, out_split = _median_runtime(run_split_cached, repeats=5)
100+
101+
e_cached, h_cached = out_cached
102+
e_nocache, h_nocache = out_nocache
103+
e_split, h_split = out_split
104+
105+
np.testing.assert_allclose(e_cached, e_nocache, rtol=1e-12, atol=1e-12)
106+
np.testing.assert_allclose(h_cached, h_nocache, rtol=1e-12, atol=1e-12)
107+
np.testing.assert_allclose(e_cached, e_split, rtol=1e-12, atol=1e-12)
108+
np.testing.assert_allclose(h_cached, h_split, rtol=1e-12, atol=1e-12)
109+
110+
print(f"eh_near_cartesian cached abcd median: {t_cached:.4f} s")
111+
print(f"eh_near_cartesian internal abcd median: {t_nocache:.4f} s")
112+
print(f"e_near_cartesian + h_near_cartesian median: {t_split:.4f} s")
113+
print(f"coeff-cache speedup: {t_nocache / t_cached:.2f}x")
114+
print(f"combined-vs-split speedup: {t_split / t_cached:.2f}x")
115+
116+
assert t_cached > 0.0
117+
assert t_nocache > 0.0
118+
assert t_split > 0.0

tests/test_mie_backend_speed.py

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
"""Benchmark and compare mie_nojit and mie_jit backend performance."""
2+
3+
import os
4+
from time import perf_counter
5+
6+
import numpy as np
7+
import pytest
8+
9+
from miepython import mie_jit, mie_nojit
10+
11+
RUN_BENCH = os.environ.get("MIEPYTHON_RUN_MIE_SPEED", "0") == "1"
12+
13+
14+
def _median_runtime(func, repeats=3):
15+
"""Return median runtime and final function output."""
16+
timings = []
17+
last = None
18+
for _ in range(repeats):
19+
t0 = perf_counter()
20+
last = func()
21+
timings.append(perf_counter() - t0)
22+
return float(np.median(timings)), last
23+
24+
25+
@pytest.mark.skipif(not RUN_BENCH, reason="Set MIEPYTHON_RUN_MIE_SPEED=1 to run mie backend speed benchmark")
26+
def test_mie_backend_speed_compare():
27+
"""Compare runtime and output agreement for no-JIT and JIT Mie backends."""
28+
rng = np.random.default_rng(12345)
29+
30+
n_particles = 4000
31+
refr = rng.uniform(1.2, 2.0, n_particles)
32+
refi = np.exp(rng.uniform(np.log(1e-4), np.log(5e-1), n_particles))
33+
xvals = np.exp(rng.uniform(np.log(5e-2), np.log(50), n_particles))
34+
mvals = refr - 1j * refi
35+
36+
mu = np.linspace(-1.0, 1.0, 361)
37+
m_ref = 1.5 - 0.05j
38+
x_ref = 12.0
39+
40+
# Warm up numba kernels so benchmark times reflect steady-state runtime.
41+
mie_jit._single_sphere_nb(np.complex128(mvals[0]), float(xvals[0]), 0, True)
42+
mie_jit._S1_S2_nb(np.complex128(m_ref), float(x_ref), mu, 0)
43+
44+
def run_single_nojit():
45+
out = np.empty((4, n_particles), dtype=np.float64)
46+
for i in range(n_particles):
47+
out[:, i] = mie_nojit._single_sphere_py(mvals[i], float(xvals[i]), 0, True)
48+
return out
49+
50+
def run_single_jit():
51+
out = np.empty((4, n_particles), dtype=np.float64)
52+
for i in range(n_particles):
53+
out[:, i] = mie_jit._single_sphere_nb(np.complex128(mvals[i]), float(xvals[i]), 0, True)
54+
return out
55+
56+
def run_s12_nojit():
57+
return mie_nojit._S1_S2_py(m_ref, x_ref, mu, 0)
58+
59+
def run_s12_jit():
60+
return mie_jit._S1_S2_nb(np.complex128(m_ref), float(x_ref), mu, 0)
61+
62+
t_single_nojit, single_nojit = _median_runtime(run_single_nojit, repeats=3)
63+
t_single_jit, single_jit = _median_runtime(run_single_jit, repeats=3)
64+
65+
t_s12_nojit, s12_nojit = _median_runtime(run_s12_nojit, repeats=5)
66+
t_s12_jit, s12_jit = _median_runtime(run_s12_jit, repeats=5)
67+
68+
np.testing.assert_allclose(single_nojit, single_jit, rtol=1e-10, atol=1e-12)
69+
np.testing.assert_allclose(s12_nojit[0], s12_jit[0], rtol=1e-10, atol=1e-12)
70+
np.testing.assert_allclose(s12_nojit[1], s12_jit[1], rtol=1e-10, atol=1e-12)
71+
72+
print(f"single_sphere nojit median: {t_single_nojit:.4f} s")
73+
print(f"single_sphere jit median: {t_single_jit:.4f} s")
74+
print(f"single_sphere speedup: {t_single_nojit / t_single_jit:.2f}x")
75+
print(f"S1_S2 nojit median: {t_s12_nojit:.4f} s")
76+
print(f"S1_S2 jit median: {t_s12_jit:.4f} s")
77+
print(f"S1_S2 speedup: {t_s12_nojit / t_s12_jit:.2f}x")
78+
79+
assert t_single_nojit > 0.0
80+
assert t_single_jit > 0.0
81+
assert t_s12_nojit > 0.0
82+
assert t_s12_jit > 0.0

0 commit comments

Comments
 (0)