大端与小端
字节序、网络序与跨平台权重——为何 0x12345678 在内存里两种摆法。
整数 0x12345678 占 4 字节,在内存里从低地址到高地址,可以是 12 34 56 78(大端 Big-Endian)或 78 56 34 12(小端 Little-Endian)。x86、ARM 常见 CPU 都是小端;网络协议、部分文件格式规定大端。读二进制权重、写跨平台格式、解析 magic number 时,字节序搞错会得到荒谬数字。
直觉:从左读还是从右读
大端:人类书写顺序,高位字节在前(像写 1234 先写千位)。小端:低地址存低位,历史原因(Intel 8080 累加器低位先方便进位)。单个 byte 没有 endian 问题;多 byte 整数、float 才有。
定义
对 32 bit 值 0x12345678:
| 地址 | +0 | +1 | +2 | +3 |
|---|---|---|---|---|
| 大端 | 0x12 | 0x34 | 0x56 | 0x78 |
| 小端 | 0x78 | 0x56 | 0x34 | 0x12 |
网络字节序 = 大端。htonl / ntohl 在 x86 上 swap。
float32 按 IEEE 754 bit 模式当 uint32 解释再谈 endian;struct.pack('<f', x) 小端,'>f' 大端。
例子一:Python struct
import struct
n = 0x12345678
print(struct.pack('<I', n).hex()) # 78563412 小端
print(struct.pack('>I', n).hex()) # 12345678 大端
print(struct.unpack('<I', b'\x78\x56\x34\x12')[0]) # 305419896 = 0x12345678例子二:PNG 与 safetensors
PNG signature 固定字节 89 50 4E 47——与 endian 无关(逐字节 magic)。
Safetensors header 是 JSON 长度 + JSON + tensor bytes;JSON 里 shape/dtype,tensor 按 little-endian 原生顺序存(与 PyTorch 默认一致)。在 大端 机器(罕见)上读需 swap——几乎只剩历史 mainframe。
NumPy .npy 文件头含 < 或 | 标记 endian。
与 AI 工程
| 场景 | endian |
|---|---|
PyTorch .pt / .pth | 平台原生(x86 小端) |
| ONNX | protobuf little-endian |
| GGUF | little-endian |
| TCP 传 tensor shape(自定义协议) | 应规定网络序或 little-endian + 文档 |
分布式训练 NCCL 在 homogenous x86/ARM 小端集群上透明;混合 endian 几乎不存在。
位域、异构与 GPU
GPU global memory 与 host 一致(CUDA 小端 x86)。cudaMemcpy 拷 float 数组无需 swap。
手动解析 wire format:
def read_u32_le(data, off):
return data[off] | (data[off+1]<<8) | (data[off+2]<<16) | (data[off+3]<<24)历史:字节序战争
1980 年代 Big-Endian 派(Motorola 68000、网络)与 Little-Endian 派(Intel x86)并存。Jonathan Swift《格列佛游记》鸡蛋大头 vs 小头——endian 词源于此。Today 小端统治 数据中心与桌面;ARM 可 bi-endian 但 practically little。
DMA 与外设:PCIe 设备寄存器有时规定 big-endian 文档;驱动里 le32_to_cpu 转换。
手动 swap 与性能
跨 endian 读 1 GB 文件需 swap 每 2/4/8 字节——纯 CPU bound,~1–2 GB/s 单线程;batch swap 用 SIMD 可加速。Homogeneous 小端集群训练 零 swap 成本——全球化云仍假设 little-endian。
跨语言 FFI 与字节序
Rust u32::from_le_bytes([...])、Go binary.LittleEndian.Uint32、Java ByteBuffer.order(LITTLE_ENDIAN)——FFI 边界 必须显式 endian。C Python struct.pack 与 Rust tensor 通过 numpy memmap 共享时,两边都 little-endian 则 零拷贝;big-endian 主机几乎绝迹于 AI 栈。
Wire format Protobuf / FlatBuffers / Cap'n Proto 均在 schema 层规定 integer endian;gRPC 传 tensor metadata 时 shape 是 varint,payload 是 little-endian float 裸流。
场景实战:读 network pcap 里的整数
Wireshark 显示 TCP payload 里 big-endian 字段(IP 头长度、端口号)。自定义 RPC 传 struct.pack('>I', len) 前缀 + raw tensor bytes(little-endian float)——同一 packet 混两种 endian 约定,靠 schema 文档区分。写跨语言推理网关时 显式规定 每个字段 endian,避免 ARM 服务器读 x86 客户端 blob 乱码。
NumPy np.frombuffer(data, dtype='>f4') vs '<f4' 一行切换——interpretation 层解决,不改变 raw bit。
常见误区
- 「UTF-8 有大小端」——UTF-8 是 byte 序列;UTF-16 有 BOM 标记 LE/BE。
- 「小端 = 反着存 bit」——仍是高位 bit 在 byte 内高位;只是** byte 顺序**反了。
- 「json 文件有 endian」——文本 UTF-8,无数 endian;二进制 tensor 文件才有。
动手试一试
检测本机 endian:
import sys
print(sys.byteorder) # little 或 big
import struct
print(struct.pack('@I', 1) == struct.pack('<I', 1)) # native vs little on x86: True用 hexdump 看 float:
import struct
f = 3.14159
b = struct.pack('<f', f)
print(b.hex(), struct.unpack('<f', b))延伸思考
1980 年代若互联网未统一 little-endian 数据 plane,今日 HuggingFace 上 一个 .bin 文件全球同构 的便利将不存在——每个市场要 endian-specific build。历史路径依赖使 小端成为 AI 事实标准;你在 ARM Mac 上训练的 checkpoint 拷到 x86 Linux 零转换 部署,这是 endian 统一的红利。Wire protocol 设计 时,整数长度字段用 big-endian(网络序)而 float payload 用 little-endian 的混用模式,在 gRPC + raw tensor 网关里仍可见;读 spec 时圈出每个字段的字节序,比背 endian 定义更有用。
补充:常见文件 magic
| 格式 | 头几个 byte(hex) |
|---|---|
| PNG | 89 50 4E 47 |
| JPEG | FF D8 FF |
| GIF | 47 49 46 38 |
| ZIP | 50 4B 03 04 |
| safetensors | JSON len + {" |
用 xxd -l 4 file 比看扩展名可靠——扩展名可改,magic 难伪造(除非刻意 polyglot)。
温故:何时该关心 endian
日常 PyTorch 训练 99.9% 场景不用手改 endian;需要关心的时刻:手写二进制协议、解析 legacy 大端文件、嵌入式 cross-compile、安全 audit 魔数。AI 工程师记住一句:权重与 tensor wire format 默认 little-endian;网络 JSON 无 endian 问题(文本 UTF-8);混合协议自己写文档。
读完带走
小端 统治 AI 栈;网络序大端 只在 legacy 协议。Homogeneous cluster 零 swap 成本。写 binary RPC 时在 schema 显式 endian + dtype,避免 silent 乱码 tensor。
小结
Endian 决定多字节整数/float 在内存中的 byte 顺序。x86/ARM AI 栈几乎全是小端;网络与大端文件格式是例外。读二进制格式认准 magic、文档与 struct 的 </>。写跨语言推理服务时,在 API 文档里固定 byte order 与 dtype,比事后 debug 乱码张量便宜得多。客户端与服务端 homogeneous little-endian 时,tensor payload 可 零拷贝 cast。ONNX protobuf 层仍要核对 TensorProto data_type 字段。
动手作业:打开 nvidia-smi 或活动监视器,对照本章公式估算当前进程的 权重与 KV 占用,误差在一个数量级内即合格。
读 ONNX TensorProto 时核对 raw_data 字节序字段说明。
Wire 协议 写清 integer endian 与 float dtype,比事后 hex dump 调试省一天。
导出 numpy.tobytes(order='C') 默认 little-endian,与 PyTorch 一致。
跨端推理时 schema 文档 应写明:整数用 big-endian 长度前缀、tensor body 用 little-endian float32 裸流——混用是常态,不是异常。
FlatBuffers 等零拷贝格式在 schema 里固定 endian,适合 edge 推理网关。
读 Wikipedia「Endianness」 对照本章 magic number 表,十分钟足够。
JSON 数字 无 endian;JSON 里的 base64 权重 decode 后才是 little-endian tensor。
本章与 网络字节序 的唯一必记点:TCP 端口与 IP 长度字段用 big-endian。
htons 即 host-to-network short,C 网络编程必记。
第一章衔接
本章用 bit 语言 描述 AI 底座;第二章 数学 用 向量与矩阵 重述同一物理过程。两章对照读:bytes 是存储,FLOPs 是计算,带宽是瓶颈——三个量构成工程三角。
下一篇:从比特到程序——编译、链接与一次推理的完整链路。跨平台读写二进制权重时,先确认 byte order 再确认 dtype 宽度,顺序反了会把完全合法的文件读成噪声张量。