2009-04-09 54 views
11

Tôi đang viết một chương trình có hiệu suất khá quan trọng, nhưng không quan trọng. Hiện tại tôi đang đọc văn bản từ một dòng FILE* và tôi sử dụng fgets để lấy từng dòng. Sau khi sử dụng một số công cụ hiệu suất, tôi đã nhận thấy rằng 20% ​​đến 30% thời gian ứng dụng của tôi đang chạy, nó nằm trong số fgets.Đọc một dòng đầu vào nhanh hơn fgets?

Có cách nào nhanh hơn để nhận một dòng văn bản không? Ứng dụng của tôi là đơn luồng không có ý định sử dụng nhiều luồng. Dữ liệu nhập có thể từ stdin hoặc từ một tệp. Cảm ơn trước.

+0

Độ dài trung bình (và có thể stdev) của các dòng mà chương trình của bạn phân tích là gì? Điều này giúp xác định cách nhanh nhất để truy cập chúng. – Juliano

+0

@Juliano, các dòng luôn nhỏ hơn 260 ký tự. Tôi đã tránh được một vòng lặp xây dựng đường dây. – dreamlax

+0

Bạn có kiểm soát định dạng đầu vào không? Bạn có thể làm cho nó nhỏ gọn hơn? – Dave

Trả lời

7

Bạn không nói bạn đang sử dụng nền tảng nào, nhưng nếu nó giống UNIX, bạn có thể thử gọi hệ thống read(), không thực hiện lớp đệm bổ sung mà fgets() et al. Điều này có thể tăng tốc độ những thứ lên một chút, mặt khác nó cũng có thể làm chậm những thứ xuống - cách duy nhất để tìm hiểu là để hút nó và xem.

+0

Đây hóa ra là phương pháp nhanh nhất. Tôi cuối cùng đã đi xuống con đường này. Nó đơn giản hơn tôi đã nghĩ để làm "buffering của riêng tôi" và nó hóa ra là nhanh hơn nhiều, nhanh hơn (gần 4 lần) so với sử dụng 'fgets()'. – dreamlax

+0

Trớ trêu thay, đối với tôi pread perfomed 4 lần tồi tệ hơn fgets. – abirvalg

2

Nếu dữ liệu đến từ đĩa, bạn có thể bị ràng buộc IO.

Nếu trường hợp đó xảy ra, hãy lấy đĩa nhanh hơn (nhưng trước tiên hãy kiểm tra xem bạn đang tận dụng tối đa hiện tại của mình ... một số bản phân phối Linux không tối ưu hóa quyền truy cập đĩa ra khỏi hộp (hdparm)) , đưa dữ liệu vào bộ nhớ (nói bằng cách sao chép dữ liệu vào đĩa RAM) trước thời gian hoặc chuẩn bị sẵn sàng để chờ.


Nếu bạn không bị ràng buộc IO, bạn có thể lãng phí rất nhiều thời gian sao chép. Bạn có thể hưởng lợi từ cái gọi là phương pháp zero-copy. Một cái gì đó như bộ nhớ ánh xạ các tập tin và chỉ truy cập nó thông qua con trỏ.

Đó là một chút ngoài chuyên môn của tôi, vì vậy bạn nên làm một số việc đọc hoặc chờ sự giúp đỡ có kiến ​​thức hơn.

BTW-- Bạn có thể tham gia nhiều công việc hơn là vấn đề đáng giá; có thể là một máy nhanh hơn sẽ giải quyết tất cả các vấn đề của mình ...

NB-- Nó không phải là rõ ràng rằng bạn nhớ có thể lập bản đồ đầu vào tiêu chuẩn hoặc ...

+0

Đôi khi nó xuất phát từ đĩa, đôi khi nó được cấp thông qua stdin, nhưng trong cả hai trường hợp, thời gian dành cho fgets gần như giống nhau. Ngay cả việc tạo ra một đĩa RAM cho tập tin không tăng tốc độ nhiều thứ. – dreamlax

+0

Sau khi chỉnh sửa: vấn đề là ứng dụng này sẽ được chạy trên máy tính của người dùng cuối, đó là lý do tại sao hiệu suất là khá quan trọng. – dreamlax

