Why Is Packing Compiler Outputs into One File Hard?

Treat a model file as a protocol with offsets, lengths, and ownership.

Posted by Bruce Lee on 2026-08-03

Series contents · Engineering and Delivery · 阅读中文版

“If we only want one file in the end, why not zip the whole directory?”

That is a reasonable backup strategy, but not necessarily a loading protocol. A runtime may need direct access to input and output tensor descriptions, instruction locations, parameter lengths, and destination addresses. It should not have to infer directory conventions or treat every temporary compiler file as a formal interface.

A model file is therefore a protocol between a producer and a consumer before it is a sequence of bytes.

File offsets and device addresses use different coordinate systems

Two numbers are especially easy to confuse: where a segment lives in the file and where it should be loaded.

An illustrative descriptor might be:

1
2
3
4
5
segment = {
file_offset: byte position within the complete file,
target_address: destination address after loading,
byte_length: payload length
}

All three fields may be integers, but they are not interchangeable. Enlarging the file header changes later file_offset values without necessarily changing target_address. A new memory plan may change only target_address.

Suppose the file begins with an H-byte header and n descriptors of D bytes each. In this teaching format, with no padding between segments and no additional alignment, the first payload begins at:

1
2
payload_start = H + n × D
offset[i] = payload_start + Σ(length[j], j < i)

If each submodel has its own local header, the protocol must specify whether descriptors hold absolute file offsets or offsets within a submodel. Forgetting a base address may fail only when a second submodel exists. Testing one model with one parameter segment can leave the defect hidden for a long time.

The reviewed packer explicitly computed segment sizes, positions, and the final cursor, then checked the total encoded length. These structural checks are protocol engineering, not miscellaneous chores around the format code.

Why the machine cannot choose byte order for you

If a format specifies little-endian fields, serialization should encode them explicitly. Writing a C structure’s memory image directly introduces dependencies on padding, alignment, type widths, and host byte order.

A teaching interface might be:

1
2
3
write_u32_le(section_count)
write_u32_le(payload_offset)
write_bytes(payload)

This illustrates a principle and is insufficient to reconstruct the original format. A real protocol also needs a version, reserved-field rules, and a compatibility policy. Should an old reader reject a new version, skip unknown segments, or follow a defined compatibility rule?

Matching total lengths is not enough either. Swapping two fields can leave the length unchanged. Structural size checks must be combined with readback or an independent format interpretation; the final cursor equation is not complete acceptance criteria.

Valid JSON is only the beginning

A syntactically valid JSON document can contain a negative address, an excessive length, an invalid dtype, or a reference to a missing file. Packaging must check protocol semantics.

Useful checks include whether an address fits its unsigned field, whether tensor ranks and dimensions satisfy the format, whether instruction text contains valid bits, whether declared lengths match actual bytes, whether listed submodels exist, and whether emitted size agrees with the computed result.

Strictness is not an end in itself. These checks prevent bad input from becoming an out-of-bounds access or an opaque model-loading failure later.

Avoid extrapolating beyond the evidence. A length check does not prove that every offset addition is protected against overflow. A filename check does not prove that all referenced paths are sandboxed. Record checks actually visible in the diff separately from checks that still need verification.

Even names are part of the protocol

A fixed-length name seems simple: truncate it when it is too long. But UTF-8 characters may occupy several bytes; a cut through a character can produce invalid encoding. Two long names can also collapse to the same prefix.

Some protocols allow truncation; others reject oversized input. Either policy must treat the name as a byte field, not merely count visible characters. The protocol should explain whether the name participates in lookup, deduplication, or output-file naming.

The native integration also checked output names obtained from model metadata, rejecting path separators and special directory names. This prevented a model name from being interpreted directly as a directory structure. A complete filename policy would still need platform rules and collision handling. Those checks alone do not establish comprehensive cross-platform path safety.

Why consider native-library packaging as well as a script?

A script is convenient for validating a format quickly. Standard libraries can handle JSON parsing, byte encoding, and file output. Native integration offers another route: the full compilation entry point can produce the final model without asking its caller to manage an additional process.

That is more than translating a function into C++. A language boundary requires an explicit ownership contract:

1
2
3
4
5
pack_directory(input, out_blob)
success → caller receives data and length, and must release them
failure → caller must not treat residual objects as a complete result
write_blob(blob, output)
release_blob(blob)

Error-string lifetime also needs a definition. Can the next call overwrite it? Is it readable after success? Can the name be accessed after release? A seemingly harmless diagnostic can lose its essential context if the object has already been cleared.

Callers should therefore preserve necessary diagnostics while the resource is valid and satisfy the ownership contract on every exit path.

How can two implementations prove they speak the same language?

The dangerous migration criterion is that the new implementation produces files that “look about right.” A stronger validation plan includes:

Experiment What it should establish
Package the same minimal input with each implementation Both follow the same field and ordering rules
Interpret the result with an independent reader The encoder and test do not share the same bug
Use multiple segments and submodels Offset bases and cursor accumulation are correct
Exercise name limits, empty segments, and large fields Failure rules are explicit and repeatable
Deliberately truncate a payload Metadata-length validation actually detects it
Simulate an output-write failure Allocated memory is released and the error propagates

If the protocol permits different but equivalent orderings, semantic comparison is more appropriate than unconditional byte equality. If it promises a canonical encoding, add byte-for-byte comparison. Acceptance criteria should come from the protocol, not the test author’s preference.

Which packaging costs matter?

Constructing the entire byte array before writing is straightforward and makes total-length checks convenient. With a large model, however, the process may hold original payloads, a concatenation buffer, and intermediate representations at the same time. Peak memory is not simply final file size.

Streaming output can eliminate some copies, but may require calculating tables first, backpatching offsets, or using another layout when output is not seekable. Both designs have costs. Without measurements, a native library cannot be declared faster, and removing one process launch cannot be presented as improved model-execution performance.

The lasting lesson is not a magic number or a structure definition. Compiler internals can evolve freely; the byte protocol delivered to the runtime needs explicit semantics, ownership, and failure behavior.


Series contents · Previous · Next


If you like this blog or find it useful for you, you are welcome to comment on it. You are also welcome to share this blog, so that more people can participate in it. All the images used in the blog are my original works or AI works, if you want to take it,don't hesitate. Thank you !