Skip to content

Commit bdff268

Browse files
authored
Merge pull request #565 from Blosc/miniexpr-win2
Miniexpr for Windows
2 parents 5d7dc9a + b16e6f8 commit bdff268

4 files changed

Lines changed: 48 additions & 10 deletions

File tree

CMakeLists.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ set(MINIEXPR_BUILD_BENCH OFF CACHE BOOL "Build miniexpr benchmarks" FORCE)
5858

5959
FetchContent_Declare(miniexpr
6060
GIT_REPOSITORY https://github.com/Blosc/miniexpr.git
61-
GIT_TAG 979573da618e0443c3984bad8db3ed5d9ce72f75
61+
GIT_TAG 77d633cb2c134552da045b8d2cc0ad23908e6b9e
6262
)
6363
FetchContent_MakeAvailable(miniexpr)
6464

@@ -116,7 +116,7 @@ else()
116116
include(FetchContent)
117117
FetchContent_Declare(blosc2
118118
GIT_REPOSITORY https://github.com/Blosc/c-blosc2
119-
GIT_TAG f057d1519c0a990f6351cd39c6a659c752fb84e9
119+
GIT_TAG bf21f84680542e680fd94fdc05c5a76259df1345
120120
)
121121
FetchContent_MakeAvailable(blosc2)
122122
include_directories("${blosc2_SOURCE_DIR}/include")

RELEASE_NOTES.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22

33
## Changes from 4.0.0-b1 to 4.0.0-b2
44

5-
XXX version-specific blurb XXX
5+
- On Windows, miniexpr is temporarily disabled for integral outputs and mixed-dtype expressions.
6+
Set `BLOSC2_ENABLE_MINIEXPR_WINDOWS=1` to override this for testing.
67

78
## Changes from 3.12.2 to 4.0.0-b1
89

