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: README.md
+23-16Lines changed: 23 additions & 16 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -3,9 +3,9 @@
3
3
Make multi-threaded concurrency backward- and forward-compatible for the free-threaded future of Python.
4
4
5
5
6
-
###Multi-Threading In and Out of Free-Threading
6
+
## Multi-Threading In and Out of Free-Threading
7
7
8
-
The following is a table of performance results for the execution of a function across each row of NumPy array, with `python3.14t` and `python3.14`, and with and without using a`ThreadPoolExecutor`. Performance improves with `python3.14t` but degrades with `python3.14`.
8
+
The following is a table of performance results for the execution of a function across each row of a NumPy array ([code](#example)), with (no [GIL](https://docs.python.org/3/glossary.html#term-global-interpreter-lock)) `python3.14t` and (GIL enabled) `python3.14`, and with and without `ThreadPoolExecutor`. Performance improves with `python3.14t` but degrades with `python3.14`.
The new free-threaded version of Python (with the [GIL](https://docs.python.org/3/glossary.html#term-global-interpreter-lock) disabled) offers extraordinary improvement in performance of CPU-bound processes. Upgrading your code to take advantage of this performance, however, is problematic. The same multi-threaded code, if run with the GIL enabled, can actually perform significantly worse than single-threaded execution. Worse, even when using a free-threaded interpreter, importing an incompatible C-extension will automatically re-enable the GIL.
29
+
The new free-threaded version of Python (with the GIL disabled) offers extraordinary performance improvements in multi-threading CPU-bound processes. Upgrading your code to take advantage of this performance, however, is problematic. The same multi-threaded code, if run with the GIL enabled, can actually perform significantly worse than single-threaded execution. Even when using a free-threaded interpreter, importing an incompatible C-extension will automatically re-enable the GIL.
30
30
31
-
For code that will run in multiple interpreters, we need interfaces that perform multi-threaded processing only when the GIL is disabled.
31
+
For code that will run across many interpreters with or without the GIL, we need interfaces that perform multi-threaded processing only when the GIL is disabled.
32
32
33
33
The `conditional-futures` package provides `ConditionalThreadPoolExecutor`, a drop-in replacement for `ThreadPoolExecutor` that adapts based on the runtime state of the GIL.
34
34
35
-
When running under free-threaded Python with the GIL disabled `ConditionalThreadPoolExecutor` behaves like a normal thread pool. When running under a GIL-enabled build, it falls back on single-threaded execution, potentially avoiding a significant degradation in performance. The same implementation thus offers optimal performance in all contexts.
35
+
When running under free-threaded Python with the GIL disabled `ConditionalThreadPoolExecutor` behaves like a normal thread pool. When running under a GIL-enabled build, it falls back on single-threaded execution, potentially avoiding a significant degradation in performance. The same implementation offers optimal performance in all contexts.
36
36
37
37
Note that, even with the GIL enabled, multi-threading can perform well for I/O-bound processes. `ConditionalThreadPoolExecutor` is appropriate only for CPU-bound processes that perform worse with the GIL.
38
38
39
39
40
-
###Example
40
+
## Example
41
41
42
-
Function application on the rows of a 2D NumPy array can prove the benefits of both free-threaded Python and the need for `ConditionalThreadPoolExecutor`.
42
+
The performance of function application on the rows of a 2D NumPy array can be used to show both the benefits of free-threaded Python and the need for `ConditionalThreadPoolExecutor`.
43
43
44
44
First, using the free-threaded build of Python 3.14, we can create an array and apply a function to each row of that array. The `ipython``%time` utility is used to measure duration.
>>>%time _ = np.fromiter((func(row) for row in array), dtype=float, count=array.shape[0])
@@ -56,19 +55,17 @@ Wall time: 581 ms
56
55
Using `ConditionalThreadPoolExecutor` with this GIL-disabled build of Python we can take advantage of multi-threaded performance on a CPU-bound process: the same routine is almost twice as fast:
Now, if using the standard Python 3.14 interpreter (with the GIL enabled), we can see detrimental performance using when using the standard `ThreadPoolExecutor`: the same operation takes six times as long!
65
+
Now, if using the standard Python 3.14 interpreter (with the GIL enabled), `ThreadPoolExecutor` degrades performance: the same operation takes six times as long!
@@ -78,11 +75,10 @@ CPU times: user 1.9 s, sys: 2.21 s, total: 4.12 s
78
75
Wall time: 2.33 s
79
76
```
80
77
81
-
Using `ConditionalThreadPoolExecutor` we can have one implementation that performs optimally in both contexts. Running the same code with the GIL enabled, `ConditionalThreadPoolExecutor` does not perform as well as in`python3.14t` but provides the best option available, single-threaded performance.
78
+
Using `ConditionalThreadPoolExecutor`, one implementation performs optimally in both contexts. Running the same code with `python3.14`, `ConditionalThreadPoolExecutor` does not perform as well as with`python3.14t`, but provides the best option available: single-threaded performance.
0 commit comments