- By刘立博
- 2021-01-08 23:40:29
- 4222人已阅读
关于时间分片的栗子
假设CPU时间片为50毫秒,有A、B两个线程并行执行。线程A在某个时间分片结尾阻塞60毫秒后,实际在那个时间点才能真正被唤醒呢?理论上在110毫秒的时间点会被唤醒,但因为110毫秒正处于线程B的时间片,所以实际会在150毫秒的时间点线程A才会被唤醒,比我们的预期慢了40毫秒。
windows操作系统SLEEP精度测试
我们使用C++库中的this_thread::sleep_for方法进行精度测试,假设每休眠20毫秒FPS加1,那么FPS在1秒后理论应该是50,但实际结果如何呢?
测试代码
clock_t begin = clock();
int fps = 0;
for (;;)
{
++fps;
this_thread::sleep_for(20ms);
if ((clock() - begin) / (CLOCKS_PER_SEC / 1000) > 1000)
{
cout << "THREAD SLEEP FPS = " << fps << endl;
break;
}
}
测试结果
FPS仅为32,为什么会有这么大的误差呢?这是因为windows线程调度的最小时间片是3个量子,在单核/单处理器系统上3个量子等于10毫秒,在多核/多处理器系统上3个量子等于15毫秒。也就是说虽然我们休眠20毫秒,但往往需要30毫秒才能被真正唤醒。
提升sleep精度
由于在多核处理器每个量子约等于5毫秒,所以要提升sleep的精度,就需要提升量子的精度。我们可以使用WINDOWS API中的timeBeginPeriod(1)设置时钟的最小周期为1毫秒,这样一个量子约等于1毫秒,那么最小时间片也就成了3毫秒。
测试代码
#include "windows.h"
#include "MMSystem.h"
#pragma comment(lib, "winmm.lib")
//设置时钟精度
timeBeginPeriod(1);
begin = clock();
fps = 0;
for (;;)
{
++fps;
this_thread::sleep_for(20ms);
if ((clock() - begin) / (CLOCKS_PER_SEC / 1000) > 1000)
{
cout << "THREAD SLEEP WITH timeBeginPeriod(1) FPS = " << fps << endl;
break;
}
}
//恢复时钟精度
timeEndPeriod(1);
测试结果
可见,FPS提升为49,接近理论值。但过小的时间片会造成线程切换过于频繁带来不必要的性能开销,所以在不需要高时钟精度时,需要调用timeEndPeriod()将时钟精度恢复为默认值。