ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

mold 仓库中 oneTBB tick_count 计时类详解:墙钟时间戳、interval_t 区间与跨线程安全的性能测量

2026/9/14 22:36:04 拓冰建站 浏览量
mold 仓库中 oneTBB tick_count 计时类详解:墙钟时间戳、interval_t 区间与跨线程安全的性能测量 mold 仓库中 oneTBB tick_count 计时类详解墙钟时间戳、interval_t 区间与跨线程安全的性能测量【免费下载链接】moldmold: A Modern Linker 项目地址: https://gitcode.com/GitHub_Trending/mo/mold本文以 mold 仓库第三方依赖 oneTBB 的官方规范文档 tick_count_cls.rst 为主线结合 tick_count.h 头文件、用户指南 Timing.rst 与仓库内测试用例系统讲解tick_count墙钟时间戳类、tick_count::interval_t时长区间类以及相关非成员运算符的完整 API 与底层实现。读完本文你将掌握用 oneTBB 在并行程序中测量真实运行时间wall clock time的标准姿势理解其跨线程安全与单调时钟steady clock保证的来源并能直接复用仓库内示例与测试的写法。为什么并行程序要测墙钟时间而不是 CPU 时间并行编程的目标是让程序真实时间跑得更快。正如 oneTBB 用户指南 Timing.rst 所强调的衡量并行程序性能时通常关心的是wall clock time墙钟时间即真实流逝时间而非 CPU 时间——因为更好的并行化往往通过动用更多 CPU 来提升总 CPU 时间而并行化的目的是让程序在真实世界里更快。oneTBB 为此提供了tick_count这一简单直观的计时接口。规范文档 timing.rst 将其归类为[timing]模块声明于tick_count.h头文件包含tick_count与tick_count::interval_t两个类。tick_count绝对墙钟时间戳概念与接口tick_count是一个绝对墙钟时间戳absolute wall clock timestamp。两个tick_count对象相减即可得到墙钟时长tick_count::interval_t后者可以转换为秒。规范文档给出的公开接口如下namespace oneapi { namespace tbb { class tick_count { public: class interval_t; tick_count(); tick_count( const tick_count ); ~tick_count(); tick_count operator( const tick_count ); static tick_count now(); static double resolution(); }; } // namespace tbb } // namespace oneapi各成员的含义成员说明tick_count()构造一个时间戳未指定的tick_count对象。注意未指定意味着默认构造出的对象没有有意义的时刻不能参与时间差计算tick_count( const tick_count )拷贝构造复制给定tick_count的时间戳~tick_count()析构函数tick_count operator( const tick_count )将一个tick_count的时间戳赋值给另一个static tick_count now()返回表示当前墙钟时间戳的tick_count对象static double resolution()返回tick_count所用时钟的分辨率单位为秒源码实现时钟的选择与单调性保证头文件 oneapi/tbb/tick_count.h 中tick_count实际定义于tbb::detail::d1内部命名空间再通过inline namespace v1与命名空间注入机制见头文件对detail/_namespace_injection.h的引用对外暴露为oneapi::tbb::tick_counttbb::tick_count亦可使用仓库同时提供兼容头 tbb/tick_count.h。其时钟类型通过编译期条件选择using clock_type typename std::conditionalstd::chrono::high_resolution_clock::is_steady, std::chrono::high_resolution_clock, std::chrono::steady_clock::type;这一选择的关键在于is_steady单调性保证若std::chrono::high_resolution_clock是单调时钟则使用之否则回退到std::chrono::steady_clock。steady_clock的特点是时间只增不减、不受系统时间调整影响——这正是测量代码耗时而非读取墙上日历时间所必需的。now()的实现即return clock_type::now();resolution()则返回interval_t::period::num / interval_t::period::den即所选时钟每个 tick 对应的秒数static double resolution() { return static_castdouble(interval_t::period::num) / interval_t::period::den; }仓库测试 test_tick_count.cpp 直接验证了这一保证TEST_CASE(Clock in tbb::tick_count should be steady) { CHECK_EQ(tbb::tick_count::clock_type::is_steady, true); }tick_count::interval_t墙钟时长区间概念与接口tick_count::interval_t表示一段墙钟时长wall clock duration。规范文档给出的接口namespace oneapi { namespace tbb { class tick_count::interval_t { public: interval_t(); explicit interval_t( double ); ~interval_t(); interval_t operator( const interval_t ); interval_t operator( const interval_t ); interval_t operator-( const interval_t ); double seconds() const; }; } // namespace tbb } // namespace oneapi各成员的含义成员说明interval_t()构造表示零时长的interval_texplicit interval_t( double )构造表示指定秒数的interval_t注意explicit禁止隐式转换~interval_t()析构函数interval_t operator( const interval_t )赋值另一个interval_t的时长interval_t operator( const interval_t )增加给定的时长返回*thisinterval_t operator-( const interval_t )减少给定的时长返回*thisdouble seconds() const返回以秒计量的时长源码实现基于 std::chrono::duration从源码看interval_t直接公有继承自所选时钟的duration类型即std::chrono::duration因此天然拥有 chrono 的时长语义class interval_t : public clock_type::duration { public: interval_t() : clock_type::duration(clock_type::duration::zero()) {} explicit interval_t( double sec ) : clock_type::duration(std::chrono::duration_castclock_type::duration(std::chrono::durationdouble(sec))) {} double seconds() const { return std::chrono::duration_caststd::chrono::durationdouble(*this).count(); } ... };这里有两个值得注意的实现细节双精度秒与 tick 之间的换算存在截断explicit interval_t(double)通过duration_cast把double秒转换为时钟的 tick 计数因此会向零截断。规范符合性测试 conformance_tick_count.cpp 专门验证了这一语义time always truncates并断言my_time - interval_time tick_count::resolution()截断损失小于一个时钟分辨率。seconds()反向转换把 tick 计数再duration_cast成double秒后取count()得到以秒为单位的浮点时长。非成员运算符时间戳与区间的算术规范文档定义了三组非成员二元运算对应[timing.tick_count.nonmember]节oneapi::tbb::tick_count::interval_t operator-( const oneapi::tbb::tick_count, const oneapi::tbb::tick_count ); oneapi::tbb::tick_count::interval_t operator( const oneapi::tbb::tick_count::interval_t, const oneapi::tbb::tick_count::interval_t ); oneapi::tbb::tick_count::interval_t operator-( const oneapi::tbb::tick_count::interval_t, const oneapi::tbb::tick_count::interval_t );运算符语义interval_t operator-( tick_count, tick_count )返回两个墙钟时间戳之间的时长后减前interval_t operator( interval_t, interval_t )返回两个区间之和interval_t operator-( interval_t, interval_t )返回两个区间之差规范特别指出这些函数的定义命名空间未指定unspecified只要它们能在tick_count与interval_t对象的二元运算表达式中被正常使用即可实现可以将其定义在某个内部命名空间中并把oneapi::tbb::tick_count定义为一个类型别名使非成员函数仅能通过实参依赖查找ADLargument-dependent lookup被找到。仓库源码正是如此实现的三个运算符在 tick_count.h 中均以类内friend函数形式声明与定义例如friend interval_t operator-( const tick_count t1, const tick_count t0 ) { return tick_count::interval_t(t1.my_time_point - t0.my_time_point); } friend interval_t operator( const interval_t i, const interval_t j ) { return interval_t(std::chrono::operator(i, j)); } friend interval_t operator-( const interval_t i, const interval_t j ) { return interval_t(std::chrono::operator-(i, j)); }friend函数不属于类作用域、只能通过 ADL 找到恰好印证了规范中命名空间未指定、依赖 ADL的设计意图。实战完整的性能测量代码最小计时示例用户指南 Timing.rst 给出的最小用法是记录开始时刻 → 执行工作 → 记录结束时刻 → 相减转秒#include oneapi/tbb/tick_count.h #include cstdio tick_count t0 tick_count::now(); // ... do some work ... tick_count t1 tick_count::now(); printf(work took %g seconds\n, (t1 - t0).seconds());注意(t1 - t0)的结果类型是tick_count::interval_t其.seconds()返回double配合%g可直接打印。区间算术示例结合interval_t的运算能力还可以对多次测量结果做累加与合并#include oneapi/tbb/tick_count.h using interval_t oneapi::tbb::tick_count::interval_t; oneapi::tbb::tick_count t0 oneapi::tbb::tick_count::now(); // ... phase A ... oneapi::tbb::tick_count t1 oneapi::tbb::tick_count::now(); // ... phase B ... oneapi::tbb::tick_count t2 oneapi::tbb::tick_count::now(); interval_t phaseA t1 - t0; interval_t phaseB t2 - t1; interval_t total phaseA phaseB; // 区间相加 double elapsed total.seconds(); // 总耗时秒 interval_t zero; // 零时长 zero phaseA; // 累加 zero - phaseB; // 累减仓库示例中的真实用法primes/main.cpp在main开头记录mainBeginMark oneapi::tbb::tick_count::now()程序结束时用(oneapi::tbb::tick_count::now() - mainBeginMark).seconds()报告总耗时game_of_life/Evolution.cppSequentialEvolution::Run与ParallelEvolution::Run分别在串行/并行演化循环中反复用tick_count::now()取样累加(t1 - t0).seconds()得到串行与并行版本各自的累计执行时间用于公平对比两种实现的性能。测试验证跨线程安全与算术正确性tick_count的规范符合性在仓库测试中有充分覆盖功能测试 test_tick_count.cppClock in tbb::tick_count should be steady断言时钟单调Subtraction of equal tick_counts should not throw相等时间戳相减不抛异常Test for subtracting calls to tick_count from different threads用SpinBarrier同步多个线程各线程独立记录now()后相减断言(end_time - start_time).seconds() 0——实证了跨线程创建的时间戳可以安全相减。规范符合性测试 conformance_tick_count.cpp默认构造不产生编译错误相等时间戳相减seconds() 0后续时间戳与先前时间戳之差大于 0时间递增运算符正确性验证(i j).seconds() k.seconds()、(k - j).seconds() i.seconds()、/-的累加累减语义误差在1e-10秒内interval_t从double秒构造后的截断语义见上文源码实现部分。使用注意事项跨线程安全与某些计时接口不同tick_count保证跨线程安全——在不同线程上创建的时间戳可以安全相减用户指南 Timing.rst 与测试用例均确认了这一点。分辨率跨平台不一致tick_count的分辨率对应平台上在同一进程内跨线程有效的最高精度计时服务。由于 CPU 计时器寄存器如 TSC在部分平台上跨线程无效因此tick_count的分辨率无法保证跨平台一致用户指南原文说明。若需获取本机分辨率调用tick_count::resolution()。默认构造的时间戳未指定tick_count()构造的对象没有确定的时刻直接对其做减法得到的结果无意义应始终用tick_count::now()获取真实时间戳。Linux 链接说明用户指南历史版本提示在 Linux 上使用oneapi::tbb::tick_count时可能需要为链接命令添加-lrt该说明对应早期 glibc 的 DSO 隐式链接变化是 oneTBB 文档中的历史性注意事项。头文件引入方式规范文档声明接口位于tick_count.h仓库内oneapi/tbb/tick_count.h为规范命名空间头文件兼容头tbb/tick_count.h亦被测试广泛使用如 test_tick_count.cpp 通过#include tbb/tick_count.h引入。小结tick_count与tick_count::interval_t构成了 oneTBB 中面向并行程序性能测量的最小而完整的计时 APInow()采样绝对墙钟时刻、相减得到区间、seconds()转成秒配合同样基于std::chrono::duration的区间算术、-、、-可灵活组合测量结果。其单调时钟的编译期选择与 friend/ADL 的运算符设计在 oneapi/tbb/tick_count.h 中均有清晰的源码印证跨线程安全、递增性与截断语义则由 test_tick_count.cpp 和 conformance_tick_count.cpp 双重重保障。在 mold 仓库的 oneTBB 第三方依赖中这套计时 API 可直接用于并行性能对比与基准测量场景。【免费下载链接】moldmold: A Modern Linker 项目地址: https://gitcode.com/GitHub_Trending/mo/mold创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考