3

Bạn có thể thử giảm thiểu số lượng thời gian bạn dành đọc từ đĩa bằng cách đọc một lượng lớn dữ liệu vào RAM rồi làm việc trên đó. Đọc từ đĩa chậm, vì vậy giảm thiểu lượng thời gian bạn bỏ ra bằng cách đọc (lý tưởng) toàn bộ tệp một lần, sau đó làm việc trên đó.

Sắp xếp như cách bộ nhớ cache CPU giảm thiểu thời gian CPU thực sự quay trở lại RAM, bạn có thể sử dụng RAM để giảm thiểu số lần bạn thực sự truy cập vào đĩa.

+0

Stdio đã được đệm, phải không? –

+0

Tôi nghĩ vậy nhưng tôi chắc chắn nó nhỏ hơn một megabyte, vì vậy đọc nhiều hơn thế vẫn nên giúp đỡ. – GManNickG

2

Tùy thuộc vào môi trường của bạn, sử dụng setvbuf() để tăng kích thước bộ đệm trong được sử dụng bởi luồng tệp có thể hoặc không thể cải thiện hiệu suất.

Đây là cú pháp -

setvbuf (InputFile, NULL, _IOFBF, BUFFER_SIZE); 

đâu Inputfile là một FILE * vào một tập tin vừa mở sử dụng fopen() và BUFFER_SIZE là kích thước của bộ đệm (được phân bổ bằng cách gọi này cho bạn).

Bạn có thể thử các kích thước bộ đệm khác nhau để xem liệu có ảnh hưởng tích cực hay không. Lưu ý rằng đây là tùy chọn hoàn toàn và thời gian chạy của bạn có thể hoàn toàn không có gì với cuộc gọi này.

4
  1. Sử dụng fgets_unlocked(), nhưng đọc kỹ những gì nó làm đầu tiên

  2. Lấy dữ liệu với fgetc() hoặc fgetc_unlocked() thay vì fgets().Với fgets(), dữ liệu của bạn được sao chép vào bộ nhớ hai lần, trước tiên là thư viện thời gian chạy C từ tệp đến bộ đệm trong (luồng I/O được đệm), sau đó từ bộ đệm trong đó tới một mảng trong chương trình của bạn

+0

Cảm ơn bạn đã gợi ý, nhưng tôi quên đề cập đến việc tôi đang sử dụng Mac OS X. fgets_unlocked không có sẵn vì nó là một phần mở rộng của GNU. Tôi sẽ xem xét sử dụng fgetc_unlocked. – dreamlax

+0

Vâng, OS X đang chạy GCC, bạn sẽ nhận được phần mở rộng GNU, phải không? –

+1

@Martin: Nó không phải là phần mở rộng của trình biên dịch GNU, mà là thư viện thời gian chạy GNU C. – dreamlax

4

Đọc toàn bộ tệp trong một lần chuyển vào bộ đệm.

Xử lý các dòng từ bộ đệm đó.

Đó là giải pháp nhanh nhất có thể.

0

Nếu hệ điều hành hỗ trợ nó, bạn có thể thử đọc tập tin không đồng bộ, tức là tệp được đọc vào bộ nhớ trong khi CPU đang bận làm việc khác. Vì vậy, các mã đi một cái gì đó như: ​ ​ ​ ​ ​

start asynchronous read 
loop: 
    wait for asynchronous read to complete 
    if end of file goto exit 
    start asynchronous read 
    do stuff with data read from file 
    goto loop 
exit: 

Nếu bạn có nhiều hơn một CPU sau đó một CPU đọc các tập tin và phân tích các dữ liệu vào dòng, CPU khác có mỗi dòng và xử lý nó .

0

Nhìn vào fread(). Nó đọc nhanh hơn nhiều cho tôi, đặc biệt là nếu bộ đệm cho fread được đặt là 65536. Nhược điểm: bạn phải làm rất nhiều công việc và về cơ bản viết chức năng getline của riêng bạn để chuyển đổi từ đọc nhị phân sang văn bản. Kiểm tra: file I/O

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