.. _l-howto-align-external-data-streaming: How to re-align external weights without loading them in memory =============================================================== This page documents the recommended recipe for re-aligning the tensor offsets of an existing two-file (or multi-file) ONNX model **without ever loading the weights in RAM**. The driver is :func:`onnx_light.onnx.align_external_data_streaming` in Python and :cpp:func:`onnx_light::AlignExternalDataStreaming` in C++. When to use it -------------- Use this how-to when **all** of the following are true: * The model is already saved on disk as a ``(.onnx, .data[, .data.1, ...])`` pair where the initializers point at the weights file(s) through their ``external_data`` entries. * You want every tensor offset in the destination weights file to be aligned to a power-of-two boundary (for example ``4096`` for page-aligned mmap or accelerator-friendly loading). * The model is too large — or the host too memory-constrained — to load the full set of weights into memory just to re-serialize them. Recipe ------ The function parses only the proto metadata and streams each tensor's bytes from the source weights file to the destination weights file in chunks of ``chunk_size`` bytes, zero-padding to the requested alignment between tensors. Peak heap usage is therefore bounded by *(metadata size + chunk_size)*, independent of the total weights size. .. tab-set:: .. tab-item:: Python :sync: python .. code-block:: python import onnx_light.onnx as onnxl total_bytes = onnxl.align_external_data_streaming( src_onnx_path="src.onnx", dst_onnx_path="dst.onnx", dst_weights_path="dst.data", alignment=4096, chunk_size=4 * 1024 * 1024, ) print(f"Wrote {total_bytes} bytes (including alignment padding) to dst.data") .. tab-item:: C++ :sync: cpp .. code-block:: cpp #include "onnx_helper.h" #include "stream.h" #include onnx::offset_t total_bytes = onnx::AlignExternalDataStreaming( /*src_onnx_path=*/"src.onnx", /*dst_onnx_path=*/"dst.onnx", /*dst_weights_path=*/"dst.data", /*alignment=*/4096, /*chunk_size=*/4 * 1024 * 1024); std::cout << "Wrote " << total_bytes << " bytes (including alignment padding) to dst.data\n"; After the call: * ``dst.onnx`` is a fresh ``.onnx`` file whose initializers all reference ``dst.data`` with aligned offsets. * ``dst.data`` is a **single** consolidated weights file — even when the source spread tensors across multiple ``external_data.location`` files. * The source files (``src.onnx`` and its ``.data`` companions) are not modified. Comparison with the in-memory variant ------------------------------------- The "usual" path is to ``load(load_external_data=True)`` the model and then re-serialize it with :attr:`SerializeOptions.alignment` set to the desired value. Functionally the two approaches produce byte-equivalent tensor payloads at the same aligned offsets; the only difference is the peak memory footprint: .. list-table:: :header-rows: 1 :widths: 30 35 35 * - Aspect - :func:`~onnx_light.onnx.align_external_data_streaming` - :func:`~onnx_light.onnx.load` + ``SerializeToFile`` * - Peak heap - O(metadata + ``chunk_size``) - O(total weights size) * - Source files - Untouched - Untouched * - Destination weights - Single consolidated file - Single consolidated file (with ``use_external_data_location=False``) * - Recommended when - Weights do not fit in RAM - Weights comfortably fit in RAM See :ref:`l-example-plot-align-external-data-streaming` for a runnable benchmark of both approaches. See also -------- * :func:`onnx_light.onnx.align_external_data_streaming` / :cpp:func:`onnx_light::AlignExternalDataStreaming` – API reference. * :ref:`l-design-loading-saving-scenarios` – design notes on complex load/save scenarios this function was designed for. * :ref:`l-howto-save-model-with-shared-external-data` – companion how-to for sharing weights across multiple models. * :ref:`l-howto-load-save-onnx-files` – common load/save patterns.