Cần một cái gì đó nhanh hơn so với trên mạng


12

Đối với một tệp thực sự lớn như 1GB wc -lxảy ra là chậm. Chúng ta có cách tính nhanh hơn số lượng dòng mới cho một tệp cụ thể không?


25
Mua đĩa nhanh hơn? Cho rằng mỗi byte của đầu vào phải được kiểm tra cho sự vô hiệu của nó 0x0A, I / O chắc chắn là nút cổ chai.
Thrig

2
Nếu bạn nghi ngờ wccó quá nhiều chi phí, bạn có thể cố gắng tự thực hiện foreach byte in file: if byte == '\n': linecount++. Nếu được triển khai trong C hoặc trình biên dịch chương trình, tôi không nghĩ rằng nó sẽ nhanh hơn, ngoại trừ có thể trong không gian kernel trên RTOS với mức độ ưu tiên cao nhất (hoặc thậm chí sử dụng ngắt cho điều đó - bạn không thể làm gì khác với hệ thống. .. được rồi, tôi lạc đề ;-))
Murphy

3
Và chỉ để có được cảm giác về quy mô, tôi đã thực hiện nhanh chóng time wc -l some_movie.avitrên một tệp không được lưu trữ, kết quả là 5172672 some_movie.avi -- real 0m57.768s -- user 0m0.255s -- sys 0m0.863s. Về cơ bản chứng minh @thrig đúng, I / O phá vỡ hiệu suất của bạn trong trường hợp này.
Murphy

10
Cách tốt nhất để hiển thị đó là một nút chai IO, thực hiện time wc -l some_large_file_smaller_than_cachehai lần liên tiếp và xem tốc độ hoạt động thứ hai nhanh như thế nào, sau đó time wc -l some_large_file_larger_than_cachevà xem thời gian không thay đổi giữa các lần chạy. Đối với tệp ~ 280 MB tại đây, thời gian từ 1,7 giây đến 0,2 giây, nhưng đối với tệp 2 GB thì 14 giây cả hai lần.
EightBitTony

1
Làm thế nào chậm là quá chậm đối với bạn? Nó /usr/bin/time wc -l <file>nói gì? Phần cứng của bạn là gì? Có nhanh hơn nếu bạn chạy lệnh nhiều lần? Chúng tôi thực sự cần thêm thông tin;)
marcelm

Câu trả lời:


21

Bạn có thể thử viết bằng C:

#include <unistd.h>
#include <stdio.h>
#include <string.h>
int main(){
  char buf[BUFSIZ];
  int nread;
  size_t nfound=0;
  while((nread=read(0, buf, BUFSIZ))>0){
    char const* p;
    for(p=buf; p=memchr(p,'\n',nread-(p-buf)); nfound++,p++) {;}
  }
  if(nread<0) { perror("Error"); return 1; }
  printf("%lu\n", nfound);
  return 0;
}

Lưu trong ví dụ, wcl.cbiên dịch ví dụ, với gcc wcl.c -O2 -o wclvà chạy với

<yourFile ./wcl

Điều này tìm thấy các dòng mới được rắc trong tệp 1GB trên hệ thống của tôi trong khoảng 370ms (chạy lặp lại). (Tăng kích thước bộ đệm tăng một chút thời gian, đó là điều được mong đợi - BUFSIZ phải gần với tối ưu). Điều này rất tương đương với ~ 380ms tôi nhận được wc -l.

Mmaping cho tôi thời gian tốt hơn khoảng 280ms , nhưng tất nhiên nó có giới hạn là bị giới hạn ở các tệp thực (không có FIFOS, không có đầu vào đầu cuối, v.v.):

#include <stdio.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
int main(){
  struct stat sbuf;
  if(fstat(0, &sbuf)<0){ perror("Can't stat stdin"); return 1; }

  char* buf = mmap(NULL, sbuf.st_size, PROT_READ, MAP_PRIVATE, 0/*stdin*/, 0/*offset*/);
  if(buf == MAP_FAILED){ perror("Mmap error"); return 1; } 

  size_t nread = sbuf.st_size, nfound=0;
  char const* p;
  for(p=buf; p=memchr(p,'\n',nread-(p-buf)); nfound++,p++) {;}

  printf("%lu\n", nfound);
  return 0;
}

Tôi đã tạo tệp thử nghiệm của mình với:

 $ dd if=/dev/zero of=file bs=1M count=1042 

và thêm một số dòng thử nghiệm mới với:

 $ echo >> 1GB 

và một trình soạn thảo hex.


