You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: doc/guides/optimization_tips.md
+8-12Lines changed: 8 additions & 12 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -130,27 +130,26 @@ The benchmark times four variants — the Python loop above, a vectorized NumPy
130
130
131
131

132
132
133
-
The default `@blosc2.jit` is **~155x faster than the Python loop** and **~2.3x faster than the best plain-NumPy version** (with `jit_backend="cc"`, ~317x and ~4.7x), and it is simply the loop you would have written anyway. The NumPy version, by contrast, costs ~15 lines of `alive`/`escaped` mask bookkeeping plus an overflow trap for the pixels that already escaped.
133
+
The default `@blosc2.jit` is **~160x faster than the Python loop** and **~2.3x faster than the best plain-NumPy version** (with `jit_backend="cc"`, ~320x and ~4.7x).
134
134
135
135
Operands may equally be on-disk {class}`~blosc2.NDArray` objects: the same kernel over `blosc2.asarray()` views of the two grids runs at the same speed and returns a plain NumPy array.
136
136
137
137
#### Gotcha: silent fallback to tracing
138
138
139
-
The DSL grammar is narrower than Python, and a body that misses it falls back to tracing *silently* — you only find out when the call fails. Two rules bite in this example:
140
-
141
-
-**Simple assignments only.** The tuple assignment `zr, zi = ...` of the Python reference is not valid DSL; that is why the jit version uses a `zr2` temporary.
142
-
-**No docstring in the kernel body.**
139
+
The DSL grammar is narrower than Python, and a body that misses it falls back to tracing *silently* — you only find out when the call fails. For example, the tuple assignment `zr, zi = ...` of the Python reference is not valid DSL program; that is why the jit version uses a `zr2` temporary.
143
140
144
141
The [DSL syntax reference](../reference/dsl_syntax.md) has the full grammar. To turn that silent fallback into a hard error, decorate with `@blosc2.jit(strict=True)`: it forces the DSL route and raises `DSLSyntaxError` at *decoration* time if the function cannot be compiled.
145
142
146
143
The opposite knob, `strict=False`, forces tracing even when there is control flow. On this function it fails loudly — tracing evaluates `if zr * zr + zi * zi > 4.0` on an array, which raises `ValueError`. The dangerous case is a branch on a plain Python value instead (say `if max_iter > 100`): tracing records only the path that one call happened to take, and that path is then reused for every element, quietly. So use `strict=False` only when the branches depend on Python values, never on the arrays.
147
144
148
145
### Element-wise functions still trace by default
149
146
150
-
If compiling the whole function is this good, why doesn't `jit` do it for everything? Because without control flow, tracing wins: the traced expression is evaluated as one vectorized miniexpr over whole chunks, while a compiled kernel has to loop element by element. Here is a heavy elementwise mix of transcendental functions:
147
+
If compiling the whole function is this good, why doesn't `jit` do it for everything? Because without control flow, tracing usually wins: the traced expression is evaluated as one vectorized miniexpr over whole chunks, while a compiled kernel has to loop element by element.
148
+
149
+
Here is a heavy elementwise mix of transcendental functions:
151
150
152
151
```python
153
-
@blosc2.jit
152
+
@blosc2.jit# using jit(strict=True) forces the DSL route
154
153
defheavy(x):
155
154
return (
156
155
np.sin(x)
@@ -159,9 +158,6 @@ def heavy(x):
159
158
+ np.sqrt(np.abs(x))
160
159
+ np.log1p(np.abs(x))
161
160
)
162
-
163
-
164
-
# The same body decorated with @blosc2.jit(strict=True) forces the DSL route.
165
161
```
166
162
167
163
Measured over 8M float32 values:
@@ -172,7 +168,7 @@ The first bar is the same expression in plain NumPy, as a scale anchor: all thre
172
168
173
169
Among the three, the default `@blosc2.jit` (which traces) is the faster route here: forcing the DSL route with `strict=True` is **~1.37x slower** with the bundled tcc, and gets close to tracing when compiled with `jit_backend="cc"` (still ~1.07x slower; the plot shows the exact times). Note that `jit_backend="cc"` alone does *not* switch an elementwise function to the compiled route; it keeps tracing, at the same speed.
174
170
175
-
So, use `strict=True` when you want the compiled-kernel guarantee, even if sometimes it may cost you speed.
171
+
So, use `strict=True` when you want the compiled-kernel guarantee; but be aware that usually it may cost you speed.
176
172
177
173
### Pros and cons of forcing the system compiler
178
174
@@ -185,7 +181,7 @@ The price is the one-time compile. The benchmark measures it as the first call m
185
181
| mandelbrot | 3.2 ms | 233 ms | 1.3 ms |
186
182
| elementwise | 4.0 ms | 253 ms | 2.9 ms |
187
183
188
-
[tcc](https://bellard.org/tcc/) compiles in memory, so every process pays those few milliseconds again. `cc` writes a shared object into `$TMPDIR/miniexpr-jit`, keyed by a fingerprint of the kernel, its dtypes and the toolchain: only the *first* process on a machine pays the compiler, and later ones just load the cached artifact — cheaper even than tcc's in-memory compile. And that cold compile is repaid by the faster steady state after ~54 calls of the mandelbrot kernel (~42 of the elementwise one).
184
+
[tcc](https://bellard.org/tcc/) compiles in memory, so every process pays those few milliseconds again. `cc` writes a shared object into `$TMPDIR/miniexpr-jit`, keyed by a fingerprint of the kernel, its dtypes and the toolchain: only the *first* process on a machine pays the compiler.
189
185
190
186
So `cc` pays off for kernels you call repeatedly in the same run (or across runs), or when run time is much larger than compile time. It also requires a C compiler and a writable cache directory.
0 commit comments