numexpr 2.10.1__tar.gz → 2.11.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. {numexpr-2.10.1 → numexpr-2.11.0}/ANNOUNCE.rst +17 -13
  2. {numexpr-2.10.1 → numexpr-2.11.0}/AUTHORS.txt +1 -1
  3. numexpr-2.11.0/PKG-INFO +233 -0
  4. {numexpr-2.10.1 → numexpr-2.11.0}/README.rst +18 -0
  5. {numexpr-2.10.1 → numexpr-2.11.0}/RELEASE_NOTES.rst +35 -6
  6. numexpr-2.11.0/VERSION +1 -0
  7. {numexpr-2.10.1 → numexpr-2.11.0}/bench/boolean_timing.py +2 -0
  8. numexpr-2.11.0/bench/free_threading.py +171 -0
  9. {numexpr-2.10.1 → numexpr-2.11.0}/bench/issue-36.py +6 -3
  10. {numexpr-2.10.1 → numexpr-2.11.0}/bench/issue-47.py +1 -0
  11. numexpr-2.11.0/bench/large_array_vs_numpy.py +154 -0
  12. {numexpr-2.10.1 → numexpr-2.11.0}/bench/multidim.py +4 -3
  13. {numexpr-2.10.1 → numexpr-2.11.0}/bench/poly.py +3 -1
  14. {numexpr-2.10.1 → numexpr-2.11.0}/bench/timing.py +4 -1
  15. {numexpr-2.10.1 → numexpr-2.11.0}/bench/unaligned-simple.py +3 -0
  16. {numexpr-2.10.1 → numexpr-2.11.0}/bench/varying-expr.py +3 -0
  17. {numexpr-2.10.1 → numexpr-2.11.0}/bench/vml_timing.py +3 -0
  18. {numexpr-2.10.1 → numexpr-2.11.0}/bench/vml_timing2.py +4 -1
  19. {numexpr-2.10.1 → numexpr-2.11.0}/bench/vml_timing3.py +3 -1
  20. {numexpr-2.10.1 → numexpr-2.11.0}/numexpr/__init__.py +10 -10
  21. {numexpr-2.10.1 → numexpr-2.11.0}/numexpr/cpuinfo.py +5 -3
  22. {numexpr-2.10.1 → numexpr-2.11.0}/numexpr/expressions.py +3 -2
  23. {numexpr-2.10.1 → numexpr-2.11.0}/numexpr/interp_body.cpp +4 -4
  24. {numexpr-2.10.1 → numexpr-2.11.0}/numexpr/interpreter.cpp +10 -8
  25. {numexpr-2.10.1 → numexpr-2.11.0}/numexpr/interpreter.hpp +1 -1
  26. {numexpr-2.10.1 → numexpr-2.11.0}/numexpr/module.cpp +8 -2
  27. {numexpr-2.10.1 → numexpr-2.11.0}/numexpr/module.hpp +2 -2
  28. {numexpr-2.10.1 → numexpr-2.11.0}/numexpr/necompiler.py +78 -76
  29. {numexpr-2.10.1 → numexpr-2.11.0}/numexpr/numexpr_config.hpp +1 -1
  30. {numexpr-2.10.1 → numexpr-2.11.0}/numexpr/numexpr_object.cpp +0 -1
  31. {numexpr-2.10.1 → numexpr-2.11.0}/numexpr/tests/__init__.py +1 -1
  32. numexpr-2.11.0/numexpr/tests/conftest.py +16 -0
  33. {numexpr-2.10.1 → numexpr-2.11.0}/numexpr/tests/test_numexpr.py +229 -128
  34. {numexpr-2.10.1 → numexpr-2.11.0}/numexpr/utils.py +93 -10
  35. numexpr-2.11.0/numexpr/version.py +5 -0
  36. {numexpr-2.10.1 → numexpr-2.11.0}/numexpr/win32/stdint.h +6 -6
  37. numexpr-2.11.0/numexpr.egg-info/PKG-INFO +233 -0
  38. {numexpr-2.10.1 → numexpr-2.11.0}/numexpr.egg-info/SOURCES.txt +3 -0
  39. {numexpr-2.10.1 → numexpr-2.11.0}/pyproject.toml +4 -3
  40. {numexpr-2.10.1 → numexpr-2.11.0}/setup.py +12 -5
  41. numexpr-2.10.1/PKG-INFO +0 -27
  42. numexpr-2.10.1/VERSION +0 -1
  43. numexpr-2.10.1/numexpr/version.py +0 -5
  44. numexpr-2.10.1/numexpr.egg-info/PKG-INFO +0 -27
  45. {numexpr-2.10.1 → numexpr-2.11.0}/LICENSE.txt +0 -0
  46. {numexpr-2.10.1 → numexpr-2.11.0}/MANIFEST.in +0 -0
  47. {numexpr-2.10.1 → numexpr-2.11.0}/numexpr/complex_functions.hpp +0 -0
  48. {numexpr-2.10.1 → numexpr-2.11.0}/numexpr/functions.hpp +0 -0
  49. {numexpr-2.10.1 → numexpr-2.11.0}/numexpr/missing_posix_functions.hpp +0 -0
  50. {numexpr-2.10.1 → numexpr-2.11.0}/numexpr/msvc_function_stubs.hpp +0 -0
  51. {numexpr-2.10.1 → numexpr-2.11.0}/numexpr/numexpr_object.hpp +0 -0
  52. {numexpr-2.10.1 → numexpr-2.11.0}/numexpr/opcodes.hpp +0 -0
  53. {numexpr-2.10.1 → numexpr-2.11.0}/numexpr/str-two-way.hpp +0 -0
  54. {numexpr-2.10.1 → numexpr-2.11.0}/numexpr/win32/pthread.c +0 -0
  55. {numexpr-2.10.1 → numexpr-2.11.0}/numexpr/win32/pthread.h +0 -0
  56. {numexpr-2.10.1 → numexpr-2.11.0}/numexpr.egg-info/dependency_links.txt +0 -0
  57. {numexpr-2.10.1 → numexpr-2.11.0}/numexpr.egg-info/requires.txt +0 -0
  58. {numexpr-2.10.1 → numexpr-2.11.0}/numexpr.egg-info/top_level.txt +0 -0
  59. {numexpr-2.10.1 → numexpr-2.11.0}/requirements.txt +0 -0
  60. {numexpr-2.10.1 → numexpr-2.11.0}/setup.cfg +0 -0
  61. {numexpr-2.10.1 → numexpr-2.11.0}/site.cfg.example +0 -0
