Làm thế nào để bạn xác định kích thước bộ đệm lý tưởng khi sử dụng FileInputStream?


156

Tôi có một phương thức tạo MessageDigest (hàm băm) từ một tệp và tôi cần thực hiện điều này với rất nhiều tệp (> = 100.000). Tôi nên tạo bộ đệm được sử dụng để đọc từ các tệp lớn đến mức nào để tối đa hóa hiệu suất?

Hầu hết mọi người đều quen thuộc với mã cơ bản (mà tôi sẽ lặp lại ở đây chỉ trong trường hợp):

MessageDigest md = MessageDigest.getInstance( "SHA" );
FileInputStream ios = new FileInputStream( "myfile.bmp" );
byte[] buffer = new byte[4 * 1024]; // what should this value be?
int read = 0;
while( ( read = ios.read( buffer ) ) > 0 )
    md.update( buffer, 0, read );
ios.close();
md.digest();

Kích thước lý tưởng của bộ đệm để tối đa hóa thông lượng là gì? Tôi biết điều này phụ thuộc vào hệ thống và tôi khá chắc chắn rằng hệ điều hành, FileSystem ổ cứng của nó và có thể có phần cứng / phần mềm khác trong hỗn hợp.

(Tôi nên chỉ ra rằng tôi hơi mới đối với Java, vì vậy đây có thể chỉ là một số lệnh gọi API Java mà tôi không biết.)

Chỉnh sửa: Tôi không biết trước các loại hệ thống này sẽ được sử dụng, vì vậy tôi không thể giả định rất nhiều. (Tôi đang sử dụng Java vì lý do đó.)

Chỉnh sửa: Đoạn mã trên bị thiếu những thứ như try..catch để làm cho bài đăng nhỏ hơn

Câu trả lời:


213

Kích thước bộ đệm tối ưu có liên quan đến một số điều: kích thước khối hệ thống tệp, kích thước bộ đệm CPU và độ trễ bộ đệm.

Hầu hết các hệ thống tệp được định cấu hình để sử dụng kích thước khối 4096 hoặc 8192. Về lý thuyết, nếu bạn định cấu hình kích thước bộ đệm của mình để bạn đọc nhiều hơn một vài byte so với khối đĩa, các hoạt động với hệ thống tệp có thể cực kỳ kém hiệu quả (ví dụ: nếu bạn cấu hình bộ đệm của bạn để đọc 4100 byte mỗi lần, mỗi lần đọc sẽ yêu cầu 2 khối đọc bởi hệ thống tệp). Nếu các khối đã có trong bộ đệm, thì bạn sẽ phải trả giá RAM -> độ trễ bộ đệm L3 / L2. Nếu bạn không may mắn và các khối chưa có trong bộ đệm, bạn cũng phải trả giá của đĩa-> độ trễ RAM.

Đây là lý do tại sao bạn thấy hầu hết các bộ đệm có kích thước bằng 2 và thường lớn hơn (hoặc bằng) kích thước khối đĩa. Điều này có nghĩa là một trong những lần đọc luồng của bạn có thể dẫn đến nhiều lần đọc khối đĩa - nhưng những lần đọc đó sẽ luôn sử dụng một khối đầy đủ - không đọc lãng phí.

Bây giờ, điều này được bù đắp khá nhiều trong một kịch bản phát trực tuyến điển hình bởi vì khối được đọc từ đĩa sẽ vẫn còn trong bộ nhớ khi bạn đọc lần đọc tiếp theo (sau tất cả chúng tôi đang đọc tuần tự ở đây) - vì vậy bạn sẽ kết thúc trả RAM -> giá trễ của bộ đệm L3 / L2 cho lần đọc tiếp theo, nhưng không phải là độ trễ của đĩa-> RAM. Về thứ tự độ lớn, độ trễ của đĩa-> RAM chậm đến mức nó có khá nhiều thay đổi so với bất kỳ độ trễ nào khác mà bạn có thể xử lý.

Vì vậy, tôi nghi ngờ rằng nếu bạn đã chạy thử nghiệm với các kích thước bộ đệm khác nhau (chưa thực hiện điều này), bạn có thể sẽ thấy tác động lớn của kích thước bộ đệm lên đến kích thước của khối hệ thống tệp. Trên đó, tôi nghi ngờ rằng mọi thứ sẽ tăng lên khá nhanh.

