2009-12-02 41 views
25

Tôi có một chương trình C nhỏ để tính toán băm (đối với bảng băm). Các mã trông khá sạch sẽ, tôi hy vọng, nhưng có một cái gì đó không liên quan đến nó đó là bugging tôi.printf làm chậm chương trình của tôi

Tôi có thể dễ dàng tạo khoảng một triệu băm trong khoảng 0,2-0,3 giây (được đánh dấu bằng/usr/bin/time). Tuy nhiên, khi tôi printf() inging chúng trong vòng lặp for, chương trình sẽ chậm lại khoảng 5 giây.

  1. Tại sao điều này?
  2. Làm thế nào để làm cho nó nhanh hơn? mmapp() ing stdout có thể?
  3. Thiết kế stdlibc liên quan đến điều này như thế nào và nó có thể được cải thiện như thế nào?
  4. Hạt nhân có thể hỗ trợ nó tốt hơn như thế nào? Làm thế nào nó sẽ cần phải được sửa đổi để làm cho thông lượng trên địa phương "tập tin" (ổ cắm, đường ống, vv) REALLY nhanh?

Tôi rất mong nhận được câu trả lời thú vị và chi tiết. Cảm ơn.

PS: đây là bộ công cụ xây dựng trình biên dịch, do đó, đừng ngại ngùng để tìm hiểu chi tiết. Trong khi đó không có gì để làm với vấn đề chính nó, tôi chỉ muốn chỉ ra rằng chi tiết tôi quan tâm.

Phụ Lục

Tôi đang tìm kiếm thêm các cách tiếp cận programatic cho các giải pháp và giải thích. Thật vậy, đường ống thực hiện công việc, nhưng tôi không kiểm soát được "người dùng" làm gì.

Tất nhiên, tôi đang thực hiện kiểm tra ngay bây giờ, điều này sẽ không được thực hiện bởi "người dùng thông thường". NHƯNG mà không thay đổi thực tế là một printf đơn giản() làm chậm quá trình, đó là vấn đề tôi đang cố gắng tìm một giải pháp lập trình tối ưu cho.


Phụ Lục - Astonishing quả

Thời gian tham chiếu là cho printf đồng bằng() gọi bên trong một TTY và mất khoảng 4 phút 20 giây.

Kiểm tra theo/dev/pts (ví dụ: Konsole) tăng tốc đầu ra lên khoảng 5 giây.

Mất khoảng thời gian tương tự khi sử dụng setbuffer() trong mã thử nghiệm của tôi với kích thước 16384, gần giống với 8192: khoảng 6 giây.

setbuffer() có dường như không có hiệu lực khi sử dụng: mất khoảng thời gian tương tự (trên TTY khoảng 4 phút, trên PTS khoảng 5 giây).

Điều đáng kinh ngạc là, nếu tôi bắt đầu thử nghiệm trên tty1 và sau đó chuyển sang khác TTY, nó chỉ mất giống như trên một PTS: khoảng 5 giây.

Kết luận: hạt nhân thực hiện điều gì đó liên quan đến khả năng truy cập và thân thiện với người dùng. HUH!

Thông thường, nó cũng không kém phần quan trọng nếu bạn nhìn vào TTY khi đang hoạt động hoặc chuyển sang TTY khác.


Lesson: khi chạy chương trình đầu ra thâm canh, chuyển sang một TTY!

+2

Nếu bạn chuyển hướng đầu ra đến/dev/null, chương trình của bạn sẽ nhanh như thế nào? –

+0

@ammoQ: Chỉ cần nhanh như khi chuyển hướng đến bất kỳ tệp thông thường nào: khoảng 0,5 giây. – Flavius

+1

Nó không phải là một vấn đề "đơn giản".I/O nói chung là đơn đặt hàng của cường độ chậm hơn so với thẳng lên tính toán CPU và hoạt động xe buýt, nó không phải là đáng kinh ngạc để nhận ra nó. –

Trả lời

29

Đầu ra không bị bong tróc rất chậm.

Theo mặc định stdout được đệm đầy đủ, tuy nhiên khi được gắn với thiết bị đầu cuối, stdout bị bỏ chặn hoặc bộ đệm dòng.

Cố gắng bật đệm cho stdout sử dụng setvbuf(), như thế này:

char buffer[8192]; 

setvbuf(stdout, buffer, _IOFBF, sizeof(buffer)); 
+0

+1 cho phương pháp tiếp cận có lập trình hơn tôi đang tìm kiếm. – Flavius

+0