@@ -1,29 +1,33 @@
1
1
  =========================
2
- Announcing NumExpr 2.10.1
2
+ Announcing NumExpr 2.11.0
3
3
  =========================
4
4
 
5
5
  Hi everyone,
6
6
 
7
- NumExpr 2.10.1 continues to establize the support for NumPy 2.0.0.
8
- Also, the default number of 'safe' threads has been upgraded to 16
9
- (instead of previous 8). Finally, preliminary support for Python 3.13;
10
- thanks to Karolina Surma.
7
+ NumExpr 2.11.0 Initial support for free-threaded Python 3.13t has been added.
8
+ This is still experimental, so please report any issues you find.
9
+ Finally, Python 3.10 is now the minimum supported version.
11
10
 
12
11
  Project documentation is available at:
13
12
 
14
13
  http://numexpr.readthedocs.io/
15
14
 
16
- Changes from 2.9.0 to 2.10.0
17
- ----------------------------
15
+ Changes from 2.10.2 to 2.11.0
16
+ -----------------------------
18
17
 
19
- * Support for NumPy 2.0.0. This is still experimental, so please
20
- report any issues you find. Thanks to Clément Robert and Thomas
21
- Caswell for the work.
18
+ * Initial support for free-threaded Python 3.13t has been added.
19
+ This is still experimental, so please report any issues you find.
20
+ For more info, see discussions PRs #504, #505 and #508.
21
+ Thanks to @andfoy, @rgommers and @FrancescAlted for the work.
22
22
 