Có một tấn các điều kiện và các ngoại lệ ở đây - sự phức tạp của hệ thống được thực sự khá loạng choạng (chỉ nhận được một tay cầm trên L3 -> chuyển bộ nhớ cache L2 là tâm bogglingly phức tạp, và nó thay đổi với tất cả các loại CPU).

Điều này dẫn đến câu trả lời 'thế giới thực': Nếu ứng dụng của bạn giống 99% ngoài kia, hãy đặt kích thước bộ đệm thành 8192 và tiếp tục (thậm chí tốt hơn, chọn đóng gói theo hiệu suất và sử dụng BufferedInputStream để ẩn chi tiết). Nếu bạn nằm trong 1% ứng dụng phụ thuộc nhiều vào thông lượng đĩa, hãy tạo ra triển khai của bạn để bạn có thể trao đổi các chiến lược tương tác đĩa khác nhau và cung cấp các nút và quay số để cho phép người dùng của bạn kiểm tra và tối ưu hóa (hoặc đưa ra một số hệ thống tự tối ưu hóa).


3
Tôi đã thực hiện một số dấu hiệu trên điện thoại di động (Nexus 5X) cho ứng dụng Android của mình cho cả hai: tệp nhỏ (3,5Mb) và tệp lớn (175 Mb). Và phát hiện ra rằng kích thước vàng sẽ là byte [] có độ dài 524288. Chà, bạn có thể giành chiến thắng 10-20ms nếu bạn chuyển đổi giữa bộ đệm nhỏ 4Kb và bộ đệm lớn 524Kb tùy thuộc vào kích thước tệp nhưng nó không xứng đáng. Vì vậy, 524 Kb là lựa chọn tốt nhất trong trường hợp của tôi.
Kirill Karmazin

19

Vâng, nó có thể phụ thuộc vào nhiều thứ - nhưng tôi nghi ngờ nó sẽ tạo ra nhiều khác biệt. Tôi có xu hướng chọn 16K hoặc 32K như một sự cân bằng tốt giữa việc sử dụng bộ nhớ và hiệu suất.

Lưu ý rằng bạn nên có một khối thử / cuối cùng trong mã để đảm bảo luồng được đóng ngay cả khi một ngoại lệ được ném.


Tôi đã chỉnh sửa bài viết về try..catch. Trong mã thực sự của tôi, tôi có một cái, nhưng tôi đã bỏ nó ra để làm cho bài viết ngắn hơn.
ARKBAN

1
Nếu chúng ta muốn xác định một kích thước cố định cho nó, kích thước nào là tốt hơn? 4k, 16k hay 32k?
BattleTested

2
@MohammadrezaPanahi: Vui lòng không sử dụng nhận xét cho người dùng xấu. Bạn đã đợi chưa đầy một giờ trước khi nhận xét thứ hai. Xin nhớ rằng người dùng có thể dễ dàng ngủ, hoặc trong các cuộc họp hoặc về cơ bản bận rộn với những thứ khác và không có nghĩa vụ trả lời bình luận. Nhưng để trả lời câu hỏi của bạn: nó hoàn toàn phụ thuộc vào ngữ cảnh. Nếu bạn đang chạy trên một hệ thống rất hạn chế bộ nhớ, có lẽ bạn muốn có một bộ đệm nhỏ. Nếu bạn đang chạy trên một hệ thống lớn, sử dụng bộ đệm lớn hơn sẽ giảm số lượng cuộc gọi đọc. Câu trả lời của Kevin Day rất hay.
Jon Skeet

7

Trong hầu hết các trường hợp, nó thực sự không quan trọng đến thế. Chỉ cần chọn một kích thước tốt như 4K hoặc 16K và gắn bó với nó. Nếu bạn khẳng định rằng đây là nút cổ chai trong ứng dụng của mình, thì bạn nên bắt đầu định hình để tìm kích thước bộ đệm tối ưu. Nếu bạn chọn kích thước quá nhỏ, bạn sẽ lãng phí thời gian để thực hiện các thao tác I / O bổ sung và các cuộc gọi chức năng bổ sung. Nếu bạn chọn một kích thước quá lớn, bạn sẽ bắt đầu thấy rất nhiều lỗi bộ nhớ cache sẽ thực sự làm bạn chậm lại. Không sử dụng bộ đệm lớn hơn kích thước bộ đệm L2 của bạn.


