2012-06-26 12 views
10

Po wykonaniu następujący test:pisać lub drukować, co jest szybsze?

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")); 
} 

okazuje się, że wzywa do printf podjąć wielki sumie 3 sekund, podczas rozmowy napisać potrwać aż o 46 sekund. Jak to się dzieje, że przy całej fantazyjnej magii formatowania, jaką robi printf, i że sam printf wywołuje write? Czy jest coś, czego mi brakuje?

Wszelkie myśli i dane wejściowe są mile widziane.

+0

zależy od Twojego systemu – JMBise

+3

printf wykonuje buforowanie. –

+9

Naprawdę? Obliczasz długość łańcucha za każdym razem, a następnie mierzysz to jako część taktów? –

Odpowiedz

22

Jak, z ... faktem, który sam nazywa printf, jest to możliwe? Czy jest coś, czego mi brakuje?

Tak, jest coś, czego brakuje. printf niekoniecznie wywołuje writeza każdym razem. Raczej printf buforuje swoje wyjście. Oznacza to, że często przechowuje wynik w buforze pamięci, wywołując tylko write, gdy bufor jest pełny lub w innych warunkach.

write to dość kosztowna rozmowa, znacznie droższa niż kopiowanie danych do bufora printf, więc zmniejszenie liczby połączeń write zapewnia wygraną netto.

Jeśli Twój odnośnik jest skierowany do urządzenia końcowego, wtedy printf dzwoni write za każdym razem, gdy widzi \n - w twoim przypadku, za każdym razem, gdy jest wywoływane. Jeśli Twój odnośnik jest skierowany do pliku (lub do /dev/null), wówczas wywołania printf będą pisane tylko wtedy, gdy wewnętrzny bufor jest pełny.

Załóżmy, że przekierowujesz dane wyjściowe, a wewnętrzny bufor printf ma rozmiar 4 KB, wtedy pierwsza pętla wywoła write 3000000/(4096/12) == 8780 razy. Twoja druga pętla wywołuje jednak write 3000000 razy.

Poza efektem mniejszej liczby połączeń z numerem write, jest to rozmiar z połączeń z write. Ilość pamięci na dysku twardym to sektor - często 512 bajtów. Zapisanie mniejszej ilości danych niż sektor może wymagać odczytania oryginalnych danych w sektorze, zmodyfikowania go i zapisania wyniku z powrotem. Wywoływanie write z pełnym sektorem może jednak przebiegać szybciej, ponieważ nie trzeba czytać oryginalnych danych. printf Rozmiar bufora jest wybrany jako wielokrotność typowego rozmiaru sektora. W ten sposób system może najskuteczniej zapisać dane na dysku.

Oczekuję, że twoja pierwsza pętla przejdzie znacznie szybciej niż druga.

+1

To wszystko wyjaśnia całkiem dobrze. Dziękuję Ci! Odnośnie komentarza na temat rozmiaru danych ...dlaczego oryginalne dane nie powinny być odczytywane tylko dlatego, że idealnie pasują do tego sektora? Czy zapis nie musi jeszcze znać danych, które należy zapisać? – Ataraxia

4

Nie jesteś porównywanie jabłek do jabłek, bo pętla z write tras strlen3000000 razy, podczas gdy printf nie zrobić żadnej z tych rzeczy; nie ma również żadnego formatowania, więc "fantazyjna magia formatowania" prawie nie ma zastosowania.

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

Inną ważną różnicą jest to, że za każdym razem printf wypróżnia kiedy przechodzą \n, natomiast write nie. Należy usunąć \n z obu ciągów, aby wyniki testów były bardziej sprawiedliwe.

+3

W moim systemie, gcc-4.5.1 ocenia 'strlen' podczas kompilacji, nawet bez optymalizacji. Płukanie/buforowanie wydaje się być tym, co robi różnicę. –

+0

@DanielFischer Dzięki! Miło jest wiedzieć, że 'gcc' jest na tyle sprytny, aby spasować wyrażenie" strlen "w stałą. – dasblinkenlight

+2

'printf' robi ** nie ** używa każdego' \ n', jeśli dane wyjściowe są przekierowywane do pliku. Ponadto dokładniej jest powiedzieć, że "pisz" za każdym razem przepuszcza, bez względu na treść - w końcu "kolor" w tym kontekście oznacza po prostu "wywołania". " –

Powiązane problemy