Як визначити ідеальний розмір буфера при використанні FileInputStream?


156

У мене є метод, який створює MessageDigest (хеш) з файлу, і мені потрібно зробити це для багатьох файлів (> = 100 000). Наскільки великим я повинен зробити буфер, який використовується для читання з файлів, щоб досягти максимальної продуктивності?

Більшість всіх знайомий з основним кодом (який я повторю тут на всякий випадок):

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();

Який ідеальний розмір буфера для максимальної пропускної здатності? Я знаю, що це залежить від системи, і я впевнений, що її ОС, FileSystem і HDD залежать, а в суміші може бути інше обладнання / програмне забезпечення.

(Я повинен зазначити, що я дещо новачок у Java, тому це може бути просто якийсь дзвінок Java API, про який я не знаю.)

Редагувати: Я не знаю, як раніше, які системи будуть використовуватися, тому я не можу припустити багато. (Я використовую Java з цієї причини.)

Редагувати: у наведеному вище коді відсутні такі речі, як спробуйте

Відповіді:


213

Оптимальний розмір буфера пов'язаний із низкою речей: розмір блоку файлової системи, розмір кешу CPU та затримка кешу.

Більшість файлових систем налаштовані на використання розмірів блоків 4096 або 8192. Теоретично, якщо ви налаштуєте розмір буфера, щоб ви читали на кілька байт більше, ніж блок диска, операції з файловою системою можуть бути вкрай неефективними (тобто якщо ви налаштував ваш буфер для зчитування одночасно 4100 байт, кожне зчитування вимагатиме 2-х блочних читання файловою системою). Якщо блоки вже в кеш-пам'яті, ви закінчите платити ціну оперативної пам'яті -> L3 / L2 затримки кешу. Якщо вам не пощастило і блоки ще не знаходяться в кеші, ви також платите ціну затримки диска-> ОЗУ.

Ось чому ви бачите більшість буферів розміром із потужністю 2 і, як правило, більше (або дорівнює) розміру блоку диска. Це означає, що одне з прочитаних потоків може призвести до читання декількох блоків диска, але вони завжди використовуватимуть повний блок - не зчитуйте їх.

Тепер це компенсується досить типовим сценарієм потокового потоку, тому що блок, який зчитується з диска, все ще залишиться в пам’яті, коли ви натиснете наступне читання (ми робимо послідовне читання тут, зрештою) - так ви закінчите оплата оперативної пам'яті -> затримка кеш-пам’яті L3 / L2 при наступному прочитанні, але не затримка диска-> ОЗУ. Щодо порядку розміру, то затримка оперативної пам’яті на диску •> ОЗУ настільки повільна, що вона значною мірою перекриває будь-які інші затримки, з якими ви можете мати справу.

Отже, я підозрюю, що якщо ви провели тест з різними розмірами кешу (не робили цього самостійно), ви, мабуть, знайдете великий вплив розміру кешу аж до розміру блоку файлової системи. Над цим я підозрюю, що все вирівняється досить швидко.

Є ціла тонна умов і винятків тут - складність системи насправді досить приголомшлива (просто отримати ручку на L3 -> передає кеш L2 розум запаморочливо складний, і він змінюється з кожним типом процесора).

Це призводить до відповіді "реального світу": Якщо у вашому додатку приблизно 99%, встановіть розмір кешу на 8192 та перейдіть далі (ще краще, виберіть інкапсуляцію над продуктивністю та використовуйте BufferedInputStream, щоб приховати деталі). Якщо у вас є 1% додатків, які сильно залежать від пропускної здатності диска, створіть свою реалізацію, щоб ви могли обмінятися різними стратегіями взаємодії з дисками, а також забезпечити ручки та циферблати, щоб ваші користувачі могли протестувати та оптимізувати (або запропонувати деякі самооптимізуюча система).


3
Я зробив декілька banchmarking на мобільному телефоні (Nexus 5X) для свого додатка Android, як для невеликих файлів (3,5 Мб), так і для великих файлів (175 Мб). І з’ясував, що золотистий розмір складе байт [] довжиною 524288. Ну, ви можете виграти 10-20 мс, якщо переключитесь між малим буфером 4 Кб і великим буфером 524 Кб залежно від розміру файлу, але це не варто. Тож 524 Kb було найкращим варіантом у моєму випадку.
Кирило Кармазін

19

Так, це, мабуть, залежить від різних речей - але я сумніваюся, що це дуже зміниться. Я схильний вибирати 16 К або 32 К як хороший баланс між використанням пам'яті та продуктивністю.

Зауважте, що у вас повинен бути спробувати / остаточно заблокувати в коді, щоб переконатися, що потік закритий, навіть якщо викид виключається.