4

Trong trường hợp lý tưởng, chúng ta nên có đủ bộ nhớ để đọc tệp trong một thao tác đọc. Đó sẽ là hiệu suất tốt nhất vì chúng tôi cho phép hệ thống quản lý Hệ thống tệp, đơn vị phân bổ và ổ cứng theo ý muốn. Trong thực tế, bạn may mắn biết trước kích thước tệp, chỉ cần sử dụng kích thước tệp trung bình được làm tròn lên tới 4K (đơn vị phân bổ mặc định trên NTFS). Và tốt nhất của tất cả: tạo một điểm chuẩn để kiểm tra nhiều tùy chọn.


bạn có nghĩa là kích thước bộ đệm tốt nhất để đọc và ghi trong một tệp là 4k?
BattleTested

4

Bạn có thể sử dụng BufferedStreams / reader và sau đó sử dụng kích thước bộ đệm của chúng.

Tôi tin rằng BufferedXStreams đang sử dụng 8192 làm kích thước bộ đệm, nhưng như Ovidiu đã nói, có lẽ bạn nên chạy thử nghiệm trên một loạt các tùy chọn. Nó thực sự sẽ phụ thuộc vào hệ thống tập tin và cấu hình đĩa như kích thước tốt nhất là gì.


4

Đọc các tệp bằng cách sử dụng FileChannel và MappedByteBuffer của Java NIO rất có thể sẽ dẫn đến một giải pháp sẽ nhanh hơn nhiều so với bất kỳ giải pháp nào liên quan đến FileInputStream. Về cơ bản, các tập tin lớn ánh xạ bộ nhớ và sử dụng bộ đệm trực tiếp cho các tập tin nhỏ.


4

Trong nguồn của BufferedInputStream, bạn sẽ tìm thấy: private static int DEFAULT_BUFFER_SIZE = 8192;
Vì vậy, đó là okey cho bạn sử dụng giá trị mặc định đó.
Nhưng nếu bạn có thể tìm ra một số thông tin, bạn sẽ nhận được câu trả lời có giá trị hơn.
Ví dụ: adsl của bạn có thể ưu tiên bộ đệm 1454 byte, đó là vì tải trọng của TCP / IP. Đối với đĩa, bạn có thể sử dụng giá trị khớp với kích thước khối của đĩa.


1

Như đã đề cập trong các câu trả lời khác, hãy sử dụng BufferedInputStreams.

Sau đó, tôi đoán kích thước bộ đệm không thực sự quan trọng. Chương trình bị ràng buộc I / O và tăng kích thước bộ đệm so với BIS mặc định, sẽ không ảnh hưởng lớn đến hiệu suất.

Hoặc chương trình là CPU bị ràng buộc bên trong MessageDigest.update () và phần lớn thời gian không được sử dụng trong mã ứng dụng, vì vậy điều chỉnh nó sẽ không giúp ích gì.

(Hmm ... với nhiều lõi, chủ đề có thể giúp đỡ.)


0

1024 phù hợp với nhiều hoàn cảnh khác nhau, mặc dù trong thực tế, bạn có thể thấy hiệu suất tốt hơn với kích thước bộ đệm lớn hơn hoặc nhỏ hơn.

Điều này phụ thuộc vào một số yếu tố bao gồm kích thước khối hệ thống tệp và phần cứng CPU.

Thông thường cũng chọn công suất 2 cho kích thước bộ đệm, vì hầu hết phần cứng cơ bản được cấu trúc với khối fle và kích thước bộ đệm là sức mạnh của 2. Các lớp đệm cho phép bạn chỉ định kích thước bộ đệm trong hàm tạo. Nếu không được cung cấp, họ sử dụng một giá trị mặc định, đó là lũy thừa 2 trong hầu hết các JVM.

Cho dù bạn chọn kích thước bộ đệm nào, mức tăng hiệu suất lớn nhất bạn sẽ thấy là chuyển từ truy cập không đệm sang truy cập tệp được đệm. Điều chỉnh kích thước bộ đệm có thể cải thiện hiệu suất một chút, nhưng trừ khi bạn đang sử dụng kích thước bộ đệm cực kỳ nhỏ hoặc cực lớn, không có khả năng có tác động đáng kể.

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.