NumExpr Documentation

repository·master·Indexed 25 days ago

https://github.com/pydata/numexpr

A high-performance numerical expression evaluator for NumPy that accelerates array-wise computations. It utilizes a specialized virtual machine and multi-core CPUs to avoid intermediate memory allocations. Key features include support for Intel MKL VML acceleration for transcendental functions, chunked processing for cache optimization, and a flexible API including evaluate(), re_evaluate(), and disassemble().

Tokens
7K
Snippets
15
Records
52
Agent score
81%

What's inside NumExpr

  1. What is NumExpr?

    master

    NumExpr is a fast numerical expression evaluator designed for NumPy. It accelerates array-based expressions (e.g., "3*a+4*b") and reduces memory consumption compared to standard Python calculations.

    Key features include:

    • Multi-threading: Leverages multi-core processors for parallel execution.
    • Intel MKL Support: Optional support for Intel's Math Kernel Library (MKL) provides extremely fast evaluation of transcendental functions like sin, cos, tan, exp, and log.
    • Lightweight Dependencies: Its only required dependency is NumPy.
  2. Use reduction operations correctly

    master

    NumExpr supports the following reduction operations:

    • sum(number, axis=None)
    • prod(number, axis=None)

    CRITICAL: Reduction operations must appear as the last operation in the expression stack. If they are not last, a RuntimeError will be raised.

    Incorrect:

    ne.evaluate('sum(1)*(-1)')  # Raises RuntimeError: invalid program: reduction operations must occur last
  3. How NumExpr achieves high performance

    master

    NumExpr achieves high performance by using an in-between approach to element-wise evaluation, avoiding the memory overhead of NumPy's temporary arrays and the loop overhead of pure Python.

    Key mechanisms include:

    • Chunked Processing: Instead of processing entire arrays or single elements, NumExpr processes arrays in chunks (defaulting to 4096 elements) using a register machine. This optimizes cache usage and branch prediction.
    • Virtual Machine: The expression is compiled into a bytecode program executed by a virtual machine written in C.
    • Vector Registers: The virtual machine uses registers that are many elements wide to handle chunks of data efficiently.
    • Multi-threading: The virtual machine is multi-threaded, allowing for efficient parallelization of operations.

    To benefit from these optimizations, you should typically operate with large arrays (larger than your CPU's cache size).

  4. NumExpr support for free-threaded CPython

    master

    NumExpr is compatible with free-threaded CPython (CPython 3.13+ where the GIL is disabled).

    Usage Recommendation: To avoid oversubscription (having too many active threads competing for CPU resources), follow one of these two patterns:

    1. Use the main CPython interpreter thread to spawn multiple C threads via the parallel NumExpr API.
    2. Spawn multiple CPython threads that do not use the parallel API.

    Avoid mixing the parallel NumExpr API with direct Python threading in a way that causes excessive thread contention.

  5. Important considerations when adding functions

    master

    When extending NumExpr with new functions, be aware of these technical constraints and edge cases:

    • Opcode Limits: OPCODES are currently limited to a maximum value of 255. If you exceed this, you must update the latin_1 encoding in necompiler.py and potentially change the get_return_sig function in interpreter.cpp which assumes an unsigned char type.
    • Complex Numbers: Functions accepting or returning complex arguments must be added to complex_functions.hpp. Their signatures in interpreter.cpp and interp_body.cpp typically differ from standard real-valued functions.
    • MSVC/Platform Specifics: Namespace clashes or casting issues on Windows may require modifications to numexpr/numexpr_config.hpp or numexpr/msvc_function_stubs.hpp (e.g., providing wrappers for isnan or isfinite if the platform's single-precision versions are inconsistent).
    • Type Mismatches: If the output type differs from the input type, you must update the __init__ function of the FuncNode class in expressions.py to handle type inference correctly.
  6. Performance characteristics of the NumExpr 2.0 Virtual Machine

    master

    The NumExpr 2.0 virtual machine is built on the NumPy ndarray iterator. It is optimized for large-scale numerical computations by providing high performance in several key scenarios:

    • Broadcasting: Arrays are broadcasted on-the-fly, avoiding additional memory allocation.
    • Non-native dtypes: Non-native data types are translated to native dtypes on-the-fly, eliminating the need for full array conversions.
    • Fortran-ordered arrays: The iterator optimizes operations for Fortran-ordered arrays directly, removing the need for transpositions.

    Note on Small Arrays: The 2.0 virtual machine has a higher setup time (approximately 8 µs) compared to the 1.x series. Consequently, performance for very small arrays may be slightly lower. For extremely small arrays, standard NumPy operations are typically faster. For large arrays, the setup overhead is negligible.

  7. Understand NumExpr internal datatypes and casting

    master

    NumExpr operates internally on a specific set of types. If your input arrays use different types, they will be upcasted to one of these:

    • 8-bit boolean (bool)
    • 32-bit signed integer (int or int32)
    • 64-bit signed integer (long or int64)
    • 32-bit single-precision float (float or float32)
    • 64-bit double-precision float (double or float64)
    • 2x64-bit double-precision complex (complex or complex128)
    • Raw string of bytes (bytes in Python 3+)

    Important Casting Notes:

    • Integer Upcasting: int8, uint8, int16, and uint16 are upcasted to int32. uint32 is upcasted to int64.
    • Float Functions: Applying a float function (like sin) to int8 or int16 returns float64 (not float32).
    • Scalar vs Array Priority: In operations involving a scalar and an array, NumExpr uses the scalar's type to determine the result, which differs from NumPy (where the array type takes priority). For example, float32_array * float64_scalar returns float64 in NumExpr. To keep float32, use a float32 scalar.
  8. Build NumExpr from source

    master

    NumExpr requires Python 3.7+ and NumPy 1.13+. You must have a C-compiler (MSVC on Windows, GCC on Linux) installed.

    To build and install:

    $ pip install .

    To verify the installation, run the following from a directory other than the repository directory:

    $ python -c "import numexpr; numexpr.test()"
    $ pip install .
    $ python -c "import numexpr; numexpr.test()"
  9. Use ne.evaluate() for fast array expressions

    master

    The primary way to use NumExpr is via the ne.evaluate() function. It takes a string expression and evaluates it against provided array operands. This is significantly faster than NumPy for large arrays because it avoids allocating memory for intermediate results and uses multi-threading.

    Key features:

    • Supports complex mathematical expressions.
    • Supports transcendental functions (e.g., sin, arcsinh).
    • Supports string arrays.
    • Works best with large arrays that exceed the L1 CPU cache.
  10. Install NumExpr from source

    master

    To build NumExpr from source, use the standard Python installation command.

    Prerequisites:

    • Linux/macOS: Compilers like gcc or clang should be present.
    • Windows: You must install the Microsoft Visual C++ Build Tools. For Python 3.6+, the latest version of MSVC build tools is sufficient.
    • NumPy: Ensure you have the required version of NumPy installed (check requirements.txt).

    Build and Test:

    1. Install using: pip install [-e] .
    2. Verify the installation by running: python -c "import numexpr; numexpr.test()"

    Warning: Do not run tests while inside the source directory, as this will cause import errors.

    pip install [-e] .
    python -c "import numexpr; numexpr.test()"
  11. Enable Intel MKL VML acceleration in NumExpr

    master

    NumExpr supports Intel's Vector Math Library (VML), which is included in Intel's Math Kernel Library (MKL). Enabling VML accelerates the evaluation of transcendental (CPU-bound) functions such as sin, cos, tan, exp, log, and sinh on Intel CPUs.

    Note that VML does not accelerate pure algebraic expressions (e.g., simple additions or multiplications).

  12. Install NumExpr via pip or conda

    master

    You can install NumExpr using standard Python package managers.

    For most users, use pip:

    pip install numexpr

    If you are using Anaconda or Miniconda, use conda:

    conda install numexpr

    Note on MKL support: Wheels installed via pip do not include Intel MKL support. If you require MKL acceleration, use conda (provided MKL is used for your NumPy backend) or build from source.