|
8 | 8 | "\n", |
9 | 9 | "**Scott Prahl**\n", |
10 | 10 | "\n", |
11 | | - "**Feb 2025**\n", |
| 11 | + "**Jan 2026**\n", |
12 | 12 | "\n", |
13 | | - "Unfortunately, switching between jit and non-jit during runtime is too complicated when combined with numba caching." |
| 13 | + "Switching between jit and non-jit during runtime is too complicated when combined with numba caching. Each run must be set up beforehand.\n", |
| 14 | + "\n", |
| 15 | + "The results from my computer for the first test are indicative of speedups. The jitted version that uses numba is about 30x faster than the non-jitted version and 60x faster than the same code running under JupyterLite\n", |
| 16 | + "\n", |
| 17 | + "| Array size | JIT (µs) | Non-JIT (µs) | JupyterLite (µs) |\n", |
| 18 | + "| ---------: | -------: | -----------: | ---------------: |\n", |
| 19 | + "| 1 | 1.8 | 32.3 | 46.8 |\n", |
| 20 | + "| 3 | 4.9 | 151 | 246 |\n", |
| 21 | + "| 15 | 22.2 | 790 | 1290 |\n", |
| 22 | + "| 63 | 90.9 | 3320 | 5350 |\n", |
| 23 | + "| 251 | 347 | 12800 | 21300 |\n", |
| 24 | + "| 1000 | 1360 | 49000 | 88000 |\n" |
14 | 25 | ] |
15 | 26 | }, |
16 | 27 | { |
17 | 28 | "cell_type": "code", |
18 | | - "execution_count": 4, |
| 29 | + "execution_count": 1, |
19 | 30 | "metadata": {}, |
20 | 31 | "outputs": [], |
21 | 32 | "source": [ |
22 | 33 | "import os\n", |
| 34 | + "import sys\n", |
23 | 35 | "import tempfile\n", |
24 | 36 | "import numpy as np\n", |
25 | 37 | "\n", |
26 | | - "os.environ[\"MIEPYTHON_USE_JIT\"] = \"1\" # Set to \"0\" to disable JIT\n", |
27 | | - "os.environ[\"NUMBA_CACHE_DIR\"] = tempfile.gettempdir()\n", |
| 38 | + "if sys.platform == \"emscripten\":\n", |
| 39 | + " import piplite\n", |
28 | 40 | "\n", |
29 | | - "import miepython as mie" |
| 41 | + " await piplite.install(\"miepython\")\n", |
| 42 | + " os.environ[\"MIEPYTHON_USE_JIT\"] = \"0\" # jupyterlite cannot use numba\n", |
| 43 | + "else:\n", |
| 44 | + " os.environ[\"MIEPYTHON_USE_JIT\"] = \"0\" # Set to \"0\" to disable JIT\n", |
| 45 | + " os.environ[\"NUMBA_CACHE_DIR\"] = tempfile.gettempdir()\n", |
| 46 | + " \n", |
| 47 | + "import miepython as mie\n", |
| 48 | + "\n", |
| 49 | + "def print_header():\n", |
| 50 | + " if sys.platform == \"emscripten\":\n", |
| 51 | + " print('JupyterLite results:')\n", |
| 52 | + " elif os.environ[\"MIEPYTHON_USE_JIT\"]=='0':\n", |
| 53 | + " print('Non-jitted results:')\n", |
| 54 | + " else:\n", |
| 55 | + " print('Jitted results:')" |
30 | 56 | ] |
31 | 57 | }, |
32 | 58 | { |
|
40 | 66 | }, |
41 | 67 | { |
42 | 68 | "cell_type": "code", |
43 | | - "execution_count": 6, |
| 69 | + "execution_count": 2, |
44 | 70 | "metadata": {}, |
45 | 71 | "outputs": [ |
46 | 72 | { |
47 | 73 | "name": "stdout", |
48 | 74 | "output_type": "stream", |
49 | 75 | "text": [ |
50 | | - "1.71 μs ± 23.7 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)\n", |
51 | | - "5.62 μs ± 156 ns per loop (mean ± std. dev. of 7 runs, 100,000 loops each)\n", |
52 | | - "25.4 μs ± 790 ns per loop (mean ± std. dev. of 7 runs, 10,000 loops each)\n", |
53 | | - "105 μs ± 558 ns per loop (mean ± std. dev. of 7 runs, 10,000 loops each)\n", |
54 | | - "407 μs ± 3.32 μs per loop (mean ± std. dev. of 7 runs, 1,000 loops each)\n", |
55 | | - "1.63 ms ± 46 μs per loop (mean ± std. dev. of 7 runs, 1,000 loops each)\n" |
| 76 | + "Non-jitted results:\n", |
| 77 | + "28.6 μs ± 504 ns per loop (mean ± std. dev. of 7 runs, 10,000 loops each)\n", |
| 78 | + "142 μs ± 3.04 μs per loop (mean ± std. dev. of 7 runs, 10,000 loops each)\n", |
| 79 | + "775 μs ± 16.1 μs per loop (mean ± std. dev. of 7 runs, 1,000 loops each)\n", |
| 80 | + "3.22 ms ± 67.3 μs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n", |
| 81 | + "12.5 ms ± 284 μs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n", |
| 82 | + "51.9 ms ± 1.58 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)\n" |
56 | 83 | ] |
57 | 84 | } |
58 | 85 | ], |
|
64 | 91 | "result = np.zeros(ntests)\n", |
65 | 92 | "resultj = np.zeros(ntests)\n", |
66 | 93 | "\n", |
| 94 | + "print_header()\n", |
67 | 95 | "for i in range(ntests):\n", |
68 | 96 | " x = np.linspace(0.1, 20, N[i])\n", |
69 | 97 | " a = %timeit -o qext, qsca, qback, g = mie.efficiencies_mx(m,x)\n", |
70 | | - " result[i] = a.best\n", |
71 | | - "\n", |
72 | | - "# mie.use_numba(True) # Ensure the JIT backend is used\n", |
73 | | - "# for i in range(ntests):\n", |
74 | | - "# x = np.linspace(0.1, 20, N[i])\n", |
75 | | - "# a = %timeit -o qext, qsca, qback, g = mie.efficiencies_mx(m,x)\n", |
76 | | - "# resultj[i] = a.best\n", |
77 | | - "\n", |
78 | | - "# improvement = result / resultj\n", |
79 | | - "# plt.loglog(N, resultj, \":r\")\n", |
80 | | - "# plt.loglog(N, result, \":b\")\n", |
81 | | - "# plt.loglog(N, resultj, \"or\", label=\"jit\")\n", |
82 | | - "# plt.loglog(N, result, \"ob\", label=\"no jit\")\n", |
83 | | - "# plt.legend()\n", |
84 | | - "# plt.xlabel(\"Number of sphere sizes calculated\")\n", |
85 | | - "# plt.ylabel(\"Execution Time\")\n", |
86 | | - "# plt.title(\"Jit improvement is %d to %dX\" % (np.min(improvement), np.max(improvement)))\n", |
87 | | - "# plt.show()" |
| 98 | + " result[i] = a.best" |
88 | 99 | ] |
89 | 100 | }, |
90 | 101 | { |
|
96 | 107 | }, |
97 | 108 | { |
98 | 109 | "cell_type": "code", |
99 | | - "execution_count": 8, |
| 110 | + "execution_count": 3, |
100 | 111 | "metadata": {}, |
101 | 112 | "outputs": [ |
102 | 113 | { |
103 | 114 | "name": "stdout", |
104 | 115 | "output_type": "stream", |
105 | 116 | "text": [ |
106 | | - "2.85 μs ± 111 ns per loop (mean ± std. dev. of 7 runs, 100,000 loops each)\n", |
107 | | - "5.81 μs ± 75.1 ns per loop (mean ± std. dev. of 7 runs, 100,000 loops each)\n", |
108 | | - "25.2 μs ± 270 ns per loop (mean ± std. dev. of 7 runs, 10,000 loops each)\n", |
109 | | - "103 μs ± 1.11 μs per loop (mean ± std. dev. of 7 runs, 10,000 loops each)\n", |
110 | | - "405 μs ± 1.84 μs per loop (mean ± std. dev. of 7 runs, 1,000 loops each)\n", |
111 | | - "1.64 ms ± 28.3 μs per loop (mean ± std. dev. of 7 runs, 1,000 loops each)\n" |
| 117 | + "62.7 μs ± 839 ns per loop (mean ± std. dev. of 7 runs, 10,000 loops each)\n", |
| 118 | + "167 μs ± 2.25 μs per loop (mean ± std. dev. of 7 runs, 10,000 loops each)\n", |
| 119 | + "846 μs ± 9.37 μs per loop (mean ± std. dev. of 7 runs, 1,000 loops each)\n", |
| 120 | + "3.58 ms ± 70.4 μs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n", |
| 121 | + "13.9 ms ± 164 μs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n", |
| 122 | + "55.7 ms ± 1.01 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)\n" |
112 | 123 | ] |
113 | 124 | } |
114 | 125 | ], |
|
123 | 134 | "result = np.zeros(ntests)\n", |
124 | 135 | "resultj = np.zeros(ntests)\n", |
125 | 136 | "\n", |
| 137 | + "print_header()\n", |
126 | 138 | "for i in range(ntests):\n", |
127 | 139 | " lambda0 = np.linspace(300, 800, N[i]) # also in nm\n", |
128 | 140 | " xx = 2 * np.pi * r * mwater / lambda0\n", |
129 | 141 | " a = %timeit -o qext, qsca, qback, g = mie.efficiencies_mx(mm,xx)\n", |
130 | | - " result[i] = a.best\n", |
131 | | - "\n", |
132 | | - "# mie.use_numba(True) # Ensure the JIT backend is used\n", |
133 | | - "# for i in range(ntests):\n", |
134 | | - "# lambda0 = np.linspace(300, 800, N[i]) # also in nm\n", |
135 | | - "# xx = 2 * np.pi * r * mwater / lambda0\n", |
136 | | - "# a = %timeit -o qext, qsca, qback, g = mie.efficiencies_mx(mm,xx)\n", |
137 | | - "# resultj[i] = a.best\n", |
138 | | - "\n", |
139 | | - "# improvement = result / resultj\n", |
140 | | - "# plt.loglog(N, resultj, \":r\")\n", |
141 | | - "# plt.loglog(N, result, \":b\")\n", |
142 | | - "# plt.loglog(N, resultj, \"or\", label=\"jit\")\n", |
143 | | - "# plt.loglog(N, result, \"ob\", label=\"no jit\")\n", |
144 | | - "# plt.legend()\n", |
145 | | - "# plt.xlabel(\"Number of Wavelengths Calculated\")\n", |
146 | | - "# plt.ylabel(\"Execution Time\")\n", |
147 | | - "# plt.title(\"Jit improvement is %d to %dX\" % (np.min(improvement), np.max(improvement)))\n", |
148 | | - "# plt.show()" |
| 142 | + " result[i] = a.best" |
149 | 143 | ] |
150 | 144 | }, |
151 | 145 | { |
|
159 | 153 | }, |
160 | 154 | { |
161 | 155 | "cell_type": "code", |
162 | | - "execution_count": 9, |
| 156 | + "execution_count": 4, |
163 | 157 | "metadata": {}, |
164 | 158 | "outputs": [ |
165 | 159 | { |
166 | 160 | "name": "stdout", |
167 | 161 | "output_type": "stream", |
168 | 162 | "text": [ |
169 | | - "3.61 μs ± 27.9 ns per loop (mean ± std. dev. of 7 runs, 100,000 loops each)\n", |
170 | | - "6.88 μs ± 60.8 ns per loop (mean ± std. dev. of 7 runs, 100,000 loops each)\n", |
171 | | - "26.3 μs ± 167 ns per loop (mean ± std. dev. of 7 runs, 10,000 loops each)\n", |
172 | | - "106 μs ± 2.52 μs per loop (mean ± std. dev. of 7 runs, 10,000 loops each)\n", |
173 | | - "407 μs ± 4.93 μs per loop (mean ± std. dev. of 7 runs, 1,000 loops each)\n", |
174 | | - "1.61 ms ± 14.3 μs per loop (mean ± std. dev. of 7 runs, 1,000 loops each)\n" |
| 163 | + "66.4 μs ± 470 ns per loop (mean ± std. dev. of 7 runs, 10,000 loops each)\n", |
| 164 | + "175 μs ± 1.52 μs per loop (mean ± std. dev. of 7 runs, 10,000 loops each)\n", |
| 165 | + "851 μs ± 13.1 μs per loop (mean ± std. dev. of 7 runs, 1,000 loops each)\n", |
| 166 | + "3.48 ms ± 24.1 μs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n", |
| 167 | + "13.8 ms ± 120 μs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n", |
| 168 | + "55.1 ms ± 1.3 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)\n" |
175 | 169 | ] |
176 | 170 | } |
177 | 171 | ], |
|
184 | 178 | "result = np.zeros(ntests)\n", |
185 | 179 | "resultj = np.zeros(ntests)\n", |
186 | 180 | "\n", |
| 181 | + "print_header()\n", |
187 | 182 | "for i in range(ntests):\n", |
188 | 183 | " lambda0 = np.linspace(300, 800, N[i]) # also in nm\n", |
189 | 184 | " a = %timeit -o qext, qsca, qback, g = mie.efficiencies(m_sphere, d, lambda0, n_water)\n", |
190 | | - " result[i] = a.best\n", |
191 | | - "\n", |
192 | | - "# mie.use_numba(True) # Ensure the JIT backend is used\n", |
193 | | - "# for i in range(ntests):\n", |
194 | | - "# lambda0 = np.linspace(300, 800, N[i]) # also in nm\n", |
195 | | - "# a = %timeit -o qext, qsca, qback, g = mie.efficiencies(m_sphere, d, lambda0, n_water)\n", |
196 | | - "# resultj[i] = a.best\n", |
197 | | - "\n", |
198 | | - "# improvement = result / resultj\n", |
199 | | - "# plt.loglog(N, resultj, \":r\")\n", |
200 | | - "# plt.loglog(N, result, \":b\")\n", |
201 | | - "# plt.loglog(N, resultj, \"or\", label=\"jit\")\n", |
202 | | - "# plt.loglog(N, result, \"ob\", label=\"no jit\")\n", |
203 | | - "# plt.legend()\n", |
204 | | - "# plt.xlabel(\"Number of Wavelengths Calculated\")\n", |
205 | | - "# plt.ylabel(\"Execution Time\")\n", |
206 | | - "# plt.title(\"Jit improvement is %d to %dX\" % (np.min(improvement), np.max(improvement)))\n", |
207 | | - "# plt.show()" |
| 185 | + " result[i] = a.best" |
208 | 186 | ] |
209 | 187 | }, |
210 | 188 | { |
|
216 | 194 | }, |
217 | 195 | { |
218 | 196 | "cell_type": "code", |
219 | | - "execution_count": 10, |
| 197 | + "execution_count": null, |
220 | 198 | "metadata": { |
221 | 199 | "tags": [] |
222 | 200 | }, |
223 | | - "outputs": [ |
224 | | - { |
225 | | - "name": "stdout", |
226 | | - "output_type": "stream", |
227 | | - "text": [ |
228 | | - "4.35 μs ± 100 ns per loop (mean ± std. dev. of 7 runs, 100,000 loops each)\n", |
229 | | - "4.12 μs ± 41.3 ns per loop (mean ± std. dev. of 7 runs, 100,000 loops each)\n", |
230 | | - "5.19 μs ± 64.3 ns per loop (mean ± std. dev. of 7 runs, 100,000 loops each)\n", |
231 | | - "8.71 μs ± 129 ns per loop (mean ± std. dev. of 7 runs, 100,000 loops each)\n", |
232 | | - "23.2 μs ± 264 ns per loop (mean ± std. dev. of 7 runs, 10,000 loops each)\n", |
233 | | - "81.7 μs ± 877 ns per loop (mean ± std. dev. of 7 runs, 10,000 loops each)\n" |
234 | | - ] |
235 | | - } |
236 | | - ], |
| 201 | + "outputs": [], |
237 | 202 | "source": [ |
238 | 203 | "ntests = 6\n", |
239 | 204 | "m = 1.5\n", |
|
243 | 208 | "result = np.zeros(ntests)\n", |
244 | 209 | "resultj = np.zeros(ntests)\n", |
245 | 210 | "\n", |
| 211 | + "print_header()\n", |
246 | 212 | "for i in range(ntests):\n", |
247 | 213 | " theta = np.linspace(-180, 180, N[i])\n", |
248 | 214 | " mu = np.cos(theta / 180 * np.pi)\n", |
249 | 215 | " a = %timeit -o s1, s2 = mie.S1_S2(m,x,mu)\n", |
250 | | - " result[i] = a.best\n", |
251 | | - "\n", |
252 | | - "# for i in range(ntests):\n", |
253 | | - "# theta = np.linspace(-180, 180, N[i])\n", |
254 | | - "# mu = np.cos(theta / 180 * np.pi)\n", |
255 | | - "# a = %timeit -o s1, s2 = mie.S1_S2(m,x,mu)\n", |
256 | | - "# resultj[i] = a.best\n", |
257 | | - "\n", |
258 | | - "# improvement = result / resultj\n", |
259 | | - "# plt.loglog(N, resultj, \":r\")\n", |
260 | | - "# plt.loglog(N, result, \":b\")\n", |
261 | | - "# plt.loglog(N, resultj, \"or\", label=\"jit\")\n", |
262 | | - "# plt.loglog(N, result, \"ob\", label=\"no jit\")\n", |
263 | | - "# plt.legend()\n", |
264 | | - "# plt.xlabel(\"Number of Angles Calculated\")\n", |
265 | | - "# plt.ylabel(\"Execution Time\")\n", |
266 | | - "# plt.title(\"Jit improvement is %d to %dX\" % (np.min(improvement), np.max(improvement)))\n", |
267 | | - "# plt.show()" |
| 216 | + " result[i] = a.best\n" |
268 | 217 | ] |
269 | 218 | }, |
270 | 219 | { |
|
276 | 225 | }, |
277 | 226 | { |
278 | 227 | "cell_type": "code", |
279 | | - "execution_count": 11, |
| 228 | + "execution_count": null, |
280 | 229 | "metadata": {}, |
281 | | - "outputs": [ |
282 | | - { |
283 | | - "name": "stdout", |
284 | | - "output_type": "stream", |
285 | | - "text": [ |
286 | | - "7.86 μs ± 100 ns per loop (mean ± std. dev. of 7 runs, 100,000 loops each)\n", |
287 | | - "12.9 μs ± 89.9 ns per loop (mean ± std. dev. of 7 runs, 100,000 loops each)\n", |
288 | | - "20.2 μs ± 23.5 ns per loop (mean ± std. dev. of 7 runs, 10,000 loops each)\n", |
289 | | - "42.9 μs ± 2.61 μs per loop (mean ± std. dev. of 7 runs, 10,000 loops each)\n", |
290 | | - "133 μs ± 1.75 μs per loop (mean ± std. dev. of 7 runs, 10,000 loops each)\n", |
291 | | - "504 μs ± 19 μs per loop (mean ± std. dev. of 7 runs, 1,000 loops each)\n" |
292 | | - ] |
293 | | - } |
294 | | - ], |
| 230 | + "outputs": [], |
295 | 231 | "source": [ |
296 | 232 | "ntests = 6\n", |
297 | 233 | "m = 1.5 - 0.1j\n", |
|
302 | 238 | "theta = np.linspace(-180, 180)\n", |
303 | 239 | "mu = np.cos(theta / 180 * np.pi)\n", |
304 | 240 | "\n", |
| 241 | + "print_header()\n", |
305 | 242 | "for i in range(ntests):\n", |
306 | 243 | " a = %timeit -o s1, s2 = mie.S1_S2(m,x[i],mu)\n", |
307 | | - " result[i] = a.best\n", |
308 | | - "\n", |
309 | | - "# mie.use_numba(True) # Ensure the JIT backend is used\n", |
310 | | - "# for i in range(ntests):\n", |
311 | | - "# a = %timeit -o s1, s2 = mie_S1_S2(m,x[i],mu)\n", |
312 | | - "# resultj[i] = a.best\n", |
313 | | - "\n", |
314 | | - "# improvement = result / resultj\n", |
315 | | - "# plt.loglog(N, resultj, \":r\")\n", |
316 | | - "# plt.loglog(N, result, \":b\")\n", |
317 | | - "# plt.loglog(N, resultj, \"or\", label=\"jit\")\n", |
318 | | - "# plt.loglog(N, result, \"ob\", label=\"no jit\")\n", |
319 | | - "# plt.legend()\n", |
320 | | - "# plt.xlabel(\"Sphere Size Parameter\")\n", |
321 | | - "# plt.ylabel(\"Execution Time\")\n", |
322 | | - "# plt.title(\"Jit improvement is %d to %dX\" % (np.min(improvement), np.max(improvement)))\n", |
323 | | - "# plt.show()" |
| 244 | + " result[i] = a.best\n" |
324 | 245 | ] |
325 | | - }, |
326 | | - { |
327 | | - "cell_type": "code", |
328 | | - "execution_count": null, |
329 | | - "metadata": {}, |
330 | | - "outputs": [], |
331 | | - "source": [] |
332 | 246 | } |
333 | 247 | ], |
334 | 248 | "metadata": { |
|
347 | 261 | "name": "python", |
348 | 262 | "nbconvert_exporter": "python", |
349 | 263 | "pygments_lexer": "ipython3", |
350 | | - "version": "3.11.13" |
| 264 | + "version": "3.12.12" |
351 | 265 | }, |
352 | 266 | "toc": { |
353 | 267 | "nav_menu": {}, |
|
0 commit comments