2010-05-21 61 views
1

更新:在代碼固定的增量計算,仍是問題仍然奇怪的結果,同時測量增量時間在Linux上

夥計們,請你解釋一下爲什麼我使用正從時間非常奇怪的結果,以時間的下面的代碼:

#include <unistd.h> 
#include <sys/time.h> 
#include <stdio.h> 

int main() 
{ 
    struct timeval start, end; 
    long mtime1, mtime2, diff; 

    while(1) 
    { 
    gettimeofday(&start, NULL); 

    usleep(2000); 

    gettimeofday(&end, NULL); 

    mtime1 = (start.tv_sec * 1000 + start.tv_usec/1000.0); 
    mtime2 = (end.tv_sec * 1000 + end.tv_usec/1000.0); 

    diff = mtime2 - mtime1; 
    if(diff > 10) 
     printf("WTF: %ld\n", diff); 
    } 

    return 0; 
} 

(你可以編譯和運行它:的gcc -o test.c的出-lrt & & ./out

什麼我遇到了幾乎是每秒或DIFF變量的零星大值,甚至更多的時候,e.g:

$ gcc test.c -o out -lrt && ./out 
WTF: 14 
WTF: 11 
WTF: 11 
WTF: 11 
WTF: 14 
WTF: 13 
WTF: 13 
WTF: 11 
WTF: 16 

這怎麼可能呢?責怪操作系統?它是否做了太多的上下文切換?但我的箱子空閒( 平均負載:0.02,0.02,0.3)。

這裏是我的Linux內核版本:

$ uname -a 
Linux kurluka 2.6.31-21-generiC#59-Ubuntu SMP Wed Mar 24 07:28:56 UTC 2010 i686 GNU/Linux 
+0

不管它是什麼,它不會發生在我的電腦上:/(Linux 2.6.33-gentoo#2 SMP x86_64) – 2010-05-21 13:11:29

+0

確定嗎?嘗試將「if(diff> 10)」行更改爲「if(diff> 3)」 – pachanga 2010-05-21 13:51:29

回答

4

睡眠功能只是確保你至少睡了一定量的時間。由於Linux不是一個實時操作系統,你不能確定它只會睡眠所需的時間。這是一個問題,因爲你不能指望這個值。正如你指出的那樣,睡覺時間真的很大。

Linux調度程序不能保證。有了實時操作系統,你可以得到它。

你的公式在某種程度上是錯誤的,但我認爲這不能成爲你有這麼大的睡眠時間的原因。我檢查了兩個公式在這個片段中,和我有同樣的結果:

#include <unistd.h> 
#include <sys/time.h> 
#include <time.h> 
#include <stdio.h> 

int main() 
{ 
    struct timeval start, end; 
    long mtime, mtime2, start_time, end_time, seconds, useconds; 

    while(1) 
    { 
    gettimeofday(&start, NULL); 

    usleep(2000); 

    gettimeofday(&end, NULL); 

    seconds = end.tv_sec - start.tv_sec; 
    useconds = end.tv_usec - start.tv_usec; 

    mtime = ((seconds) * 1000 + useconds/1000.0) + 0.5; 

    start_time = ((start.tv_sec) * 1000 + start.tv_usec/1000.0) + 0.5; 
    end_time = ((end.tv_sec) * 1000 + end.tv_usec/1000.0) + 0.5; 

    mtime2 = end_time - start_time; 

    if(mtime > 10 || mtime2 > 10) 
    { 
     printf("WTF: %ld\n", mtime); 
     printf("WTF2: %ld\n", mtime2); 
    } 
    } 

    return 0; 
} 

結果:

$ gcc test.c -o out -lrt && ./out 
WTF: 11 
WTF2: 12 
WTF: 21 
WTF2: 21 

我認爲這錯了,因爲在useconds部分是循環的,並可能導致大的負區別。但它不會導致你使用簽署長整數如此大的時間...

my2cents

編輯:從人了nanosleep:

當前實現 了nanosleep的()是基於正常的內核定時器機制 ,其具有 分辨率爲1/HZ(見時間(7))。 因此,nanosleep()始終在至少指定的時間內暫停 ,但至少可能比指定的時間延長10毫秒 ,直到進程 再次變爲可運行爲止。對於相同的 原因,在* rem的情況下返回的值爲 遞送的信號通常是 ,其四捨五入爲 1/HZ的下一個較大倍數。

+0

感謝有關Linux默認調度程序不是實時的解釋。發現這樣的問題是相當令人驚訝的。我想知道軟件實時系統開發人員(例如gamedev)在這種情況下做什麼,因爲delta時間對這樣的系統有很大的影響...... – pachanga 2010-05-21 13:45:09

+0

@pachanga:不客氣。當我發現這一點時,我也很驚訝。這是一個問題。您可以依賴於使用納秒睡眠的時間讀數精度,並嘗試以您可以吸收的方式實現,例如,試圖在整個同步循環中只使用一次睡眠來減少錯誤。你應該睡得少一點。我可以想象更復雜的機制,但沒有什麼可以真正依靠,但沒有使用睡眠:) – neuro 2010-05-21 14:07:34

+0

是的,「不使用睡眠」,這也是我也在考慮......但在這種情況下,我的應用程序將完全消耗所有CPU時鐘:( – pachanga 2010-05-21 19:45:49

0

你的公式是錯誤的。您必須以相同的比例轉換兩次。在你的示例ms中。

double mtime1 = (start.tv_sec * 1000 + start.tv_usec/1000.0) ; 
double mtime2 = (end.tv_sec * 1000 + end.tv_usec/1000.0) ; 

double diff = mtime2 - mtime1; 
if(diff > 10) 
    printf("WTF: %ld\n", diff); 

你必須。減去校正值

實施例:t1 = 1.999999 t2 = 2.000001這樣的2微秒

隨着公式的間隔你計算:

2 - 1 == 11 - 9999999給人(1 * 1000 - 999998/1000) + 0.5結果= = 0.502這顯然是錯誤的。

我的方法得出:

mtime1 = (1 * 1000 + 999999/1000) = 1999.999 
mtime2 = (2 * 1000 +  1/1000) = 2000.001 

2000.001 - 1999.999 = 0.002 ms 
+0

是的,這是錯誤的,但並不能解釋這麼大的差異。它導致1毫秒的差異不是10毫秒或更多。看到我的答案。 – neuro 2010-05-21 13:30:54

+0

使用錯誤的公式,錯誤高達1秒,但你說得對,那不是OP的問題。 – 2010-05-21 13:45:57

0

我已經做了這樣的措施,而我的結論是完全一樣的。在Windows和Linux平等地發生。

在10^-n秒內建立直方圖的程序給出以下結果。

0.1 0 
0.01 0 
0.001 0 
0.0001 2 
1e-05 24 
1e-06 69726023 
Total: 69726049 
Duration: 6.47403 seconds. 
Average: 0.0928495 microseconds. 

但請注意,這是一個全新的系統。我記得一年前在2004年的一個系統中使用這個功能,並且在0.01的範圍內(10毫秒以上)每秒有幾次點擊。

+0

是的。我也有這樣的問題。問題在於,睡眠可以保證你至少睡一定的時間,但是由於linux(和Windows)不是實時操作系統,它們可以睡得更多。看到我的回答... – neuro 2010-05-21 13:34:24

2

我的猜測是它與操作系統有關。嘗試以實時優先級運行進程(請參閱chrt程序)並查看是否有幫助。

在另一個說明中,您正在計算錯誤的mtime。這裏有一個程序,我使用的,雖然它是結構的timespec而不是timeval結構(納秒,而不是微秒)的原則應該是清楚的:

timespec diff(timespec start, timespec end) 
{ 
    timespec temp; 
    if ((end.tv_nsec - start.tv_nsec) < 0) { 
     temp.tv_sec = end.tv_sec - start.tv_sec - 1; 
     temp.tv_nsec = 1000000000 + end.tv_nsec - start.tv_nsec; 
    } else { 
     temp.tv_sec = end.tv_sec - start.tv_sec; 
     temp.tv_nsec = end.tv_nsec - start.tv_nsec; 
    } 
    return temp; 
} 
+0

計算變化對我來說沒有什麼不同,但'chrt'確實如此。與OP相同的Linux內核。 – 2010-05-21 14:15:23

1

發現,通過系統計時器的粒度請參見手冊頁

http://linux.die.net/man/3/usleep

這是10ms afaik。因此usleep可能會在過程重新計劃之前過期很久。

它也與你得到的值是「正常」時間片的數量級一致。