Python · Runtime Dispatch

Runtime Dispatch

import stringzilla 6.0.0-rc.1 · 91a27bdd

StringZilla detects the running CPU’s SIMD features and routes every kernel to the fastest capability both the CPU and this build have, without recompilation. Capabilities are sz.Capability flags, one member per CPU capability, like sz.Capability.HASWELL or sz.Capability.NEON; GPU bits sit above every member, so a GPU mask prints as a number.

The producers report capabilities for the CPU, or for one GPU by its runtime’s ordinal, or make a stream on that GPU, and are the only calls taking an ordinal.

  • sz.__version__ — the package version string.
  • sz.cpu_capabilities_detected(), sz.cuda_capabilities_detected(ordinal) — what the device can execute, whether or not its kernels were compiled in.
  • sz.cpu_capabilities_compiled(), sz.cuda_capabilities_compiled() — what this build holds kernels for, whether or not the device runs them.
  • sz.cpu_capabilities_enabled(), sz.cuda_capabilities_enabled(ordinal) — what calls dispatch with, both of the above; on the CPU it always has SERIAL.
  • sz.cuda_count_devices() — the number of CUDA devices; it raises RuntimeError when none answers, as when this build lacks its kernels, and so does an ordinal past the last device.
  • sz.cpu_configure_thread(capabilities) — prepare the calling thread for the kernels of capabilities; call it on every thread that runs kernels.
  • sz.cuda_stream_init(ordinal), sz.cuda_stream_free(stream) — make a stream on one device, as the integer every stream= takes, and free it once nothing queued on it remains.
  • sz.synchronize(capabilities, stream=None) — wait for one stream of the vendor capabilities names, or its default one.

ROCm and Metal have the same rocm_ and metal_ producers. Every function that runs kernels also takes a keyword-only capabilities=, narrowing that one call and leaving every other call alone. The keyword keeps no serial fallback, so a mask with no kernel for the call raises LookupError.

import stringzilla as sz

sz.cpu_capabilities_enabled()                                 # e.g. <Capability.SERIAL|NEON|NEONAES|NEONSHA: 449>
sz.find("haystack", "st", capabilities=sz.Capability.SERIAL)  # one call on the scalar kernel
sz.cpu_configure_thread(sz.cpu_capabilities_enabled())        # on a thread that runs kernels
[sz.cuda_capabilities_enabled(ordinal) for ordinal in range(sz.cuda_count_devices())]  # raises without CUDA

Engines and the stateful objects, like Hasher, Sha256 and the AES keys, dispatch every call with the capabilities they were constructed with.

API

Types

Capability
__init__.pyi:19 ↗
class Capability(enum.IntFlag)

Values

SERIAL
WESTMERE
GOLDMONT
HASWELL
SKYLAKE
ICELAKE
NEON
NEONAES
NEONSHA
SVE
SVE2
SVE2AES
RVV
RVVCRYPTO
V128
V128RELAXED
LOONGSONASX
POWERVSX

Functions

cpu_capabilities_detectedGet the capabilities this CPU can execute.
__init__.pyi:1292 ↗
def cpu_capabilities_detected() -> Capability

Detected from CPUID or HWCAP. Says nothing about whether the kernels were compiled in, which cpu_capabilities_compiled reports.

cpu_capabilities_compiledGet the CPU capabilities whose kernels were compiled into this binary.
__init__.pyi:1302 ↗
def cpu_capabilities_compiled() -> Capability

Decided at build time. Independent of the hardware: a binary built with a broken probe toolchain still detects this machine's full set while containing no SIMD kernels at all, which is what makes a scalar build hard to spot.

cpu_capabilities_enabledGet the CPU capabilities kernels run with, unless a call passes capabilities=.
__init__.pyi:1313 ↗
def cpu_capabilities_enabled() -> Capability

It is cpu_capabilities_detected() & cpu_capabilities_compiled(), and always has SERIAL.

cpu_configure_threadPrepare the calling thread for kernels of capabilities.
__init__.pyi:1322 ↗
def cpu_configure_thread(capabilities: Capability, /) -> None

Call it on every thread that runs kernels, before the first of them.

Parameters

capabilitiesCapabilityThe mask kernels on this thread run with.
*_count_devices× 3Count the devices of one GPU vendor this process sees: cuda_count_devices, and the rocm_ and metal_ ones alike.
__init__.pyi:1334 ↗
def cuda_count_devices() -> int
Symbol
cuda_count_devices
rocm_count_devices
metal_count_devices

Raises

  • RuntimeError: No device of that vendor answers, or this build lacks its kernels.
*_capabilities_detected× 3Get the capabilities a GPU can execute, by its runtime's ordinal, whether or not the kernels were compiled in: cuda_capabilities_detected, and the rocm_ and metal_ ones alike.
__init__.pyi:1367 ↗
def cuda_capabilities_detected(ordinal: int, /) -> Capability
Symbol
cuda_capabilities_detected
rocm_capabilities_detected
metal_capabilities_detected

Returns

GPU bits sit above every member, so they print as a number.

Raises

  • RuntimeError: No device with that ordinal answers.
*_capabilities_compiled× 3Get the capabilities of one GPU vendor whose kernels were compiled into this binary, whether or not a device runs them: cuda_capabilities_compiled, and the rocm_ and metal_ ones alike.
__init__.pyi:1409 ↗
def cuda_capabilities_compiled() -> Capability
Symbol
cuda_capabilities_compiled
rocm_capabilities_compiled
metal_capabilities_compiled
*_capabilities_enabled× 3Get the capabilities kernels run with on a GPU, by its runtime's ordinal, both detected and compiled: cuda_capabilities_enabled, and the rocm_ and metal_ ones alike.
__init__.pyi:1433 ↗
def cuda_capabilities_enabled(ordinal: int, /) -> Capability
Symbol
cuda_capabilities_enabled
rocm_capabilities_enabled
metal_capabilities_enabled

Engines, Strings.copy and synchronize take this mask, which names the vendor, and a stream, which names the device.

Raises

  • RuntimeError: No device with that ordinal answers.
*_stream_init× 3Create a stream on a GPU, by its runtime's ordinal, which names that device to the engines, Strings.copy and synchronize it is passed to: cuda_stream_init, and the rocm_ and metal_ ones alike.
__init__.pyi:1472 ↗
def cuda_stream_init(ordinal: int, /) -> int
Symbol
cuda_stream_init
rocm_stream_init
metal_stream_init

Free it with the matching *_stream_free once its work is synchronized.

Returns

The cudaStream_t, hipStream_t or id<MTLCommandQueue> handle.

Raises

  • RuntimeError: No device with that ordinal answers, or this build lacks its vendor.
*_stream_free× 3Free a stream cuda_stream_init made, and the rocm_ and metal_ ones alike, once synchronize joined whatever was queued on it; nothing may use the stream afterwards.
__init__.pyi:1517 ↗
def cuda_stream_free(stream: int, /) -> None
Symbol
cuda_stream_free
rocm_stream_free
metal_stream_free
synchronizeWait for everything enqueued on one stream, releasing the GIL meanwhile.
__init__.pyi:1541 ↗
def synchronize(
    capabilities: Capability,
    stream: int | None = None,
) -> None

A GPU engine's verbs enqueue and return, so their outputs are read, and their inputs freed, only after this returns. Returns at once for CPU capabilities.

Parameters

capabilitiesCapabilityA producer's mask, which names the vendor.
streamint | NoneA stream of that vendor as an integer, or None for the default.

Raises

  • BufferError: The stream belongs to another device.
  • RuntimeError: A launch on the stream failed, or the device does not answer.