2012-06-26 145 views
10

執行下列試驗後:write或printf,哪個更快?

for(i = 0; i < 3000000; i++) { 
    printf("Test string\n"); 
} 

for(i = 0; i < 3000000; i++) { 
    write(STDOUT_FILENO, "Test string\n", strlen("Test string\n")); 
} 

事實證明,所述呼叫對printf採取總計爲3秒,而調用寫採取高達46秒。如何用printf所具有的所有奇妙格式化魔術,以及printf本身稱爲write的事實,這可能嗎?有什麼我失蹤了嗎?

任何和所有的想法和輸入讚賞。

+0

這取決於您的系統 – JMBise

+3

printf會緩衝。 –

+9

真的嗎?你是每次計算字符串長度,然後測量它作爲時間的一部分? –

回答

22

怎麼,與...事實上,printf本身調用寫入,這可能嗎?有什麼我失蹤了嗎?

是的,有些東西是你缺少的。 printf不一定每請撥打write。而是,printf緩衝其輸出。也就是說,它通常會將其結果存儲在內存緩衝區中,緩衝區已滿或其他條件時僅調用write

write是一個相當昂貴的調用,將數據複製到printf的緩衝區,因此減少了write呼叫的數量提供了一個淨值表現贏昂貴得多。

如果你的標準輸出指向一個終端設備,那麼每當它看到一個\n時,printf就會調用write - 就你而言,每次調用時都會如此。如果你的stdout被定向到一個文件(或者到/dev/null),那麼printf調用只有在其內部緩衝區已滿時纔會寫入。

假設您重定向您的輸出,並且printf的內部緩衝區爲4K字節,則第一個循環調用write 3000000 /(4096/12)== 8780次。然而,你的第二個循環調用了3000000次write

超越更少的調用的效果write,是大小調用write。硬盤中的存儲量是一個扇區 - 通常是512個字節。要寫入比扇區少的數據量,可能需要讀取扇區中的原始數據,修改它並將結果寫回。然而,使用完整扇區調用write可能會更快,因爲您不必讀入原始數據。 printf的緩衝區大小被選爲典型扇區大小的倍數。這樣系統可以最有效地將數據寫入磁盤。

我希望你的第一個循環比第二個循環要快得多。

+1

這解釋了一切都很好。謝謝!關於你對數據大小的評論......爲什麼原始數據不能僅僅因爲它完全適合該領域而被讀取呢?寫入調用不需要知道需要寫入的數據嗎? – Ataraxia

4

你是不是比較蘋果和蘋果,因爲與write運行循環strlen3000000倍,而printf沒有做任何的是什麼;它也沒有做任何格式化,所以「幻想格式化魔術」幾乎不適用。

size_t len = strlen("Test string\n"); 
for(i = 0; i < 3000000; i++) { 
    write(STDOUT_FILENO, "Test string\n", len); 
} 

另一個重要區別是,每一次刷新printf當你通過\n,而write沒有。您應該從兩個字符串中刪除\n,以使您的基準更加公平。

+3

在我的系統上,即使沒有優化,gcc-4.5.1在編譯時也會評估'strlen'。沖洗/緩衝似乎是造成差異的原因。 –

+0

@DanielFischer謝謝!很高興知道'gcc'足夠聰明,可以將'strlen'表達式摺疊成一個常量。 – dasblinkenlight

+2

如果輸出被重定向到一個文件,'printf'不會在每個'\ n'上刷新**。此外,更準確地說,每次都寫「寫」,無內容 - 畢竟,在這種情況下,「刷新」僅僅意味着「調用」寫入「。 –