尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

oneTBB 并行编程核心概念解读:以 mold 链接器中的实际应用为例

oneTBB 并行编程核心概念解读:以 mold 链接器中的实际应用为例 oneTBB 并行编程核心概念解读以 mold 链接器中的实际应用为例【免费下载链接】moldmold: A Modern Linker 项目地址: https://gitcode.com/GitHub_Trending/mo/mold导读本文基于 mold 仓库内嵌的 oneTBBoneAPI Threading Building Blocks官方文档third-party/tbb/doc/main/intro/intro_os.rst系统讲解 oneTBB 并行编程模型的核心设计理念——任务task驱动的逻辑并行、嵌套并行与泛型编程接口。同时结合 mold 链接器的真实源码展示这一并行库在工业级构建工具中的落地方式从构建期集成、头文件引入到链接各阶段的并行化实现。读完本文你将理解 oneTBB 指定任务而非线程 的编程哲学并能从 mold 的源码中看到parallel_for、parallel_for_each、task_arena、task_group与global_control的实际用法。一、oneTBB 是什么根据 intro_os.rstoneTBB 是一个支持可扩展并行编程scalable parallel programming的 C 库其核心特征可以概括为使用标准 ISO C 编写不需要特殊语言或特殊编译器面向数据并行data parallel编程强调可扩展性完整支持嵌套并行可以用较小的并行组件构建更大的并行组件指定任务tasks而非线程threads由库将任务高效地映射到线程上大量接口采用泛型编程接口由对类型的需求定义而非具体的类型要求 C11 及以上标准的编译器支持。其设计目标非常明确让开发者指定并行性比使用裸线程raw threads方便得多同时还能提升性能。oneTBB 并不替代操作系统线程而是作为线程之上的调度与抽象层自动把逻辑并行映射到物理线程资源上。与 mold 的关系mold 是一个用 C 编写的高性能链接器它对多线程并行有着强烈的需求——链接过程的许多阶段读取输入文件、符号解析、重定位、垃圾回收、ICF 等天然适合并行化。因此 mold 将 oneTBB 作为强制依赖嵌入仓库库源码位于third-party/tbb/目录下顶层 CMakeLists.txt 中明确注释 TBB (OneTBB or Intel TBB) is a high-level threading library. Use of this library is mandatory.TBB 是高层线程库使用它是强制的。这是理解后文任务驱动理念的最佳落地案例mold 没有直接编写线程管理代码而是把大量循环交给 oneTBB 的并行算法去调度。二、核心设计理念一指定任务而非线程原文中最重要的论断是To use the library, you specify tasks, not threads, and let the library map tasks onto threads in an efficient manner. 使用该库时你指定的是任务而不是线程并让库以高效的方式把任务映射到线程上。大多数线程库要求程序员直接创建和管理线程。但线程是贴近硬件的低层、重量级构造直接以线程为单位编程既繁琐又低效还会迫使程序员亲自完成逻辑任务 → 线程的映射。oneTBB 的做法正好相反程序员只描述有多少工作要做、每块工作做什么线程的创建、调度、负载均衡全部由运行时库接管。这一理念在 mold 源码中体现得淋漓尽致。mold 很少显式操作线程而是大量调用 oneTBB 的并行算法模板例如在 src/icf.cc 中统计每个输入文件中符合 ICFIdentical Code Folding合并条件的 section 数量时直接写tbb::parallel_for((i64)0, (i64)ctx.objs.size(), { for (InputSectionE *isec : ctx.objs[i]-sections) { ... if (is_eligible(ctx, *isec)) { eligible; isec-icf_idx 0; indices[i 1]; } ... } });这里 mold 只声明了对每个对象文件 i 执行这段统计逻辑至于在多少个线程上跑、如何切分索引范围、如何做负载均衡全部交给 oneTBB 的调度器决定。同样的模式遍布链接器的各个 passsrc/gc-sections.cc、src/passes.cc、src/output-chunks.cc、src/mapfile.cc、src/gdb-index.cc等文件中都有大量tbb::parallel_for/tbb::parallel_for_each调用。数据并行可扩展性的关键原文强调 oneTBB 侧重可扩展的数据并行编程把程序拆成固定数量的功能块、每块分配一个线程的做法通常扩展性很差因为功能块数量固定而数据并行让多个线程分别处理一个集合的不同部分把集合切得越细就能利用越多的处理器核心性能随处理器数量的增加而提升。mold 的并行化正是数据并行的典型它把对象文件列表ctx.objs和输入 section 集合这类数据集合作为并行单位。例如 src/main.cc 中并行读取输入文件tbb::parallel_for_each(jobs, { ... });每个 job 对应一个输入文件不同 job 之间互不依赖可以安全地在任意数量的线程上并行执行。三、核心设计理念二完整的嵌套并行支持原文明确指出 oneTBB fully supports nested parallelism完整支持嵌套并行因此可以从较小的并行组件构建较大的并行组件。嵌套并行意味着一个并行区域内部可以再开启并行区域且不会因为线程资源耗尽或死锁而失败。oneTBB 的调度器通过工作窃取work stealing等机制保证内层并行任务能被当前空闲的线程承接。从 mold 的源码结构可以推断这种嵌套能力正是 mold 多层 pass 架构得以成立的基础链接器的外层遍历对象文件一层parallel_for_each对每个对象文件内部的 section 列表又可能触发新的并行处理内层parallel_for例如 src/icf.cc 中在 section 集合上再开并行。若运行时不支持嵌套并行这种外层并行套内层并行的写法将难以安全落地。配套文档 Cancellation_and_Nested_Parallelism.rst 进一步说明了嵌套并行与任务取消机制cancellation如何协同工作异常与取消可以在嵌套的任务树中正确传播。四、核心设计理念三泛型编程接口原文将 oneTBB 的接口风格与 C STL 类比STL 的sort模板通过对迭代器的需求随机访问、*i *j定义序关系、swap(*i, *j)可交换元素来约束类型从而能对vector、deque等不同容器生效。oneTBB 采用同样的泛型编程路线接口由对类型的需求定义而非具体的类型因此算法可以适配不同的数据表示数组、STL 容器、迭代器区间、自定义 range 等组件可按需定制保持灵活性的同时不失效率使用门槛低——只需要满足相应命名需求named requirements的类型即可参与并行。parallel_for/parallel_for_each的 lambda 重载形式就是泛型接口的体现mold 在 src/mapfile.cc 中甚至直接对 oneTBB 的 range 类型使用parallel_fortbb::parallel_for(map.range(), [](const typename MapE::range_type range) { ... });这里的MapE::range_type就是 oneTBB 泛型 range 接口可拆分、可遍历的区间抽象的直接使用印证了接口由需求定义的设计——只要类型提供 range 所需的成员能力就能参与并行遍历。五、mold 中的 oneTBB 实战构建集成与高级组件5.1 构建期集成mold 的 CMakeLists.txt 提供了两条 oneTBB 集成路径选项行为MOLD_USE_SYSTEM_TBBOFF默认使用仓库内置的third-party/tbb/源码通过add_subdirectory静态编译并链接进 moldBUILD_SHARED_LIBS OFFMOLD_USE_SYSTEM_TBBON通过find_package(TBB REQUIRED)链接系统安装的libtbb2.so同时构建脚本关闭了 TBB 自带的测试与严格告警TBB_TEST OFF、TBB_STRICT OFF并定义了__TBB_DYNAMIC_LOAD_ENABLED0来禁用动态加载路径保证作为链接器运行时的行为确定性。5.2 引入的头文件mold 在 src/mold.h 中集中引入了 oneTBB 的 6 个头文件基本覆盖了链接器所需的全部并行原语#include tbb/concurrent_hash_map.h #include tbb/concurrent_vector.h #include tbb/global_control.h #include tbb/spin_mutex.h #include tbb/task_arena.h #include tbb/task_group.h它们分别对应 oneTBB 的几个能力维度并发容器concurrent_vector、concurrent_hash_map、并行控制global_control、细粒度锁spin_mutex、任务调度上下文task_arena与任务组task_group。这也是原文档泛型接口 任务调度设计在真实工程中的完整选型。5.3 控制并行度global_controloneTBB 用tbb::global_control在运行时限制全局最大并行度。mold 在 src/main.cc 中读取当前可用并行度并有意将线程数上限限制为 32// mold doesnt scale well with too many threads, so limit it to 32. int n tbb::global_control::active_value( tbb::global_control::max_allowed_parallelism); return std::min(n, 32);随后在 src/main.cc 中通过tbb::global_control对象把这个上限应用到整个链接进程ctx.global_limit.emplace(tbb::global_control::max_allowed_parallelism, get_thread_count(ctx));这体现了 oneTBB 任务由库调度的粒度优势只需设置一个全局并行度上限所有并行算法与任务组都会自动遵守无需逐个循环手工限流。用户也可以通过命令行--thread-count参数覆盖默认线程数。5.4 后台低优先级任务task_arena 与 task_grouponeTBB 的task_arena允许创建独立的任务执行环境可指定线程数与优先级task_group则用于组织一组可并行/可等待的任务。mold 在 src/main.cc 中展示了二者的组合用法——把 .gdb_index 的输入解析放到一个低优先级 arena中异步执行与前台主要 pass 并行推进tbb::task_arena gdb_input_arena(tbb::task_arena::automatic, 1, tbb::task_arena::priority::low); tbb::task_group gdb_task; if (create_gdb_index) gdb_input_arena.execute([] { gdb_task.run([] { read_gdb_index_inputs(ctx); }); });这里用到了tbb::task_arena::automatic自动选择线程数tbb::task_arena::priority::low低优先级避免抢占前台链接 pass 的计算资源gdb_task.run(...)向任务组提交后台任务之后可在合适时机wait。同类手法还出现在 src/main.cc 的 gdb 符号表构建阶段。这正是原文档从较小并行组件构建较大并行组件的工程化注解一个task_group任务内部仍可再调用parallel_for形成多层任务树。5.5 并发容器与同步原语链接器各 pass 之间需要共享结构oneTBB 的并发容器在此发挥作用。从 src/mold.h 可以看到mold 用tbb::concurrent_vector保存链接过程产生的各种池化对象合并后的 section、对象文件、DSO、字符串池、输出 chunk 等多个线程可以安全地并发 push 而无需外部加锁src/mold.h 用tbb::concurrent_hash_map记录未定义符号错误src/mold.h 用tbb::spin_mutex保护临界区。这些组件让 mold 可以放心地让多个并行 pass 共享同一份上下文数据。六、使用前提C11 与编译器要求原文档的.. note::明确指出|full_name| requires C11 standard compiler support. oneTBB 要求支持 C11 标准的编译器。这是一个重要的工程约束使用 oneTBB 的项目必须确保编译器开启 C11或更新标准。mold 本身以现代 C 编写构建时使用 C17/C20 级别完全满足这一前提。若读者要在自己的项目中引入 oneTBB除了确认编译器标准还应留意头文件包含路径应指向 oneTBB 的include/目录mold 通过 CMakeadd_subdirectory自动完成链接时需要提供 TBB 运行时mold 默认静态链接内置版本也可选择系统libtbb2.so调试与发布构建建议使用对应版本的 TBB 库详见配套文档 Debug_Versus_Release_Libraries.rst。七、总结从文档到工业实践回顾整个链路oneTBB 的 Introduction 文档用精炼的篇幅界定了该库的设计哲学——标准 C、任务而非线程、数据并行、嵌套并行、泛型编程而 mold 链接器则用数千行源码验证了这套哲学的工程价值mold 将 oneTBB 设为强制依赖构建期默认静态链接内置版本CMakeLists.txt链接各阶段普遍采用parallel_for/parallel_for_each做数据并行程序员从不直接管理线程src/icf.cc、src/gc-sections.cc 等用global_control统一限制全局并行度上限 32 线程用task_arenatask_group实现低优先级后台任务src/main.cc、src/main.cc用concurrent_vector、concurrent_hash_map、spin_mutex支撑线程间数据共享src/mold.h。对于想在自己的 C 项目中引入 oneTBB 的读者可以遵循相同的模式先用parallel_for/parallel_for_each替换可并行的数据循环再用task_group组织有依赖关系的任务最后通过global_control和task_arena精细控制资源。文档中提到的嵌套并行与泛型接口会在这种渐进式并行化中自然发挥作用。进一步的参考材料可继续阅读同目录下的 Benefits.rstoneTBB 相对裸线程的五大优势与third-party/tbb/doc/main/tbb_userguide/下的用户指南如 Parallelizing_Simple_Loops_os.rst、creating_tasks_with_task_group.rst以获取更多编程模式的细节。【免费下载链接】moldmold: A Modern Linker 项目地址: https://gitcode.com/GitHub_Trending/mo/mold创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表