如何测量互斥量在被获取之前等待的时间?
How to measure the time a mutex waits before being acquired?
我不确定这个问题是否容易理解,所以我将从我想做的(错误的)代码开始。
...
{
Time start = getCurrentTime();
scoped_lock<MutexType> lock(mutex);
Time lockWait = getCurrentTime() - start;
// Protected section
...
}
此代码不起作用,因为无法保证线程不会在开始时间保存指令和互斥锁之间的某处被抢占,在这种情况下,计算出的 lockWait 将是错误的(高估)。
我想不出任何解决这个问题的办法。我觉得我需要在多线程低级机制中动手,但我不知道该怎么做。您有任何解决方案或指示吗?
显然,我对等待时间很感兴趣,但我也想知道锁是否有争议,我不确定我能否仅通过时间测量获得。如果没有,我如何获得相关信息?
附言。我已经看到存在一些提供一些测量的工具,例如 mutrace 甚至 Walgrind,但不幸的是,我的测量需要集成到应用程序源代码中。
简答:所有时间都是估计值,因此无法按照您想要的方式精确测量。
长答案:如果你想做的是测量获取无竞争锁的速度,那么准确的方法是只计算模拟器中的指令周期。然而,次优的是 运行 测试数千次(以便将抢占的影响降到最低)然后取平均值。 (真的,抢占无关紧要,因为所有用户模式代码都受制于它。你无法在用户模式下阻止抢占,并且在大量样本中时序效应将趋于零)就是这样几乎所有计时工具都可以。您可以通过 运行ning 在轻负载的多 CPU 系统上最小化抢占的可能性(从而改进估计)。
相反,如果您对真实的程序指标感兴趣(例如,还有可能对锁进行竞争的情况),则同样的建议适用。
我对@bleater 对你的第一个问题的回答投了赞成票。一个小建议是,在某些情况下,您可能更想知道 minimum(或者更有趣的是 minimum above floor)而不是平均。或者它可能是 最大值 或某个百分位数。哪个更有意义取决于你的目的。
关于你的第二个问题:求锁被竞争的次数,你可以使用原子计数器。
用法几乎与您测量时间的方式完全相同:在尝试获取锁之前递增 并保存结果值,并在离开范围时递减。
只要此值大于 1,就会发生争用。
请记住,C++ std::atomic<T>
不强制其库实现使用低开销 CPU 提供的原子指令。因此,您可能需要查看生成的反汇编,以确保它使用的是低开销 CPU 指令。
低级计时需要非常小心才能获得正确的结果。如果您使用 TSC (RDTSC) 进行 CPU 级时间测量,您可能还需要:
- 禁用 SpeedStep(英特尔 EIST)
- 禁用 TurboBoost
- 确保您的 CPU 和平台支持不变的 TSC
- 最后,如果发现一个线程已经 preempted and then migrated to another CPU core,则过滤不良时序结果,因为 TSC 读数不能保证 CPU 内核之间一致,即使非常小心.
(毕竟核心之间存在物理距离,电信号传播速度比光慢...)
最后,结果(无论是比赛计数、等待时间还是来自每个线程的原始时间戳跟踪)需要以某种方式存储,最好以某种形式的线程本地存储存储以防止缓存争用。同样,为了确保正确的结果,必须查看低级代码并测量开销以确保它做正确的事情。
我不确定这个问题是否容易理解,所以我将从我想做的(错误的)代码开始。
...
{
Time start = getCurrentTime();
scoped_lock<MutexType> lock(mutex);
Time lockWait = getCurrentTime() - start;
// Protected section
...
}
此代码不起作用,因为无法保证线程不会在开始时间保存指令和互斥锁之间的某处被抢占,在这种情况下,计算出的 lockWait 将是错误的(高估)。
我想不出任何解决这个问题的办法。我觉得我需要在多线程低级机制中动手,但我不知道该怎么做。您有任何解决方案或指示吗?
显然,我对等待时间很感兴趣,但我也想知道锁是否有争议,我不确定我能否仅通过时间测量获得。如果没有,我如何获得相关信息?
附言。我已经看到存在一些提供一些测量的工具,例如 mutrace 甚至 Walgrind,但不幸的是,我的测量需要集成到应用程序源代码中。
简答:所有时间都是估计值,因此无法按照您想要的方式精确测量。
长答案:如果你想做的是测量获取无竞争锁的速度,那么准确的方法是只计算模拟器中的指令周期。然而,次优的是 运行 测试数千次(以便将抢占的影响降到最低)然后取平均值。 (真的,抢占无关紧要,因为所有用户模式代码都受制于它。你无法在用户模式下阻止抢占,并且在大量样本中时序效应将趋于零)就是这样几乎所有计时工具都可以。您可以通过 运行ning 在轻负载的多 CPU 系统上最小化抢占的可能性(从而改进估计)。
相反,如果您对真实的程序指标感兴趣(例如,还有可能对锁进行竞争的情况),则同样的建议适用。
我对@bleater 对你的第一个问题的回答投了赞成票。一个小建议是,在某些情况下,您可能更想知道 minimum(或者更有趣的是 minimum above floor)而不是平均。或者它可能是 最大值 或某个百分位数。哪个更有意义取决于你的目的。
关于你的第二个问题:求锁被竞争的次数,你可以使用原子计数器。
用法几乎与您测量时间的方式完全相同:在尝试获取锁之前递增 并保存结果值,并在离开范围时递减。
只要此值大于 1,就会发生争用。
请记住,C++ std::atomic<T>
不强制其库实现使用低开销 CPU 提供的原子指令。因此,您可能需要查看生成的反汇编,以确保它使用的是低开销 CPU 指令。
低级计时需要非常小心才能获得正确的结果。如果您使用 TSC (RDTSC) 进行 CPU 级时间测量,您可能还需要:
- 禁用 SpeedStep(英特尔 EIST)
- 禁用 TurboBoost
- 确保您的 CPU 和平台支持不变的 TSC
- 最后,如果发现一个线程已经 preempted and then migrated to another CPU core,则过滤不良时序结果,因为 TSC 读数不能保证 CPU 内核之间一致,即使非常小心.
(毕竟核心之间存在物理距离,电信号传播速度比光慢...)
最后,结果(无论是比赛计数、等待时间还是来自每个线程的原始时间戳跟踪)需要以某种方式存储,最好以某种形式的线程本地存储存储以防止缓存争用。同样,为了确保正确的结果,必须查看低级代码并测量开销以确保它做正确的事情。