Ồ, printf() ghi vào chế độ mặc định theo mặc định. Tôi không can thiệp vào cách printf() hoạt động. – Flavius

8

Nếu bạn đang printf() vào bảng điều khiển, nó thường rất chậm. Tôi không chắc chắn tại sao nhưng tôi tin rằng nó không trở lại cho đến khi giao diện điều khiển đồ họa cho thấy chuỗi xuất ra. Ngoài ra, bạn không thể mmap() để stdout.

Việc ghi vào tệp phải nhanh hơn nhiều (nhưng vẫn có độ lớn chậm hơn so với tính toán băm, tất cả I/O đều chậm).

7

Bạn có thể thử chuyển hướng đầu ra trong trình bao từ bảng điều khiển sang tệp. Sử dụng này, các bản ghi có kích thước gigabyte có thể được tạo chỉ trong vài giây.

6
  1. I/O luôn chậm so với tính toán thẳng. Hệ thống có để đợi nhiều thành phần hơn là có sẵn để sử dụng chúng. Nó sau đó phải chờ phản hồi trước khi có thể tiếp tục. Ngược lại nếu chỉ đơn giản là tính toán, thì chỉ thực sự di chuyển dữ liệu giữa các thanh ghi RAM và CPU .

  2. Tôi chưa thử nghiệm điều này, nhưng có thể nhanh hơn để gắn các băm của bạn vào một chuỗi, và sau đó chỉ cần in chuỗi ở cuối. Mặc dù nếu bạn đang sử dụng C, không phải C++, điều này có thể chứng minh là một nỗi đau!

3 và 4 nằm ngoài tôi Tôi sợ.

14

Bạn có thể lưu chuỗi của mình vào bộ đệm và xuất chúng vào tệp (hoặc bảng điều khiển) ở cuối hoặc theo định kỳ, khi bộ đệm của bạn đã đầy.

Nếu xuất ra bàn điều khiển, cuộn thường là kẻ giết người.

+4

+1, đặc biệt là để cuộn. Chỉ cần tưởng tượng tất cả các blitting và bitmap sao chép liên quan đến di chuyển ... – sleske

+2

Phản ứng của bạn làm cho tôi kiểm tra chương trình theo một TTY sạch, và theo một PTS quản lý của Konsole. Kết quả: Konsole tăng tốc độ lên một chút! Phải mất 4 phút 20 giây khi chạy từ TTY (mà nên được sử dụng như là tài liệu tham khảo thực sự cho thử nghiệm, tôi nghĩ), 5 giây từ PTY. – Flavius

+1

+1 khác để cuộn. Đơn giản chỉ cần chạy một số chương trình chatty trong màn hình GNU (sau đó tách nó) sẽ tăng tốc độ những thứ lên rất nhiều! –

4
  1. Tại sao không tạo chuỗi theo yêu cầu thay vì tại thời điểm xây dựng? Không có điểm nào trong việc xuất 40 màn hình dữ liệu trong một giây làm sao bạn có thể đọc nó? Tại sao không tạo đầu ra theo yêu cầu và chỉ hiển thị màn hình cuối cùng đầy đủ và sau đó theo yêu cầu người dùng cuộn ???

  2. Tại sao không sử dụng sprintf để in thành chuỗi và sau đó tạo chuỗi nối của tất cả các kết quả trong bộ nhớ và in ở cuối?

  3. Bằng cách chuyển sang sprintf, bạn có thể thấy rõ thời gian được sử dụng trong chuyển đổi định dạng và số tiền chi tiêu hiển thị kết quả cho bảng điều khiển và thay đổi mã một cách thích hợp.

  4. Đầu ra của bảng điều khiển theo định nghĩa chậm, việc tạo băm chỉ thao tác một vài byte bộ nhớ. Đầu ra bàn điều khiển cần phải trải qua nhiều lớp của hệ điều hành, sẽ có mã để xử lý khóa/quá trình khóa vv một khi nó cuối cùng được trình điều khiển hiển thị có thể là thiết bị baud 9600! hoặc hiển thị bitmap lớn, các chức năng đơn giản như cuộn màn hình có thể liên quan đến thao tác với megabyte bộ nhớ.

+1

Về (4): Tôi nhận ra rằng, NHƯNG nếu tôi là một nhà văn hệ điều hành, có thể sao chép đầu ra từ một vị trí đến một vị trí/quy trình khác không? Nếu có, làm thế nào tôi sẽ đi về nó, theo ý kiến ​​của bạn, để những điều tăng tốc độ? – Flavius

+1

