2012-06-26 40 views
10

Sau khi thực hiện các xét nghiệm sau:viết hoặc printf, nhanh hơn?

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

nó chỉ ra rằng các cuộc gọi đến printf lấy đi tổng cộng lớn của 3 giây, trong khi các cuộc gọi để viết mất một con số khổng lồ 46 giây. Làm thế nào, với tất cả các ma thuật định dạng ưa thích mà printf làm, và thực tế là printf chính nó gọi write, là điều này có thể? Có cái gì tôi đang mất tích?

Mọi suy nghĩ và đầu vào đều được đánh giá cao.

+0

tùy thuộc vào hệ thống của bạn – JMBise

+3

printf sẽ lưu vào bộ đệm. –

+9

Thật sao? Bạn đang tính toán độ dài chuỗi mỗi lần và sau đó đo lường đó như là một phần của timings? –

Trả lời

22

Làm thế nào, với ... thực tế là bản thân printf gọi ghi, điều này có khả thi không? Có cái gì tôi đang mất tích?

Có, có điều gì đó bạn đang thiếu. printf không nhất thiết phải gọi writemỗi lần. Thay vào đó, printf làm đệm đầu ra của nó. Đó là, nó thường lưu trữ kết quả của nó trong một bộ nhớ đệm, chỉ gọi write khi bộ đệm đầy, hoặc trên một số điều kiện khác.

write là một cuộc gọi khá tốn kém, đắt hơn nhiều so với sao chép dữ liệu vào bộ đệm của printf, do đó giảm số lượng cuộc gọi write mang lại hiệu suất ròng.

Nếu thiết bị đầu cuối của bạn được chuyển hướng đến thiết bị đầu cuối, sau đó gọi printf gọi write mỗi lần thấy \n - trong trường hợp của bạn, mỗi lần được gọi. Nếu thiết bị xuất chuẩn của bạn được chuyển hướng đến một tệp (hoặc tới /dev/null), thì printf cuộc gọi chỉ ghi khi bộ đệm trong của nó đầy.

Giả sử bạn đang chuyển hướng đầu ra của mình và bộ đệm trong của printf là 4Kbytes, sau đó vòng lặp đầu tiên gọi write 3000000/(4096/12) == 8780 lần. Tuy nhiên, vòng lặp thứ hai của bạn, gọi số write 3000000 lần.

Ngoài ảnh hưởng của ít cuộc gọi đến write, là số kích thước của các cuộc gọi đến write. Lượng tử lưu trữ trong ổ cứng là một sector - thường là 512 byte. Để viết một lượng nhỏ dữ liệu hơn một ngành có thể liên quan đến việc đọc dữ liệu gốc trong ngành, sửa đổi nó và viết kết quả ra ngoài. Tuy nhiên, việc gọi số write với một khu vực hoàn chỉnh có thể nhanh hơn vì bạn không phải đọc dữ liệu gốc. Kích thước bộ đệm của printf được chọn là bội số của kích thước ngành điển hình. Bằng cách đó, hệ thống có thể ghi dữ liệu vào đĩa một cách hiệu quả nhất.

Tôi hy vọng vòng lặp đầu tiên của bạn sẽ nhanh hơn nhiều so với giây thứ hai.

+1

Điều này giải thích mọi thứ khá tốt. Cảm ơn bạn! Về nhận xét của bạn về kích thước dữ liệu ...tại sao dữ liệu gốc sẽ không được đọc chỉ vì nó phù hợp hoàn hảo trong lĩnh vực này? Không phải cuộc gọi viết vẫn cần phải biết dữ liệu cần phải được viết? – Ataraxia

4

Bạn không so sánh táo với táo, bởi vì vòng lặp có write chạy strlen3000000 lần, trong khi printf không làm bất kỳ điều nào trong số đó; nó không làm bất kỳ định dạng nào, do đó, "ma thuật định dạng ưa thích" hầu như không áp dụng.

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

Một khác biệt quan trọng là printf bừng mỗi khi bạn vượt qua \n, trong khi write không. Bạn nên xóa \n khỏi cả hai chuỗi để làm cho điểm chuẩn của bạn công bằng hơn.

+3

Trên hệ thống của tôi, gcc-4.5.1 đánh giá 'strlen' tại thời gian biên dịch ngay cả khi không tối ưu hóa. Việc xả/đệm dường như là điều tạo ra sự khác biệt. –

+0

@DanielFischer Cảm ơn! Thật tuyệt khi biết rằng 'gcc' đủ thông minh để gấp biểu thức' strlen' thành hằng số. – dasblinkenlight

+2

'printf' không ** không ** tuôn ra trên mọi' \ n' nếu đầu ra được chuyển hướng đến một tệp. Ngoài ra, chính xác hơn để nói rằng 'write' tuôn ra mọi lúc, bất kể nội dung - sau khi tất cả," tuôn ra "trong ngữ cảnh này có nghĩa là chỉ đơn thuần là" gọi "viết". " –

Các vấn đề liên quan