Heuristic:Onnx Onnx Big Endian Byte Order Handling
| Knowledge Sources | |
|---|---|
| Domains | Data_Conversion, Platform_Compatibility |
| Last Updated | 2026-02-10 02:00 GMT |
Overview
ONNX tensors use little-endian byte order internally; big-endian systems require automatic byte-swapping during conversion.
Description
The ONNX specification stores raw tensor data in little-endian byte order. On big-endian systems (such as IBM s390x/z, some POWER systems, and older SPARC), the numpy_helper conversion functions automatically detect the system's byte order and perform byte-swapping as needed. When converting a NumPy array to a TensorProto (`from_array`), data is converted to little-endian before storage. When converting a TensorProto back to a NumPy array (`to_array`), data is byte-swapped from little-endian to the system's native byte order. The `ModelContainer` class also handles byte-swapping for direct tensor data access.
Usage
Use this heuristic when:
- Running ONNX on big-endian systems (s390x, POWER in BE mode)
- Debugging unexpected tensor values on non-x86 platforms
- Implementing custom ONNX tools that read or write raw tensor data
- Understanding cross-platform model portability
The Insight (Rule of Thumb)
- Action: Let the ONNX library handle byte-order conversion automatically. Do not manually byte-swap tensor data. If implementing custom raw data handling, always check `sys.byteorder` and convert to/from little-endian.
- Value: ONNX canonical byte order is little-endian (matching x86 native order).
- Trade-off: Automatic byte-swapping adds a small overhead on big-endian systems. This is unavoidable for cross-platform compatibility.
Reasoning
Little-endian was chosen as the canonical byte order because the vast majority of modern computing hardware (x86, ARM in LE mode) uses little-endian. This means most users pay zero overhead for byte order handling. Only big-endian systems incur the byte-swap cost, which is a simple in-place operation via `np.byteswap()`.
The byte-order handling is implemented at two levels: `to_array` (read path) and `from_array`/`_to_bytes` (write path), ensuring round-trip correctness regardless of the host system's endianness.
Code evidence from `onnx/numpy_helper.py:209-211` (read path):
if sys.byteorder == "big":
# Convert endian from little to big
raw_data = np.frombuffer(raw_data, dtype=np_dtype).byteswap().tobytes()
Code evidence from `onnx/numpy_helper.py:297-301` (write path):
if array.dtype.byteorder == ">" or (
sys.byteorder == "big" and array.dtype.byteorder == "="
):
# Ensure that the bytes will be in little-endian byte-order.
array = array.astype(array.dtype.newbyteorder("<"))