23
- * Avoid erroring when OMP_NUM_THREADS is empty string. Thanks to
24
- Patrick Hoefler.
23
+ * Fix imaginary evaluation in the form of `1.1e1j`. This was
24
+ previously not supported and would raise an error. Thanks to @27rabbitlt
25
+ for the fix.
25
26
 
26
- * Do not warn if OMP_NUM_THREAD set.
27
+ * The test suite has been modernized to use `pytest` instead of `unittest`.
28
+ This should make it easier to run the tests and contribute to the project.
29
+
30
+ * Python 3.10 is now the minimum supported version.
27
31
 
28
32
  What's Numexpr?
29
33
  ---------------
@@ -23,7 +23,7 @@ Google Inc. contributed bug fixes.
23
23
 
24
24
  David Cox improved readability of the Readme.
25
25
 
26
- Robert A. McLeod contributed bug fixes and ported the documentation to
26
+ Robert A. McLeod contributed bug fixes and ported the documentation to
27
27
  numexpr.readthedocs.io. He has served as the maintainer of the package
28
28
  since 2016 to 2023.
29
29
 
@@ -0,0 +1,233 @@
1
+ Metadata-Version: 2.4
2
+ Name: numexpr
3
+ Version: 2.11.0
4
+ Summary: Fast numerical expression evaluator for NumPy
5
+ Author-email: "David M. Cooke, Francesc Alted, and others" <blosc@blosc.org>
6
+ Maintainer-email: Blosc Development Team <blosc@blosc.org>
7
+ License-Expression: MIT
8
+ Project-URL: homepage, https://github.com/pydata/numexpr
9
+ Project-URL: documentation, https://numexpr.readthedocs.io
10
+ Project-URL: repository, https://github.com/pydata/numexpr
11
+ Classifier: Development Status :: 6 - Mature
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Intended Audience :: Information Technology
14
+ Classifier: Intended Audience :: Science/Research
15
+ Classifier: Programming Language :: Python
16
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
17
+ Classifier: Operating System :: Microsoft :: Windows
18
+ Classifier: Operating System :: Unix
19
+ Classifier: Programming Language :: Python :: 3
20
+ Classifier: Programming Language :: Python :: 3.10
21
+ Classifier: Programming Language :: Python :: 3.11
22
+ Classifier: Programming Language :: Python :: 3.12
23
+ Classifier: Programming Language :: Python :: 3.13
24
+ Requires-Python: >=3.10
25
+ Description-Content-Type: text/x-rst
26
+ License-File: LICENSE.txt
27
+ License-File: AUTHORS.txt
28
+ Requires-Dist: numpy>=1.23.0
29
+ Dynamic: license-file
30
+
31
+ ======================================================
32
+ NumExpr: Fast numerical expression evaluator for NumPy
33
+ ======================================================
34
+
35
+ :Author: David M. Cooke, Francesc Alted, and others.
36
+ :Maintainer: Francesc Alted
37
+ :Contact: faltet@gmail.com
38
+ :URL: https://github.com/pydata/numexpr
39
+ :Documentation: http://numexpr.readthedocs.io/en/latest/
40
+ :GitHub Actions: |actions|
41
+ :PyPi: |version|
42
+ :DOI: |doi|
43
+ :readthedocs: |docs|
44
+
45
+ .. |actions| image:: https://github.com/pydata/numexpr/workflows/Build/badge.svg
46
+ :target: https://github.com/pydata/numexpr/actions
47
+ .. |travis| image:: https://travis-ci.org/pydata/numexpr.png?branch=master
48
+ :target: https://travis-ci.org/pydata/numexpr
49
+ .. |docs| image:: https://readthedocs.org/projects/numexpr/badge/?version=latest
50
+ :target: http://numexpr.readthedocs.io/en/latest
51
+ .. |doi| image:: https://zenodo.org/badge/doi/10.5281/zenodo.2483274.svg
52
+ :target: https://doi.org/10.5281/zenodo.2483274
53
+ .. |version| image:: https://img.shields.io/pypi/v/numexpr
54
+ :target: https://pypi.python.org/pypi/numexpr
55
+
56
+
57
+ What is NumExpr?
58
+ ----------------
59
+
60
+ NumExpr is a fast numerical expression evaluator for NumPy. With it,
61
+ expressions that operate on arrays (like :code:`'3*a+4*b'`) are accelerated
62
+ and use less memory than doing the same calculation in Python.
63
+
64
+ In addition, its multi-threaded capabilities can make use of all your
65
+ cores -- which generally results in substantial performance scaling compared
66
+ to NumPy.
67
+
68
+ Last but not least, numexpr can make use of Intel's VML (Vector Math
69
+ Library, normally integrated in its Math Kernel Library, or MKL).
70
+ This allows further acceleration of transcendent expressions.
71
+
72
+
73
+ How NumExpr achieves high performance
74
+ -------------------------------------
75
+
76
+ The main reason why NumExpr achieves better performance than NumPy is
77
+ that it avoids allocating memory for intermediate results. This
78
+ results in better cache utilization and reduces memory access in
79
+ general. Due to this, NumExpr works best with large arrays.
80
+
81
+ NumExpr parses expressions into its own op-codes that are then used by
82
+ an integrated computing virtual machine. The array operands are split
83
+ into small chunks that easily fit in the cache of the CPU and passed
84
+ to the virtual machine. The virtual machine then applies the
85
+ operations on each chunk. It's worth noting that all temporaries and
86
+ constants in the expression are also chunked. Chunks are distributed among
87
+ the available cores of the CPU, resulting in highly parallelized code
88
+ execution.
89
+
90
+ The result is that NumExpr can get the most of your machine computing
91
+ capabilities for array-wise computations. Common speed-ups with regard
92
+ to NumPy are usually between 0.95x (for very simple expressions like
93
+ :code:`'a + 1'`) and 4x (for relatively complex ones like :code:`'a*b-4.1*a > 2.5*b'`),
94
+ although much higher speed-ups can be achieved for some functions and complex
95
+ math operations (up to 15x in some cases).
96
+
97
+ NumExpr performs best on matrices that are too large to fit in L1 CPU cache.
98
+ In order to get a better idea on the different speed-ups that can be achieved
99
+ on your platform, run the provided benchmarks.
100
+
101
+ Installation
102
+ ------------
103
+
104
+ From wheels
105
+ ^^^^^^^^^^^
106
+
107
+ NumExpr is available for install via `pip` for a wide range of platforms and
108
+ Python versions (which may be browsed at: https://pypi.org/project/numexpr/#files).
109
+ Installation can be performed as::
110
+
111
+ pip install numexpr
112
+
113
+ If you are using the Anaconda or Miniconda distribution of Python you may prefer
114
+ to use the `conda` package manager in this case::
115
+
116
+ conda install numexpr
117
+
118
+ From Source
119
+ ^^^^^^^^^^^
120
+
121
+ On most \*nix systems your compilers will already be present. However if you
122
+ are using a virtual environment with a substantially newer version of Python than
123
+ your system Python you may be prompted to install a new version of `gcc` or `clang`.
124
+
125
+ For Windows, you will need to install the Microsoft Visual C++ Build Tools
126
+ (which are free) first. The version depends on which version of Python you have
127
+ installed:
128
+
129
+ https://wiki.python.org/moin/WindowsCompilers
130
+
131
+ For Python 3.6+ simply installing the latest version of MSVC build tools should
132
+ be sufficient. Note that wheels found via pip do not include MKL support. Wheels
133
+ available via `conda` will have MKL, if the MKL backend is used for NumPy.
134
+
135
+ See `requirements.txt` for the required version of NumPy.
136
+
137
+ NumExpr is built in the standard Python way::
138
+
139
+ python setup.py build install
140
+
141
+ You can test `numexpr` with::
142
+
143
+ python -c "import numexpr; numexpr.test()"
144
+
145
+ Do not test NumExpr in the source directory or you will generate import errors.
146
+
147
+ Enable Intel® MKL support
148
+ ^^^^^^^^^^^^^^^^^^^^^^^^^
149
+
150
+ NumExpr includes support for Intel's MKL library. This may provide better
151
+ performance on Intel architectures, mainly when evaluating transcendental
152
+ functions (trigonometrical, exponential, ...).
153
+
154
+ If you have Intel's MKL, copy the `site.cfg.example` that comes with the
155
+ distribution to `site.cfg` and edit the latter file to provide correct paths to
156
+ the MKL libraries in your system. After doing this, you can proceed with the
157
+ usual building instructions listed above.
158
+
159
+ Pay attention to the messages during the building process in order to know
160
+ whether MKL has been detected or not. Finally, you can check the speed-ups on
161
+ your machine by running the `bench/vml_timing.py` script (you can play with
162
+ different parameters to the `set_vml_accuracy_mode()` and `set_vml_num_threads()`
163
+ functions in the script so as to see how it would affect performance).
164
+
165
+ Usage
166
+ -----
167
+
168
+ ::
169
+
170
+ >>> import numpy as np
171
+ >>> import numexpr as ne
172
+
173
+ >>> a = np.arange(1e6) # Choose large arrays for better speedups
174
+ >>> b = np.arange(1e6)
175
+
176
+ >>> ne.evaluate("a + 1") # a simple expression
177
+ array([ 1.00000000e+00, 2.00000000e+00, 3.00000000e+00, ...,
178
+ 9.99998000e+05, 9.99999000e+05, 1.00000000e+06])
179
+
180
+ >>> ne.evaluate("a * b - 4.1 * a > 2.5 * b") # a more complex one
181
+ array([False, False, False, ..., True, True, True], dtype=bool)
182
+
183
+ >>> ne.evaluate("sin(a) + arcsinh(a/b)") # you can also use functions
184
+ array([ NaN, 1.72284457, 1.79067101, ..., 1.09567006,
185
+ 0.17523598, -0.09597844])
186
+
187
+ >>> s = np.array([b'abba', b'abbb', b'abbcdef'])
188
+ >>> ne.evaluate("b'abba' == s") # string arrays are supported too
189
+ array([ True, False, False], dtype=bool)
190
+
191
+
192
+ Free-threading support
193
+ ----------------------
194
+ Starting on CPython 3.13 onwards there is a new distribution that disables the
195
+ Global Interpreter Lock (GIL) altogether, thus increasing the performance yields
196
+ under multi-threaded conditions on a single interpreter, as opposed to having to use
197
+ multiprocessing.
198
+
199
+ Whilst numexpr has been demonstrated to work under free-threaded
200
+ CPython, considerations need to be taken when using numexpr native parallel
201
+ implementation vs using Python threads directly in order to prevent oversubscription,
202
+ we recommend either using the main CPython interpreter thread to spawn multiple C threads
203
+ using the parallel numexpr API, or spawning multiple CPython threads that do not use
204
+ the parallel API.
205
+
206
+ For more information about free-threaded CPython, we recommend visiting the following
207
+ `community Wiki <https://py-free-threading.github.io/>`
208
+
209
+
210
+ Documentation
211
+ -------------
212
+
213
+ Please see the official documentation at `numexpr.readthedocs.io <https://numexpr.readthedocs.io>`_.
214
+ Included is a user guide, benchmark results, and the reference API.
215
+
216
+
217
+ Authors
218
+ -------
219
+
220
+ Please see `AUTHORS.txt <https://github.com/pydata/numexpr/blob/master/AUTHORS.txt>`_.
221
+
222
+
223
+ License
224
+ -------
225
+
226
+ NumExpr is distributed under the `MIT <http://www.opensource.org/licenses/mit-license.php>`_ license.
227
+
228
+
229
+ .. Local Variables:
230
+ .. mode: text
231
+ .. coding: utf-8
232
+ .. fill-column: 70
233
+ .. End:
@@ -159,6 +159,24 @@ Usage
159
159
  array([ True, False, False], dtype=bool)
160
160
 
161
161
 
162
+ Free-threading support
163
+ ----------------------
164
+ Starting on CPython 3.13 onwards there is a new distribution that disables the
165
+ Global Interpreter Lock (GIL) altogether, thus increasing the performance yields
166
+ under multi-threaded conditions on a single interpreter, as opposed to having to use
167
+ multiprocessing.
168
+
169
+ Whilst numexpr has been demonstrated to work under free-threaded
170
+ CPython, considerations need to be taken when using numexpr native parallel
171
+ implementation vs using Python threads directly in order to prevent oversubscription,
172
+ we recommend either using the main CPython interpreter thread to spawn multiple C threads
173
+ using the parallel numexpr API, or spawning multiple CPython threads that do not use
174
+ the parallel API.
175
+
176
+ For more information about free-threaded CPython, we recommend visiting the following
177
+ `community Wiki <https://py-free-threading.github.io/>`
178
+
179
+
162
180
  Documentation
163
181
  -------------
164
182
 
@@ -1,22 +1,51 @@
1
1
  =====================================
2
- Release notes for NumExpr 2.10 series
2
+ Release notes for NumExpr 2.11 series
3
3
  =====================================
4
4
 
5
+ Changes from 2.10.2 to 2.11.0
6
+ -----------------------------
7
+
8
+ * Initial support for free-threaded Python 3.13t has been added.
9
+ This is still experimental, so please report any issues you find.
10
+ For more info, see discussions PRs #504, #505 and #508.
11
+ Thanks to @andfoy, @rgommers and @FrancescAlted for the work.
12
+
13
+ * Fix imaginary evaluation in the form of `1.1e1j`. This was
14
+ previously not supported and would raise an error. Thanks to @27rabbitlt
15
+ for the fix.
16
+
17
+ * The test suite has been modernized to use `pytest` instead of `unittest`.
18
+ This should make it easier to run the tests and contribute to the project.
19
+
20
+ * Python 3.10 is now the minimum supported version.
21
+
22
+ Changes from 2.10.1 to 2.10.2
23
+ -----------------------------
24
+
25
+ * Better support for CPUs that do not have a power of 2 number of
26
+ cores. See #479 and #490. Thanks to @avalentino.
27
+
28
+ * Allow numexpr to run with the multithreading package in Python.
29
+ See PR #496. Thanks to @emmaai
30
+
31
+ * Wheels for Python 3.13 are now provided.
32
+
33
+
5
34
  Changes from 2.10.0 to 2.10.1
6
35
  -----------------------------
7
36
 
8
- - The default number of 'safe' threads has been upgraded to 16 (instead of
37
+ * The default number of 'safe' threads has been upgraded to 16 (instead of
9
38
  previous 8). That means that if your CPU has > 16 cores, the default is
10
39
  to use 16. You can always override this with the "NUMEXPR_MAX_THREADS"
11
40
  environment variable.
12
41
 
13
- - NumPy 1.23 is now the minimum supported.
42
+ * NumPy 1.23 is now the minimum supported.
14
43
 
15
- - Preliminary support for Python 3.13. Thanks to Karolina Surma.
44
+ * Preliminary support for Python 3.13. Thanks to Karolina Surma.
16
45
 
17
- - Fix tests on nthreads detection (closes: #479). Thanks to @avalentino.
46
+ * Fix tests on nthreads detection (closes: #479). Thanks to @avalentino.
18
47
 
19
- - The build process has been modernized and now uses the `pyproject.toml`
48
+ * The build process has been modernized and now uses the `pyproject.toml`
20
49
  file for more of the configuration options.
21
50
 
22
51
 
numexpr-2.11.0/VERSION ADDED
@@ -0,0 +1 @@
1
+ 2.11.0
@@ -9,8 +9,10 @@
9
9
  ####################################################################
10
10
 
11
11
  from __future__ import print_function
12
+
12
13
  import sys
13
14
  import timeit
15
+
14
16
  import numpy
15
17
 
16
18
  array_size = 5_000_000
@@ -0,0 +1,171 @@
1
+ #################################################################################
2
+ # To compare the performance of numexpr when free-threading CPython is used.
3
+ #
4
+ # This example makes use of Python threads, as opposed to C native ones
5
+ # in order to highlight the improvement introduced by free-threading CPython,
6
+ # which now disables the GIL altogether.
7
+ #################################################################################
8
+ """
9
+ Results with GIL-enabled CPython:
10
+
11
+ Benchmarking Expression 1:
12
+ NumPy time (threaded over 32 chunks with 16 threads): 1.173090 seconds
13
+ numexpr time (threaded with re_evaluate over 32 chunks with 16 threads): 0.951071 seconds
14
+ numexpr speedup: 1.23x
15
+ ----------------------------------------
16
+ Benchmarking Expression 2:
17
+ NumPy time (threaded over 32 chunks with 16 threads): 10.410874 seconds
18
+ numexpr time (threaded with re_evaluate over 32 chunks with 16 threads): 8.248753 seconds
19
+ numexpr speedup: 1.26x
20
+ ----------------------------------------
21
+ Benchmarking Expression 3:
22
+ NumPy time (threaded over 32 chunks with 16 threads): 9.605909 seconds
23
+ numexpr time (threaded with re_evaluate over 32 chunks with 16 threads): 11.087108 seconds
24
+ numexpr speedup: 0.87x
25
+ ----------------------------------------
26
+ Benchmarking Expression 4:
27
+ NumPy time (threaded over 32 chunks with 16 threads): 3.836962 seconds
28
+ numexpr time (threaded with re_evaluate over 32 chunks with 16 threads): 18.054531 seconds
29
+ numexpr speedup: 0.21x
30
+ ----------------------------------------
31
+
32
+ Results with free-threading CPython:
33
+
34
+ Benchmarking Expression 1:
35
+ NumPy time (threaded over 32 chunks with 16 threads): 3.415349 seconds
36
+ numexpr time (threaded with re_evaluate over 32 chunks with 16 threads): 2.618876 seconds
37
+ numexpr speedup: 1.30x
38
+ ----------------------------------------
39
+ Benchmarking Expression 2:
40
+ NumPy time (threaded over 32 chunks with 16 threads): 19.005238 seconds
41
+ numexpr time (threaded with re_evaluate over 32 chunks with 16 threads): 12.611407 seconds
42
+ numexpr speedup: 1.51x
43
+ ----------------------------------------
44
+ Benchmarking Expression 3:
45
+ NumPy time (threaded over 32 chunks with 16 threads): 20.555149 seconds
46
+ numexpr time (threaded with re_evaluate over 32 chunks with 16 threads): 17.690749 seconds
47
+ numexpr speedup: 1.16x
48
+ ----------------------------------------
49
+ Benchmarking Expression 4:
50
+ NumPy time (threaded over 32 chunks with 16 threads): 38.338372 seconds
51
+ numexpr time (threaded with re_evaluate over 32 chunks with 16 threads): 35.074684 seconds
52
+ numexpr speedup: 1.09x
53
+ ----------------------------------------
54
+ """
55
+
56
+ import os
57
+
58
+ os.environ["NUMEXPR_NUM_THREADS"] = "2"
59
+ import threading
60
+ import timeit
61
+
62
+ import numpy as np
63
+
64
+ import numexpr as ne
65
+
66
+ array_size = 10**8
67
+ num_runs = 10
68
+ num_chunks = 32 # Number of chunks
69
+ num_threads = 16 # Number of threads constrained by how many chunks memory can hold
70
+
71
+ a = np.random.rand(array_size).reshape(10**4, -1)
72
+ b = np.random.rand(array_size).reshape(10**4, -1)
73
+ c = np.random.rand(array_size).reshape(10**4, -1)
74
+
75
+ chunk_size = array_size // num_chunks
76
+
77
+ expressions_numpy = [
78
+ lambda a, b, c: a + b * c,
79
+ lambda a, b, c: a**2 + b**2 - 2 * a * b * np.cos(c),
80
+ lambda a, b, c: np.sin(a) + np.log(b) * np.sqrt(c),
81
+ lambda a, b, c: np.exp(a) + np.tan(b) - np.sinh(c),
82
+ ]
83
+
84
+ expressions_numexpr = [
85
+ "a + b * c",
86
+ "a**2 + b**2 - 2 * a * b * cos(c)",
87
+ "sin(a) + log(b) * sqrt(c)",
88
+ "exp(a) + tan(b) - sinh(c)",
89
+ ]
90
+
91
+
92
+ def benchmark_numpy_chunk(func, a, b, c, results, indices):
93
+ for index in indices:
94
+ start = index * chunk_size
95
+ end = (index + 1) * chunk_size
96
+ time_taken = timeit.timeit(
97
+ lambda: func(a[start:end], b[start:end], c[start:end]), number=num_runs
98
+ )
99
+ results.append(time_taken)
100
+
101
+
102
+ def benchmark_numexpr_re_evaluate(expr, a, b, c, results, indices):
103
+ for index in indices:
104
+ start = index * chunk_size
105
+ end = (index + 1) * chunk_size
106
+ # if index == 0:
107
+ # Evaluate the first chunk with evaluate
108
+ time_taken = timeit.timeit(
109
+ lambda: ne.evaluate(
110
+ expr,
111
+ local_dict={
112
+ "a": a[start:end],
113
+ "b": b[start:end],
114
+ "c": c[start:end],
115
+ },
116
+ ),
117
+ number=num_runs,
118
+ )
119
+ results.append(time_taken)
120
+
121
+
122
+ def run_benchmark_threaded():
123
+ chunk_indices = list(range(num_chunks))
124
+
125
+ for i in range(len(expressions_numpy)):
126
+ print(f"Benchmarking Expression {i+1}:")
127
+
128
+ results_numpy = []
129
+ results_numexpr = []
130
+
131
+ threads_numpy = []
132
+ for j in range(num_threads):
133
+ indices = chunk_indices[j::num_threads] # Distribute chunks across threads
134
+ thread = threading.Thread(
135
+ target=benchmark_numpy_chunk,
136
+ args=(expressions_numpy[i], a, b, c, results_numpy, indices),
137
+ )
138
+ threads_numpy.append(thread)
139
+ thread.start()
140
+
141
+ for thread in threads_numpy:
142
+ thread.join()
143
+
144
+ numpy_time = sum(results_numpy)
145
+ print(
146
+ f"NumPy time (threaded over {num_chunks} chunks with {num_threads} threads): {numpy_time:.6f} seconds"
147
+ )
148
+
149
+ threads_numexpr = []
150
+ for j in range(num_threads):
151
+ indices = chunk_indices[j::num_threads] # Distribute chunks across threads
152
+ thread = threading.Thread(
153
+ target=benchmark_numexpr_re_evaluate,
154
+ args=(expressions_numexpr[i], a, b, c, results_numexpr, indices),
155
+ )
156
+ threads_numexpr.append(thread)
157
+ thread.start()
158
+
159
+ for thread in threads_numexpr:
160
+ thread.join()
161
+
162
+ numexpr_time = sum(results_numexpr)
163
+ print(
164
+ f"numexpr time (threaded with re_evaluate over {num_chunks} chunks with {num_threads} threads): {numexpr_time:.6f} seconds"
165
+ )
166
+ print(f"numexpr speedup: {numpy_time / numexpr_time:.2f}x")
167
+ print("-" * 40)
168
+
169
+
170
+ if __name__ == "__main__":
171
+ run_benchmark_threaded()
@@ -2,10 +2,14 @@
2
2
  # performs better than the serial code. See issue #36 for details.
3
3
 
4
4
  from __future__ import print_function
5
+
6
+ from time import time
7
+
5
8
  import numpy as np
6
- import numexpr as ne
7
9
  from numpy.testing import assert_array_equal
8
- from time import time
10
+
11
+ import numexpr as ne
12
+
9
13
 
10
14
  def bench(N):
11
15
  print("*** array length:", N)
@@ -31,4 +35,3 @@ if __name__ == "__main__":
31
35
  ne.set_num_threads(2)
32
36
  for N in range(10, 20):
33
37
  bench(2**N)
34
-
@@ -1,4 +1,5 @@
1
1
  import numpy
2
+
2
3
  import numexpr
3
4
 
4
5
  numexpr.set_num_threads(8)