在 C++ 中检查单调时钟的滴答声是否可以免费等待?
Is checking the tick of a monotonic clock in C++ wait free?
假设我们有一个非常基本的代码来检查经过的时间间隔
#include <iostream>
#include <chrono>
int main()
{
auto start = std::chrono::steady_clock::now();
// some work
auto end = std::chrono::steady_clock::now();
std::chrono::duration<double> diff = end-start;
std::cout << "The interval is " << diff.count() << "\n";
}
是否每次调用 std::chrono::steady_clock::now()
都会阻塞其他线程?它是否被认为是无锁的,甚至是免费等待的?对 std::chrono::steady_clock::now()
的调用是否会饿死或阻塞任何其他线程?
我最感兴趣的是规范对 std::chrono::steady_clock::now()
的保证。如果它不能保证它是免费等待的,有没有什么办法可以在 MSVC/gcc/clang 上获得保证免费等待的单调时钟的滴答声?
C++ 标准对这个问题保持沉默。
基本原理:每个 OS 都有一些获得单调时间的方法。在硬件级别,这是每个时钟周期的计数器。在 OS 级别,这可能会或可能不会转换为某些方便的单位(例如纳秒)。
<chrono>
解决的问题是 OS API 用于获取此时间度量的方法随着 OS 的移动而不同,有时甚至从一个版本转移到下一个版本。 <chrono>
使底层 API 对 C++ 程序员来说是统一的。不多也不少。
所以要回答这个问题,你真的要降到OS级别的时机API。
那么,让我们看看 std::chrono::steady_clock::now()
在 gcc 中是如何实现的。答案可以在 https://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/src/c%2B%2B11/chrono.cc
找到
steady_clock::now() noexcept
{
#ifdef _GLIBCXX_USE_CLOCK_MONOTONIC
timespec tp;
// -EINVAL, -EFAULT
#ifdef _GLIBCXX_USE_CLOCK_GETTIME_SYSCALL
syscall(SYS_clock_gettime, CLOCK_MONOTONIC, &tp);
#else
clock_gettime(CLOCK_MONOTONIC, &tp);
#endif
return time_point(duration(chrono::seconds(tp.tv_sec)
+ chrono::nanoseconds(tp.tv_nsec)));
#else
return time_point(system_clock::now().time_since_epoch());
#endif
}
从中可以推断出它将工作委托(或更准确地说:可以委托)C 的标准函数clock_gettime
。
它的手册页 https://www.man7.org/linux/man-pages/man3/clock_gettime.3.html has a section "attributes", and there the value of "thread safety" is "MT-safe". This is explained here: https://www.man7.org/linux/man-pages/man7/attributes.7.html
Being MT-Safe does not imply a function is atomic, nor that it uses any of the memory synchronization mechanisms POSIX exposes to users.
换句话说,除了可以在多线程环境中安全调用该函数之外,您无法保证。
在这一点上,您可以理解 gcc/clang 是开源的这一事实,并亲自了解 clock_gettime
的实现,就像我对 steady_clock::now()
所做的那样,但是您对于闭源编译器,我永远不会得到这样的答案,也无法保证 gcc/clang 使用的实现永远不会改变。
但是如果您在无锁算法中使用 C++ 时钟,那么您可能会关注性能。那么,请阅读:
- Does steady_clock have only 10ms resolution on windows/cygwin?(C++ 时钟的某些实现在其时钟分辨率方面曾经“损坏”)
- https://en.wikipedia.org/wiki/Time_Stamp_Counter(为什么在现代多处理器平台上依靠 TSC 寄存器获取当前时间已成为一件不平凡的事情)
如果您的解决方案是多平台的并且基本上与硬件无关,那么使用时钟的代价可能与使用互斥锁进行同步的代价相似甚至更大。我不知道是否是这种情况,但这肯定是您需要考虑的事情。
假设我们有一个非常基本的代码来检查经过的时间间隔
#include <iostream>
#include <chrono>
int main()
{
auto start = std::chrono::steady_clock::now();
// some work
auto end = std::chrono::steady_clock::now();
std::chrono::duration<double> diff = end-start;
std::cout << "The interval is " << diff.count() << "\n";
}
是否每次调用 std::chrono::steady_clock::now()
都会阻塞其他线程?它是否被认为是无锁的,甚至是免费等待的?对 std::chrono::steady_clock::now()
的调用是否会饿死或阻塞任何其他线程?
我最感兴趣的是规范对 std::chrono::steady_clock::now()
的保证。如果它不能保证它是免费等待的,有没有什么办法可以在 MSVC/gcc/clang 上获得保证免费等待的单调时钟的滴答声?
C++ 标准对这个问题保持沉默。
基本原理:每个 OS 都有一些获得单调时间的方法。在硬件级别,这是每个时钟周期的计数器。在 OS 级别,这可能会或可能不会转换为某些方便的单位(例如纳秒)。
<chrono>
解决的问题是 OS API 用于获取此时间度量的方法随着 OS 的移动而不同,有时甚至从一个版本转移到下一个版本。 <chrono>
使底层 API 对 C++ 程序员来说是统一的。不多也不少。
所以要回答这个问题,你真的要降到OS级别的时机API。
那么,让我们看看 std::chrono::steady_clock::now()
在 gcc 中是如何实现的。答案可以在 https://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/src/c%2B%2B11/chrono.cc
steady_clock::now() noexcept
{
#ifdef _GLIBCXX_USE_CLOCK_MONOTONIC
timespec tp;
// -EINVAL, -EFAULT
#ifdef _GLIBCXX_USE_CLOCK_GETTIME_SYSCALL
syscall(SYS_clock_gettime, CLOCK_MONOTONIC, &tp);
#else
clock_gettime(CLOCK_MONOTONIC, &tp);
#endif
return time_point(duration(chrono::seconds(tp.tv_sec)
+ chrono::nanoseconds(tp.tv_nsec)));
#else
return time_point(system_clock::now().time_since_epoch());
#endif
}
从中可以推断出它将工作委托(或更准确地说:可以委托)C 的标准函数clock_gettime
。
它的手册页 https://www.man7.org/linux/man-pages/man3/clock_gettime.3.html has a section "attributes", and there the value of "thread safety" is "MT-safe". This is explained here: https://www.man7.org/linux/man-pages/man7/attributes.7.html
Being MT-Safe does not imply a function is atomic, nor that it uses any of the memory synchronization mechanisms POSIX exposes to users.
换句话说,除了可以在多线程环境中安全调用该函数之外,您无法保证。
在这一点上,您可以理解 gcc/clang 是开源的这一事实,并亲自了解 clock_gettime
的实现,就像我对 steady_clock::now()
所做的那样,但是您对于闭源编译器,我永远不会得到这样的答案,也无法保证 gcc/clang 使用的实现永远不会改变。
但是如果您在无锁算法中使用 C++ 时钟,那么您可能会关注性能。那么,请阅读:
- Does steady_clock have only 10ms resolution on windows/cygwin?(C++ 时钟的某些实现在其时钟分辨率方面曾经“损坏”)
- https://en.wikipedia.org/wiki/Time_Stamp_Counter(为什么在现代多处理器平台上依靠 TSC 寄存器获取当前时间已成为一件不平凡的事情)
如果您的解决方案是多平台的并且基本上与硬件无关,那么使用时钟的代价可能与使用互斥锁进行同步的代价相似甚至更大。我不知道是否是这种情况,但这肯定是您需要考虑的事情。