Я відредагував публікацію про спробу. У моєму реальному коді у мене є такий, але я залишив його, щоб зробити публікацію коротшою.
АРКБАН

1
якщо ми хочемо визначити для нього фіксований розмір, який розмір краще? 4k, 16k або 32k?
BattleTested

2
@MohammadrezaPanahi: Будь ласка, не використовуйте коментарів для користувачів, які займаються барсуками. Ви зачекали менше години перед другим коментарем. Будь ласка, пам’ятайте, що користувачі можуть легко спати або на зустрічах, або в основному зайняті іншими справами і не мають обов'язку відповідати на коментарі. Але щоб відповісти на ваше запитання: це повністю залежить від контексту. Якщо ви працюєте в дуже обмеженій пам'яті системі, ви, мабуть, хочете невеликий буфер. Якщо ви працюєте у великій системі, використання більшого буфера зменшить кількість прочитаних дзвінків. Відповідь Кевіна Дей дуже хороша.
Джон Скіт

7

У більшості випадків це насправді не так вже й важливо. Просто виберіть хороший розмір, наприклад 4K або 16K, і дотримуйтесь його. Якщо ви є позитивними , що це вузьке місце в вашому додатку, то ви повинні почати профілювання , щоб знайти розмір оптимальний буфера. Якщо ви виберете замалий розмір, ви витратите час на додаткові операції вводу / виводу та додаткові виклики функцій. Якщо ви виберете занадто великий розмір, ви почнете бачити багато пропусків кешу, які дійсно сповільнюватимуть вас. Не використовуйте буфер, більший за розмір кешу L2.


4

В ідеальному випадку у нас повинно бути достатньо пам'яті, щоб прочитати файл за одну операцію з читання. Це було б найкращим виконавцем, оскільки ми дозволяємо системі керувати файловою системою, розподільними блоками та жорстким диском. На практиці вам пощастило знати розміри файлів заздалегідь, просто використовуйте середній розмір файлу, округлений до 4 К (одиниця розподілу за замовчуванням на NTFS). І найкраще: створити орієнтир для тестування кількох варіантів.


ти маєш на увазі, що найкращий розмір буфера для читання та запису у файлі - 4k?
BattleTested

4

Ви можете використовувати BufferedStreams / Readers, а потім використовувати їх розміри буфера.

Я вважаю, що BufferedXStreams використовує 8192 як розмір буфера, але, як сказав Овідіу, ви, ймовірно, повинні провести тест на цілу купу варіантів. Її дійсно буде залежати від конфігурації файлової системи та диска щодо оптимальних розмірів.


4

Читання файлів за допомогою FileChannel Java NIO і MappedByteBuffer, швидше за все, призведе до рішення, яке буде набагато швидшим, ніж будь-яке рішення, що включає FileInputStream. В основному, картографуйте великі файли пам'яті, а для невеликих використовуйте прямі буфери.


4

У джерелі BufferedInputStream ви знайдете: приватний статичний int DEFAULT_BUFFER_SIZE = 8192;
Тож вам добре використовувати це значення за замовчуванням.
Але якщо ви зможете з’ясувати ще трохи інформації, ви отримаєте більш корисні відповіді.
Наприклад, ваш adsl, можливо, надає буфер 1454 байти, тому що навантаження TCP / IP. Для дисків ви можете використовувати значення, яке відповідає розміру блоку вашого диска.


1

Як уже згадувалося в інших відповідях, використовуйте BufferedInputStreams.

Після цього, я думаю, розмір буфера насправді не має значення. Або програма пов'язана з входом / виводом, і збільшення розміру буфера за BIS за замовчуванням не вплине на продуктивність.

Або програма пов'язана з процесором всередині MessageDigest.update (), і більшість часу не витрачається на код програми, тому налаштування не допоможе.

(Хм ... з декількома ядрами, нитки можуть допомогти.)


0

1024 підходить для найрізноманітніших обставин, хоча на практиці ви можете побачити кращі показники роботи з більшим або меншим розміром буфера.

Це залежатиме від ряду факторів, включаючи розмір блоку файлової системи та апаратне забезпечення процесора.

Також звичайно вибирати потужність 2 для розміру буфера, оскільки більшість основного обладнання обладнані розмірами блоків fle та кешу, які мають потужність 2. Класи буферизації дозволяють задати розмір буфера в конструкторі. Якщо цього не передбачено, вони використовують значення за замовчуванням, яке в більшості JVM є потужністю 2.

Незалежно від того, який розмір буфера ви виберете, найбільше збільшення продуктивності ви побачите - це перехід від небуферованого до буферизованого доступу до файлів. Регулювання розміру буфера може дещо покращити продуктивність, але якщо ви не використовуєте надзвичайно малого або надзвичайно великого розміру буфера, це навряд чи матиме суттєвий вплив.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.