代码结构与构建

本章介绍 upb 的目录布局、port 移植/优化层(所有性能宏的集中地)、生成代码如何接入快核心,以及 Bazel / CMake / amalgamation 三种构建形态。port 层是后续每一章都会反复引用的基础,值得先读熟。

1. 目录布局

upb 的目录即分层(详见 第 2 章)。按代码量排序的主要目录:

目录 角色 量级
reflection/ 反射层:DefPool、各种 *_def、descriptor 最大(含生成的 descriptor 代码)
wire/ 二进制编解码 + decode_fast/ fasttable ~7.5k 行
message/ 消息表示、访问器、array、map ~6.6k 行
json/ JSON 编解码 ~2.5k 行
mini_table/ upb_MiniTable 运行时布局表 ~2.5k 行
mini_descriptor/ 从紧凑串重建 MiniTable ~2.2k 行
mem/ arena / alloc 内存模型 ~1.6k 行
hash/ 开放寻址哈希表 ~1.4k 行
util/ 基于反射的高层工具 ~1.3k 行
text/ 文本序列化 / debug_string ~1k 行
base/ status、常量、string_view ~0.5k 行
port/ 纯预处理器移植/优化层 ~0.5k 行
lex/ 数字 / unicode 转换 ~0.4k 行

一个普遍约定:每个目录里 internal/ 子目录放内部头文件——真正的结构体定义、内联热路径,通常带 UPB_PRIVATE 命名保护(见下文)。公共头只暴露 API 声明。

2. port 层:所有优化宏的集中地

port/def.inc 是理解 upb 性能的”词典”。它不是普通头文件(扩展名 .inc),而是被每个翻译单元在最后包含、再由 port/undef.inc 在头文件结尾全部 #undef,以免污染用户命名空间(约定见 port/def.inc:8-28,清理见 undef.inc:8)。

2.1 能力探测

所有优化都建立在”编译器真的支持”之上,先有探测宏再有应用:

2.2 内联控制

展开 用途
UPB_INLINE 尽量内联(def.inc:140-147 普通内联
UPB_FORCEINLINE __inline__ __attribute__((always_inline)) staticdef.inc:281 强制内联热路径
UPB_NOINLINE __attribute__((noinline))def.inc:282 把冷/回落代码踢出热函数

把冷路径标 UPB_NOINLINE(例如解码器的回落函数)是一种反复出现的手法:热函数体积小、留在 i-cache 里,罕见分支被分到别处。

2.3 分支与不可达提示

  • UPB_LIKELY / UPB_UNLIKELY = __builtin_expect(...,1/0)def.inc:265-269)。
  • UPB_UNPREDICTABLE = __builtin_expect_with_probability(...,0.5)def.inc:272-277),用在二分查找里故意压制分支预测mini_table/internal/message.h:168)。
  • UPB_ASSUME——release 下展开为 if(!expr) __builtin_unreachable()def.inc:321-335),让优化器把不变量传播下去(例如解码偏移掩码、消息 size 已对齐);debug 下退化为 assert
  • UPB_ASSERT——release 下完全编译掉,但仍对表达式做类型检查(def.inc:348-354)。

2.4 对齐

UPB_ALIGN_UP / DOWN / MALLOCdef.inc:209-211),UPB_ALIGN_OF(C23/C11/GNU/MSVC 级联,def.inc:213-230)。UPB_MALLOC_ALIGN 普通为 8,HWASAN 下为 16(def.inc:179:182)。arena 的对齐分配(第 4 章)就建立在这些之上。

2.5 命名保护:UPB_PRIVATEUPB_ONLYBITS

  • UPB_PRIVATE(x) 把名字改写成 x_dont_copy_me__upb_internal_use_onlydef.inc:423),让”公开”结构体成员无法被随意使用。
  • UPB_ONLYBITS(x) 默认等于 UPB_PRIVATE(x),但定义了 UPB_ALLOW_PRIVATE_ACCESS__FOR_BITS_ONLY 时变回原名(def.inc:425-429)——让生成代码/位布局代码够得着成员,普通用户够不着。两者贯穿 MiniTable 结构(mini_table/internal/message.h:72-83)。

2.6 fasttable / musttail —— 核心速度开关

这是 upb 最关键的一组宏(详见 第 7 章):

  • UPB_MUSTTAIL = __attribute__((musttail))(有就用,否则空,def.inc:433-437)——保证尾调用,让解析链编译成一连串寄存器传参的尾跳转。
  • UPB_PRESERVE_MOST / UPB_PRESERVE_NONEdef.inc:439-449)——自定义调用约定,把解析参数留在寄存器里。
  • UPB_FASTTABLE_SUPPORTED——要求 x86-64 或 aarch64-LE 支持 preserve_none 支持 musttaildef.inc:474-479)。
  • UPB_FASTTABLE——只有显式 -DUPB_ENABLE_FASTTABLE(不支持就报错)或 UPB_TRY_ENABLE_FASTTABLE(尽力而为)时才为 1,否则为 0(def.inc:484-496)。
  • UPB_FASTTABLE_INIT(...) / UPB_FASTTABLE_MASK(mask)——fasttable 关闭时展开成空/-1,让生成的 MiniTable 优雅降级def.inc:500-506)。MiniTable 里的 fasttable[] 柔性数组也只在 UPB_FASTTABLE 时才存在(mini_table/internal/message.h:89-93)。

