Windows select 计时误差
测试:普通线程优先级
-
测试代码
// // [from] uthash/tests/keystat.c // void gettimeofday(struct timeval* p, void* tz /* IGNORED */) { LARGE_INTEGER q; static long long freq; static long long cyg_timer; QueryPerformanceFrequency(&q); freq = q.QuadPart; QueryPerformanceCounter(&q); cyg_timer = q.QuadPart; p->tv_sec = (long)(cyg_timer / freq); p->tv_usec = (long)(((cyg_timer % freq) * 1000000) / freq); }double deviation_sum = 0.0; double deviation_cnt = 0.0; double deviation_max = 0.0; struct timeval lastSent; struct timeval lastPrint; gettimeofday(&lastSent, NULL); while(1) { FD_SET readfd; struct timeval timeout; //... timeout.tv_sec = interval / 1000000; timeout.tv_usec = interval % 1000000; gettimeofday(&lastSent, NULL); int reVal = select(num + 1, &readfd, NULL, NULL, &timeout); gettimeofday(&tv, NULL); if (SOCKET_ERROR == reVal) continue; else if (0 == reVal) { uint32_t diff; if (tv.tv_sec == lastSent.tv_sec) { diff = (uint32_t)(tv.tv_usec - lastSent.tv_usec); } else if (tv.tv_sec > lastSent.tv_sec) { diff = (uint32_t)(tv.tv_sec - lastSent.tv_sec - 1) * 1000000; /* lean 1 sec to tv_usec */ diff = diff + (uint32_t)(1000000/*borrowed from tv_sec*/ + tv.tv_usec - lastSent.tv_usec); } else { printf("gettimeofday err ...\n"); break; } if ((double)diff > deviation_max) deviation_max = diff; deviation_cnt += 1.0; deviation_sum += diff; if (lastSent.tv_sec != lastPrint.tv_sec) { lastPrint.tv_sec = lastSent.tv_sec; printf("%s max:%.2lf avg:%.2lf cnt:%.lf\n", GetCurrentTimestamp(&lastSent), deviation_max, deviation_sum / deviation_cnt, deviation_cnt); } continue; } else { continue;; } }
-
测试结果
test.exe -c 127.0.0.01:7777 -t 100 ### interval == 100 us 1503.000640 max:1164.00 avg:964.83 cnt:882 ... 1506.000451 max:1273.00 avg:964.59 cnt:3954 ^C test.exe -c 127.0.0.01:7777 -t 500 ### interval == 500 us 1332.000614 max:1180.00 avg:964.92 cnt:438 ... 1401.000570 max:1488.00 avg:964.37 cnt:30136 ... 1431.000583 max:1727.00 avg:964.40 cnt:60858 ^C test.exe -c 127.0.0.01:7777 -t 1500 1437.001326 max:2126.00 avg:1942.53 cnt:36 ... 1458.000003 max:2517.00 avg:1938.90 cnt:10788 ^C test.exe -c 127.0.0.01:7777 -t 5000 1512.000066 max:6018.00 avg:5839.33 cnt:58 ... 1523.005303 max:6128.00 avg:5835.96 cnt:1938 ^C test.exe -c 127.0.0.01:7777 -t 10000 1528.001068 max:10972.00 avg:10722.54 cnt:37 ... 1546.002710 max:10981.00 avg:10720.36 cnt:1713 ^C test.exe -c 127.0.0.01:7777 -t 50000 1553.007225 max:50838.00 avg:50761.67 cnt:9 ... 1608.038217 max:50967.00 avg:50738.57 cnt:305 -
结论
定时误差: 900 us ~ 1200 us
测试:最高线程优先级
-
提高优先级
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST); -
测试结果
test.exe -c 127.0.0.01:7777 -t 1000 5323.036629 max:1938.00 avg:1938.00 cnt:1 5324.001369 max:2115.00 avg:1745.90 cnt:549 ... 5344.000173 max:2690.00 avg:1784.91 cnt:11647 -
结论
定时误差: 900 us ~ 1200 us
测试:Sleep(0)循环等待
-
Code
int reVal; if (true && interval < 5000) { while (1) { Sleep(0); gettimeofday(&tv, NULL); if ((tv.tv_sec > lastSent.tv_sec) || (tv.tv_sec == lastSent.tv_sec && tv.tv_usec >= lastSent.tv_usec)) { break; } } reVal = 0; } else { reVal = select(num + 1, &readfd, NULL, NULL, &timeout); gettimeofday(&tv, NULL); } if (SOCKET_ERROR == reVal) continue; -
情形一:最高优先级
test.exe -c 127.0.0.01:7777 -t 500 5506.000000 max:91.00 avg:0.36 cnt:40969 5507.000003 max:107.00 avg:0.37 cnt:246429 5508.000002 max:176.00 avg:0.38 cnt:450611 ... 5525.000001 max:377.00 avg:0.37 cnt:3962238 ... 5530.000002 max:1971.00 avg:0.38 cnt:4991011 -
情形二:普通优先级
test.exe -c 127.0.0.01:7777 -t 500 5640.000003 max:125.00 avg:0.39 cnt:156529 5641.000006 max:125.00 avg:0.38 cnt:363393 5642.000004 max:474.00 avg:0.39 cnt:566785 ... 5647.000000 max:734.00 avg:0.38 cnt:1609190 ... 5703.000025 max:6910.00 avg:0.39 cnt:4893457 -
结论
调度时,线程优先级的影响比较大。对于要求高计时精度通信的场合,高线程优先级的情形下也可能有调度导致延时超过误差值,需要特别处理。

本文探讨了Windows select函数在不同线程优先级下的计时误差问题,通过测试发现无论普通线程优先级还是最高线程优先级,定时误差均在900 us到1200 us之间。即使使用Sleep(0)循环等待,高线程优先级下仍可能出现调度导致的延时超误差,对于高精度计时需求需谨慎处理。

2593

被折叠的 条评论
为什么被折叠?