Tôi đã rất ngạc nhiên với kết quả mmBH TBH. Tôi đã từng nghĩ mmaping nhanh hơn đọc / ghi, nhưng sau đó tôi thấy một số điểm chuẩn linux cho thấy điều ngược lại. Có vẻ như nó rất đúng trong trường hợp này.
PSkocik

4
mmap sẽ nhận được kết quả tốt hơn rất nhiều trên linux bởi vì nó sẽ ánh xạ tới các trang lớn trong những ngày này và các lỗi TLB là sloooowwwwwww.
ngày

Có thể có một số lợi ích khi đọc các phần khác nhau của tệp trong các luồng riêng biệt (ví dụ với forvòng lặp OpenMP ) để một số tiến trình có thể được thực hiện trong khi một luồng bị đình trệ chờ đầu vào. Nhưng mặt khác, nó có thể cản trở việc lên lịch I / O, vì vậy tất cả những gì tôi có thể khuyên là hãy thử và đo lường!
Toby Speight

Các read()phiên bản có thể được hưởng lợi từ đọc trước.
Barmar

1
@TobySpeight Vâng, đa luồng có thể tăng tốc nó. Ngoài ra, việc quét hai byte cùng một lúc thông qua các bảng tra cứu 2 ^ 16 cung cấp tốc độ khá tốt trong lần cuối tôi chơi với nó.
PSkocik

18

Bạn có thể cải thiện giải pháp được đề xuất bởi @pskocik bằng cách giảm số lượng cuộc gọi đến read. Có rất nhiều cuộc gọi để đọc BUFSIZcác đoạn từ tệp 1Gb. Cách tiếp cận thông thường để làm điều này là bằng cách tăng kích thước bộ đệm:

  • chỉ để giải trí, hãy thử tăng kích thước bộ đệm lên 10 lần hoặc 100. Trên Debian 7 của tôi, BUFSIZlà 8192. Với chương trình ban đầu, đó là 120 nghìn thao tác đọc. Bạn có thể có khả năng chi trả bộ đệm đầu vào 1Mb để giảm hệ số đó xuống 100 lần.
  • Để có cách tiếp cận tối ưu hơn, các ứng dụng có thể phân bổ bộ đệm lớn như tệp, yêu cầu một thao tác đọc. Điều đó hoạt động đủ tốt cho các tệp "nhỏ" (mặc dù một số độc giả có nhiều hơn 1Gb trên máy của họ).
  • cuối cùng, bạn có thể thử nghiệm I / O được ánh xạ bộ nhớ, xử lý phân bổ như vậy.

Khi đo điểm chuẩn cho các phương pháp khác nhau, bạn có thể nhớ rằng một số hệ thống (như Linux) sử dụng hầu hết bộ nhớ chưa sử dụng của máy làm bộ đệm đĩa. Cách đây một thời gian (gần 20 năm trước, được đề cập trong Câu hỏi thường gặp ), tôi đã bối rối trước kết quả tốt bất ngờ từ thuật toán phân trang (không tốt lắm) mà tôi đã phát triển để xử lý các điều kiện bộ nhớ thấp trong trình soạn thảo văn bản. Tôi được giải thích rằng nó chạy rất nhanh vì chương trình đang hoạt động từ bộ đệm được sử dụng để đọc tệp và chỉ khi tệp được đọc lại hoặc ghi thì sẽ có sự khác biệt về tốc độ.

Điều tương tự cũng áp dụng cho mmap(trong một trường hợp khác vẫn nằm trong danh sách việc cần làm của tôi để kết hợp vào Câu hỏi thường gặp, nhà phát triển đã báo cáo kết quả rất tốt trong tình huống trong đó bộ đệm đĩa là lý do thực sự để cải thiện). Phát triển điểm chuẩn cần có thời gian và sự quan tâm để phân tích lý do cho hiệu suất tốt (hoặc xấu).

Đọc thêm:


2
Bạn đang đánh giá quá cao ảnh hưởng của kích thước bộ đệm trên một ngưỡng nhất định. Thông thường, việc tăng kích thước bộ đệm vượt quá 4KB-ish không giúp ích nhiều và trên thực tế có thể gây bất lợi vì nó có thể đẩy bộ đệm ra khỏi bộ đệm L1. Trên máy của tôi, thử nghiệm với dd, sử dụng bộ đệm 1MB chậm hơn 8KB. Giá trị mặc định 8KB cho wc thực sự được chọn khá tốt, nó sẽ gần tối ưu cho một phạm vi lớn các hệ thống.
marcelm
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.