2.7 其他值得一提

  • UPB_SIZE(s32,s64)——按指针宽度选值(def.inc:107-111),生成代码里的偏移到处用它,让同一份源码在 32/64 位都正确。
  • UPB_LINKARR_*——用链接器数组替代 main 之前的注册(def.inc:547-650),支撑 README 的 “no global state”,分 ELF / Mach-O / MSVC 三套实现。
  • UPB_ATOMIC(T)——映射到 C11 _Atomic(T) / MSVC volatile T / 无线程构建的裸 Tdef.inc:413-417),arena 融合的原子操作建立在此(第 4 章)。

3. 生成代码如何接入

每个生成文件都包含同一个入口头 generated_code_support.h。它和 port 层做了一个刻意的两步舞(generated_code_support.h:11-21):先包含 port/def.inc,若 UPB_FASTTABLE 打开就定义 UPB_INCLUDE_FAST_DECODE,再包含 port/undef.inc——这样后面才能按需把快速解码器拉进来(:44-46),而不至于让两个 .inc 互相递归包含。然后它 export 出生成代码需要的全部快核心头:base/upcast、message 访问器/array/map、mini_descriptor/decode、所有 mini_table 头、wire decode/encode(:23-47)。注意它一个反射头都不拉,再次印证快核心/反射的切分。

生成代码的接入方式:

  • .upb_minitable.c:包含 generated_code_support.h 和对应 .upb_minitable.h,发射静态的 upb_MiniTableField 数组和 upb_MiniTable 结构,用到 UPB_SIZEUPB_PRIVATEUPB_FASTTABLE_MASKUPB_FASTTABLE_INITdescriptor.upb_minitable.c:9-39)。fasttable 条目直接引用形如 upb_DecodeFast_Message_Repeated_Tag1Byte 的解析函数(:36-37,以及 UPB_FASTTABLE_INIT:74-83)。
  • .upb.h:包含 generated_code_support.h + minitable 头,定义不透明的消息结构({ upb_Message base; })和内联的类型化访问器(descriptor.upb.h:12-23)。

4. 构建系统

4.1 Bazel(主构建)

顶层 BUILD 定义各层库以及 codegen 支持库(如 generated_code_supportBUILD:109)。fasttable 是一个 bool_flagBUILD:46-50),限定 64 位(BUILD:59-79);打开时通过 copts 注入 -DUPB_ENABLE_FASTTABLECPPOPTSbazel/copts.bzl:47COPTS:55),并条件链接 decode_fast:field_parsersBUILD:129-136)。各语言的 proto 规则在 bazel/upb_proto_library.bzlupb_c_proto_library.bzlupb_minitable_proto_library.bzlupb_proto_reflection_library.bzlbazel/upb_proto_library.bzl:13-37)。

4.2 CMake 支持

CMake 支持是从 Bazel 生成的。cmake/ 下的 staleness_test 宏校验签入的生成文件(如 reflection/cmake/ 下的 .upb.h / .upb_minitable.c)与生成器产物一致,带 --fix 模式(cmake/build_defs.bzl:11-40)。

4.3 Amalgamation(单文件合并)

amalgamation 把所有源码合并成一对 .h + .cbazel/private/oss/amalgamate.py 遍历 #include "..." 行并把每个文件内联一次(amalgamate.py:37-60)。upb_amalgamation Bazel 规则收集 srcs(丢弃 .hpp)后运行合并器(amalgamation.bzl:38-60)。顶层 BUILD 提供 :amalgamation:php_amalgamation:ruby_amalgamationBUILD:200:255:310)——这就是 PHP / Ruby 把 upb 作为单文件随扩展分发的方式。

5. 本章小结

要点 出处
def.inc 内部约定 + 全部 undef port/def.inc:8-28undef.inc:8
UPB_FORCEINLINE / UPB_NOINLINE def.inc:281-282
UPB_LIKELY/UNLIKELY / UPB_UNPREDICTABLE def.inc:265-277
UPB_ASSUME / UPB_ASSERT def.inc:321-354
UPB_PRIVATE / UPB_ONLYBITS def.inc:423-429
UPB_MUSTTAIL / UPB_PRESERVE_NONE def.inc:433-449
UPB_FASTTABLE_SUPPORTED / UPB_FASTTABLE def.inc:474-496
UPB_FASTTABLE_INIT / MASK 降级 def.inc:500-506
UPB_SIZE / UPB_LINKARR / UPB_ATOMIC def.inc:107-111:547-650:413-417
generated_code_support.h 两步舞 + 只导出快核心 generated_code_support.h:11-47
生成的 .upb_minitable.c 用 FASTTABLE 宏 descriptor.upb_minitable.c:9-39
fasttable bool_flag + 64 位限定 + copts BUILD:46-79copts.bzl:47-54
amalgamation 合并器 amalgamate.py:37-60

本站访问量

This site uses Just the Docs, a documentation theme for Jekyll.