src/blosc2/blosc2_ext.pyx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1946,6 +1946,7 @@ cdef int aux_miniexpr(me_udata *udata, int64_t nchunk, int32_t nblock,
19461946
memset(params_output, 0, udata.array.blocknitems * typesize)
19471947
free(input_buffers)
19481948
return 0
1949+
19491950
for i in range(udata.ninputs):
19501951
ndarr = udata.inputs[i]
19511952
input_buffers[i] = malloc(ndarr.sc.blocksize)
@@ -2012,14 +2013,19 @@ cdef int aux_miniexpr(me_udata *udata, int64_t nchunk, int32_t nblock,
20122013
cdef uintptr_t offset_bytes = typesize * linear_block_index
20132014

20142015
# Call thread-safe miniexpr C API
2016+
# NOTE: me_eval_nd expects the OUTPUT block size (in items), not the input block size.
2017+
# For element-wise operations with same dtypes, they're equal, but for type-changing
2018+
# operations (e.g., arccos(int32) -> float64), we must use the output's block item count.
2019+
cdef int output_blocknitems = udata.array.blocknitems
2020+
20152021
if udata.aux_reduc_ptr == NULL:
20162022
aux_reduc_ptr = <void *> params_output
20172023
else:
20182024
# Reduction operation: evaluate only valid items into a single output element.
20192025
# NOTE: miniexpr handles scalar outputs in me_eval_nd without touching tail bytes.
20202026
aux_reduc_ptr = <void *> (<uintptr_t> udata.aux_reduc_ptr + offset_bytes)
20212027
rc = me_eval_nd(miniexpr_handle, <const void**> input_buffers, udata.ninputs,
2022-
aux_reduc_ptr, blocknitems, nchunk, nblock, udata.eval_params)
2028+
aux_reduc_ptr, output_blocknitems, nchunk, nblock, udata.eval_params)
20232029
if rc != 0:
20242030
raise RuntimeError(f"miniexpr: issues during evaluation; error code: {rc}")
20252031

src/blosc2/lazyexpr.py

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -95,10 +95,9 @@
9595
try_miniexpr = True
9696
if blosc2.IS_WASM:
9797
try_miniexpr = False
98-
if sys.platform == "win32":
99-
# Although miniexpr has support for windows, the integration with Blosc2
100-
# still has some rough edges.
101-
try_miniexpr = False
98+
99+
_MINIEXPR_WINDOWS_OVERRIDE = os.environ.get("BLOSC2_ENABLE_MINIEXPR_WINDOWS", "").strip().lower()
100+
_MINIEXPR_WINDOWS_OVERRIDE = _MINIEXPR_WINDOWS_OVERRIDE not in ("", "0", "false", "no", "off")
102101

103102

104103
def ne_evaluate(expression, local_dict=None, **kwargs):
@@ -1326,14 +1325,34 @@ def fast_eval( # noqa: C901
13261325
use_miniexpr = False
13271326
if not (all_ndarray and out is None):
13281327
use_miniexpr = False
1328+
has_complex = any(
1329+
isinstance(op, blosc2.NDArray) and blosc2.isdtype(op.dtype, "complex floating")
1330+
for op in operands.values()
1331+
)
1332+
if isinstance(expression, str) and has_complex:
1333+
if sys.platform == "win32":
1334+
# On Windows, miniexpr has issues with complex numbers
1335+
use_miniexpr = False
1336+
if any(tok in expression for tok in ("!=", "==", "<=", ">=", "<", ">")):
1337+
use_miniexpr = False
1338+
if sys.platform == "win32" and use_miniexpr and not _MINIEXPR_WINDOWS_OVERRIDE:
1339+
# Work around Windows miniexpr issues for integer outputs and dtype conversions.
1340+
if blosc2.isdtype(dtype, "integral"):
1341+
use_miniexpr = False
1342+
else:
1343+
dtype_mismatch = any(
1344+
isinstance(op, blosc2.NDArray) and op.dtype != dtype for op in operands.values()
1345+
)
1346+
if dtype_mismatch:
1347+
use_miniexpr = False
13291348

13301349
if use_miniexpr:
13311350
cparams = kwargs.pop("cparams", blosc2.CParams())
13321351
# All values will be overwritten, so we can use an uninitialized array
13331352
res_eval = blosc2.uninit(shape, dtype, chunks=chunks, blocks=blocks, cparams=cparams, **kwargs)
13341353
try:
13351354
res_eval._set_pref_expr(expression, operands, fp_accuracy=fp_accuracy)
1336-
print("expr->miniexpr:", expression, fp_accuracy)
1355+
# print("expr->miniexpr:", expression, fp_accuracy)
13371356
# Data to compress is fetched from operands, so it can be uninitialized here
13381357
data = np.empty(res_eval.schunk.chunksize, dtype=np.uint8)
13391358
# Exercise prefilter for each chunk
@@ -2036,6 +2055,18 @@ def reduce_slices( # noqa: C901
20362055
isinstance(op, blosc2.NDArray) and blosc2.isdtype(op.dtype, "complex floating")
20372056
for op in operands.values()
20382057
)
2058+
if has_complex and sys.platform == "win32":
2059+
# On Windows, miniexpr has issues with complex numbers
2060+
use_miniexpr = False
2061+
if sys.platform == "win32" and use_miniexpr and not _MINIEXPR_WINDOWS_OVERRIDE:
2062+
if blosc2.isdtype(dtype, "integral"):
2063+
use_miniexpr = False
2064+
else:
2065+
dtype_mismatch = any(
2066+
isinstance(op, blosc2.NDArray) and op.dtype != dtype for op in operands.values()
2067+
)
2068+
if dtype_mismatch:
2069+
use_miniexpr = False
20392070
if has_complex and any(tok in expression for tok in ("!=", "==", "<=", ">=", "<", ">")):
20402071
use_miniexpr = False
20412072
if where is not None and len(where) != 2:
@@ -2073,7 +2104,7 @@ def reduce_slices( # noqa: C901
20732104
else:
20742105
expression_miniexpr = f"{reduce_op_str}({expression})"
20752106
res_eval._set_pref_expr(expression_miniexpr, operands, fp_accuracy, aux_reduc)
2076-
print("expr->miniexpr:", expression, reduce_op, fp_accuracy)
2107+
# print("expr->miniexpr:", expression, reduce_op, fp_accuracy)
20772108
# Data won't even try to be compressed, so buffers can be unitialized and reused
20782109
data = np.empty(res_eval.schunk.chunksize, dtype=np.uint8)
20792110
chunk_data = np.empty(res_eval.schunk.chunksize + blosc2.MAX_OVERHEAD, dtype=np.uint8)

0 commit comments

Comments
 (0)