Trong những ngày cũ trò chơi lập trình được sử dụng để giải quyết các thiết bị đầu ra trực tiếp để ví dụ thực sự viết các ký tự vào bộ nhớ hiển thị - Hôm nay thậm chí họ trong hầu hết các phần sử dụng thư viện để nói chuyện với phần cứng để họ có thể được thiết bị độc lập và tận dụng lợi thế tăng tốc phần cứng. Nó hiếm khi có giá trị vượt qua những lớp này ngày nay. – AnthonyLambert

4

Vì I/O luôn chậm hơn nhiều so với tính toán CPU, bạn có thể lưu trữ tất cả các giá trị trong I/O nhanh nhất có thể.Vì vậy, sử dụng RAM nếu bạn có đủ, sử dụng tập tin nếu không, nhưng nó là chậm hơn nhiều so với RAM.

In ra các giá trị hiện có thể được thực hiện sau hoặc song song với một chuỗi khác. Vì vậy, các chủ đề tính toán (s) có thể không cần phải chờ cho đến khi printf đã trở lại.

2

Tôi đoán các loại thiết bị đầu cuối được sử dụng một số hoạt động sản xuất đệm, vì vậy khi bạn làm một printf nó không xảy ra với sản lượng trong split micro giây, nó được lưu trữ trong bộ nhớ đệm của hệ thống con đầu cuối.

Điều này có thể bị ảnh hưởng bởi những thứ khác có thể gây chậm lại, có thể có một hoạt động chuyên sâu về bộ nhớ chạy trên nó ngoài chương trình của bạn. Tóm lại, có quá nhiều thứ có thể xảy ra cùng lúc, phân trang, hoán đổi, nặng nề bởi quá trình khác, cấu hình bộ nhớ được sử dụng, có thể nâng cấp bộ nhớ, v.v.

Có thể tốt hơn là ghép các chuỗi cho đến khi đạt đến một giới hạn nhất định, sau đó, khi viết xong, hãy viết tất cả cùng một lúc. Hoặc thậm chí sử dụng pthreads để thực hiện quá trình thực hiện mong muốn.

Đã chỉnh sửa: Đối với 2,3 nó nằm ngoài tôi. Đối với 4, Tôi không quen thuộc với Sun, nhưng không biết và đã sai lầm với Solaris, Có thể có một tùy chọn hạt nhân để sử dụng một tty ảo .. tôi sẽ thừa nhận nó được một lúc kể từ khi rối tung với các cấu hình hạt nhân và biên dịch lại nó. Như vậy bộ nhớ của tôi có thể không tuyệt vời về điều này, có một gốc xung quanh với các tùy chọn để xem.

 
[email protected]:/usr/src/linux $ make; make menuconfig **OR kconfig if from X** 

này sẽ cháy lên menu hạt nhân, có một cuộc khai quật xung quanh để xem phần cài đặt video dưới các thiết bị phụ cây ..

được sửa đổi: nhưng có một tinh chỉnh bạn đặt vào hạt nhân bằng cách thêm một tệp vào hệ tập tin proc (nếu một thứ như vậy tồn tại), hoặc có thể chuyển đổi thành hạt nhân, như thế này (đây là trí tưởng tượng và không ngụ ý nó tồn tại), fastio

Hy vọng điều này sẽ giúp, Trân trọng, Tom.

+0

Cảm ơn câu trả lời của bạn. Đó là một máy linux, như bạn có thể thấy trong các thẻ của câu hỏi. – Flavius

+0

@Flavius: Rất tiếc, xin lỗi vì mặt trời và bit năng lượng mặt trời, tôi chắc chắn tôi đã thấy nó ở đó một thời gian trước khi tôi đang chỉnh sửa câu trả lời. Phải có được trộn lẫn với một số chủ đề khác ở đây trên SO ... Xin lỗi – t0mm13b

+0

@ Flavius: Tôi đã chỉnh sửa ở trên và loại bỏ các nhận xét về Sun ... – t0mm13b

4

Tôi đã khám phá từ lâu trước đây using this technique điều gì đó cần phải rõ ràng. Không chỉ là I/O chậm, đặc biệt là giao diện điều khiển, nhưng định dạng số thập phân cũng không nhanh. Nếu bạn có thể đặt các số vào nhị phân thành các bộ đệm lớn và ghi các số đó vào một tệp, bạn sẽ thấy nó nhanh hơn rất nhiều.

Bên cạnh đó, ai sẽ đọc chúng? Không có điểm in ấn tất cả trong một định dạng con người có thể đọc được nếu không ai cần phải đọc tất cả chúng.

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