Getting Started#
Installation#
Install from source:
pip install .
Or with Pixi:
pixi install
pixi run install
Or build with CMake (C++ only):
cmake -S . -B build -DONNX_LIGHT_CPU_BUILD_TESTS=ON \
-DONNX_LIGHT_CPU_BUILD_PYTHON=OFF \
-DONNX_LIGHT_CPU_WITH_ONNX_LIGHT=ON
cmake --build build
The C++ quick start requires the onnx-light C++ package to be installed so
find_package(onnx_light) can locate it.
Diagnosing native-extension import failures#
Before attributing an undefined symbol to import order, rebuild both projects from clean source trees with no existing native artifacts. Reusing an in-place build can mix extensions and shared libraries from different configurations. Use absolute source paths and fresh build directories; do not install either project into site-packages or use editable installs for this check.
For example, on Linux, with the Python build dependencies already available:
export ONNX_SOURCE=/absolute/path/to/onnx-light
export CPU_SOURCE=/absolute/path/to/onnx-light-cpu
export BUILD_ROOT="$(mktemp -d)"
cmake -S "$ONNX_SOURCE" -B "$BUILD_ROOT/onnx-light" \
-DCMAKE_BUILD_TYPE=Release -DONNX_LIGHT_INSTALL=OFF \
-DPython_EXECUTABLE="$(command -v python)"
cmake --build "$BUILD_ROOT/onnx-light" --parallel 4
cmake --install "$BUILD_ROOT/onnx-light" --prefix "$ONNX_SOURCE"
export PYTHONPATH="$CPU_SOURCE:$ONNX_SOURCE"
python "$CPU_SOURCE/setup.py" build_ext --inplace --onnx-light-source \
--build-temp "$BUILD_ROOT/onnx-light-cpu" --parallel 4
cd "$CPU_SOURCE"
python -m unittest \
unittests.python.test_kernels_e2e.TestExtensionImportOrder -v
Here cmake --install places the dependency’s freshly built libraries in its
source tree, not in site-packages. --onnx-light-source links against those
same runtime libraries rather than rebuilding a second copy.
In-place builds may place symbolic links in onnx_light/onnx_py pointing to
native artifacts in the same checkout’s build directory. These are supported:
the extension is located through the selected source package, and linking uses
the resolved runtime-library paths.
Python builds that exchange nanobind-bound C++ objects must also use the same
effective ABI mode. Release builds target onnx-light 0.2.0; its wheels must be
published before the matching onnx-light-cpu release can be built. The release
configuration uses nanobind’s
native ABI even though ONNX_LIGHT_PYTHON_STABLE_ABI defaults to ON:
the option requests stable ABI support, but the effective mode depends on the
build frontend and Python configuration. The matching onnx-light-cpu release
therefore sets ONNX_LIGHT_CPU_PYTHON_STABLE_ABI=OFF. In either mode,
onnx-light-cpu must link the exact shared onnx-light runtime loaded by Python;
linking a second copy creates a separate type and registry universe, so objects
such as RuntimeContext cannot cross the module boundary. A downstream build
must match the effective ABI reported by onnx-light rather than infer it from a
CMake option or wheel tag.
With onnx-light 0.2.0 source checkouts, the standalone proto headers are under
onnx_proto/include and their helpers under onnx_proto/helpers/include.
--onnx-light-source uses those paths while still linking the exact core
and proto shared libraries loaded by Python. Source checkouts with the
standalone threading library also use onnx_threads/include and link the
same threading shared library as Python, without rebuilding it. Direct CMake
builds can select that library with ONNX_LIGHT_CPU_ONNX_LIGHT_THREADS_LIBRARY;
Windows builds also need ONNX_LIGHT_CPU_ONNX_LIGHT_THREADS_IMPLIB.
Installed Python wheels retain the packaged header layout under onnx_light.
The selected threading library is also linked with that layout, without requiring
the source-only onnx_threads/include directory. Both layouts are recognized
when selecting the runtime through PYTHONPATH, including forwarding the
threading import library on Windows.
The regression test imports _cpukernels then _cpuregister, and the
reverse order, in separate Python subprocesses. It runs from both the checkout
and a temporary working directory, verifies that the CPU extensions came from
the checkout, and exercises kernel registration. A failure includes the child
process’s output and traceback. If a clean build passes but reused artifacts
fail, record both source revisions and build commands with the report rather
than changing symbol visibility or preloading libraries to mask the failure.
For issue #698, this
procedure did not reproduce the reported undefined symbol on Linux x86-64.
The clean Release builds used CPU sources at 1ebe06e8b3e4 and onnx-light at
e03a1ba56145, with Python 3.13.15, GCC 13.3, CMake 3.31.6, and nanobind 3.0.1.
Both import orders passed from both working directories, as did the existing
kernel-usage and SIMD-detection tests (nine tests total). Neither project was
installed into site-packages, and no runtime-library preload was needed.
The reported mixed-artifact failure therefore did not warrant a source linkage
or export change.
The same regression subsequently exposed a separate Windows DLL-search failure:
_cpuregister could not locate onnx-light’s dependent DLLs in a fresh process.
On Windows, the bindings package registers the selected onnx-light package’s
onnx_py directory with os.add_dll_directory and retains its handle.
This makes the DLLs discoverable without preloading either project’s extensions;
adding the directory to PATH alone is insufficient for Python’s extension
loader.
Windows release-wheel repair excludes the runtime DLLs supplied by onnx-light
from delvewheel bundling and renaming, just as Linux and macOS keep that
runtime external. The installed onnx-light wheel supplies the shared runtime;
bundling another copy would separate its types and kernel registries.
Quick Start#
Register the optimized kernels, then execute the graph through onnx-light’s runtime. Runtime dispatch selects the registered onnx-light-cpu kernel and its best available SIMD path:
from onnx_light_cpu import detect_simd_level, has_cpu_kernels, register_kernels
# Check what SIMD level is available:
print("CPU kernels available:", has_cpu_kernels())
print("SIMD level:", detect_simd_level())
# When onnx-light is installed, register the optimized kernels globally so
# ReferenceEvaluator uses them for supported ONNX operators.
register_kernels()
#include <onnx_light_cpu/kernels/register_kernels.h>
#include <onnx_core/runtime/kernels/kernel_context.h>
#include <onnx_core/runtime/memory/simple_tensor.h>
#include <onnx_core/runtime/runtime_context.h>
#include <onnx_core/runtime/runtime_session.h>
#include <onnx_proto/onnx_helper.h>
int main() {
namespace rt = ONNX_LIGHT_NAMESPACE::core::runtime;
onnx_light_cpu::RegisterAllKernels();
ONNX_LIGHT_NAMESPACE::GraphProto graph;
graph.ref_node().push_back(
ONNX_LIGHT_NAMESPACE::MakeNode("Abs", {"x"}, {"y"}));
rt::RuntimeContext context(rt::KernelContext(rt::DefaultOpset(18)));
context.Set(
"x", rt::Tensor::FromFloat("x", {4}, {-1.0f, 2.0f, -3.0f, 4.0f}));
rt::RuntimeSession session(context.GetExecutionPlan(graph));
session.Run(context);
const float *output = context.Get("y").AsFloat();
// output = {1.0f, 2.0f, 3.0f, 4.0f}
}