คุณจะกำหนดขนาดบัฟเฟอร์ในอุดมคติได้อย่างไรเมื่อใช้ 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 บล็อกอ่านโดยระบบไฟล์) หากบล็อกอยู่ในแคชอยู่แล้วคุณต้องจ่ายค่า RAM -> เวลาแฝงแคช L3 / L2 หากคุณโชคไม่ดีและบล็อกยังไม่ได้อยู่ในแคชคุณต้องจ่ายราคาของดิสก์ -> RAM latency เช่นกัน

นี่คือเหตุผลที่คุณเห็นบัฟเฟอร์ส่วนใหญ่เป็นกำลัง 2 และโดยทั่วไปมีขนาดใหญ่กว่า (หรือเท่ากับ) ขนาดบล็อกดิสก์ ซึ่งหมายความว่าหนึ่งในการอ่านสตรีมของคุณอาจส่งผลให้มีการอ่านบล็อกดิสก์หลายบล็อก แต่การอ่านเหล่านั้นจะใช้บล็อกเต็ม - ไม่อ่านเสีย

ทีนี้สิ่งนี้ถูกชดเชยไปเล็กน้อยในสถานการณ์การสตรีมทั่วไปเพราะบล็อกที่อ่านจากดิสก์จะยังคงอยู่ในหน่วยความจำเมื่อคุณกดอ่านครั้งถัดไป จ่ายแรม -> ราคาแคช latency L3 / L2 ในการอ่านครั้งถัดไป แต่ไม่ใช่ค่า latency ของดิสก์ -> RAM ในแง่ของลำดับความสำคัญของดิสก์ -> RAM เวลาในการตอบสนองช้ามากจนทำให้เกิดความล่าช้าในการตอบสนองอื่น ๆ ที่คุณอาจต้องเผชิญ

ดังนั้นฉันสงสัยว่าถ้าคุณทำการทดสอบด้วยขนาดแคชที่แตกต่างกัน (ยังไม่ได้ทำด้วยตัวเอง) คุณอาจพบว่าขนาดแคชมีผลกระทบอย่างมากจนถึงขนาดของบล็อกระบบไฟล์ เหนือสิ่งนั้นฉันสงสัยว่าสิ่งต่าง ๆ จะลดระดับลงอย่างรวดเร็ว

มีเป็นตันความซับซ้อนของระบบที่เป็นจริงค่อนข้างส่าย - เงื่อนไขและข้อยกเว้นที่นี่ (เพียงแค่ได้รับการจัดการใน L3 -> L2 โอนแคชใจ bogglingly ซับซ้อนและมีการเปลี่ยนแปลงที่มีประเภทของ CPU ทุกครั้ง)

สิ่งนี้นำไปสู่คำตอบ 'โลกแห่งความจริง': หากแอปของคุณมี 99% ออกมาให้ตั้งค่าขนาดแคชเป็น 8192 และดำเนินการต่อ (ดียิ่งขึ้นเลือก encapsulation มากกว่าประสิทธิภาพและใช้ BufferedInputStream เพื่อซ่อนรายละเอียด) หากคุณอยู่ใน 1% ของแอพที่ขึ้นอยู่กับปริมาณงานของดิสก์มากสร้างการใช้งานของคุณเพื่อให้คุณสามารถแลกเปลี่ยนกลยุทธ์การโต้ตอบของดิสก์ที่แตกต่างกันและจัดให้มีปุ่มจับและปุ่มหมุนเพื่อให้ผู้ใช้ของคุณสามารถทดสอบและเพิ่มประสิทธิภาพ ระบบเพิ่มประสิทธิภาพตนเอง)


3
ฉันทำ banchmarking บนโทรศัพท์มือถือ (Nexus 5X) สำหรับแอพ Android ของฉันทั้ง: ไฟล์ขนาดเล็ก (3,5Mb) และไฟล์ขนาดใหญ่ (175 Mb) และพบว่าขนาดทองคำจะเป็นไบต์ [] ของความยาว 524288 คุณอาจชนะ 10-20ms ถ้าคุณสลับระหว่างบัฟเฟอร์ขนาดเล็ก 4Kb และบัฟเฟอร์ขนาดใหญ่ 524Kb ขึ้นอยู่กับขนาดไฟล์ แต่ก็ไม่คุ้มค่า ดังนั้น 524 Kb เป็นตัวเลือกที่ดีที่สุดในกรณีของฉัน
คิริลล์คาร์มิซิน

19

ใช่มันอาจขึ้นอยู่กับสิ่งต่าง ๆ - แต่ฉันสงสัยว่ามันจะสร้างความแตกต่างอย่างมาก ฉันมักจะเลือก 16K หรือ 32K เป็นสมดุลที่ดีระหว่างการใช้หน่วยความจำและประสิทธิภาพ

โปรดทราบว่าคุณควรมีการลอง / ปิดกั้นสุดท้ายในรหัสเพื่อให้แน่ใจว่ากระแสข้อมูลถูกปิดแม้ว่าจะมีข้อยกเว้นเกิดขึ้นก็ตาม


ฉันแก้ไขโพสต์เกี่ยวกับ try..catch ในรหัสจริงของฉันฉันมี แต่ฉันทิ้งมันไว้เพื่อทำให้การโพสต์สั้นลง
ARKBAN

1
ถ้าเราต้องการกำหนดขนาดคงที่สำหรับขนาดไหนดีกว่ากัน? 4k, 16k หรือ 32k?
BattleTested

2
@MohammadrezaPanahi: โปรดอย่าใช้ความคิดเห็นกับผู้ใช้แบดเจอร์ คุณรอน้อยกว่าหนึ่งชั่วโมงก่อนที่ความคิดเห็นที่สอง โปรดจำไว้ว่าผู้ใช้สามารถนอนหลับหรืออยู่ในที่ประชุมได้ง่ายหรือโดยทั่วไปยุ่งอยู่กับสิ่งอื่น ๆและไม่มีภาระผูกพันในการตอบความคิดเห็น แต่เพื่อตอบคำถามของคุณ: ทั้งหมดขึ้นอยู่กับบริบท หากคุณใช้ระบบที่ จำกัด หน่วยความจำมากคุณอาจต้องการบัฟเฟอร์ขนาดเล็ก หากคุณใช้ระบบขนาดใหญ่การใช้บัฟเฟอร์ที่มีขนาดใหญ่ขึ้นจะลดจำนวนการโทรเข้า คำตอบของ Kevin Day นั้นดีมาก
Jon Skeet

7

ในกรณีส่วนใหญ่มันไม่สำคัญมากนัก เพียงเลือกขนาดที่เหมาะสมเช่น 4K หรือ 16K และติดกับมัน หากคุณเป็นบวกที่ว่านี้เป็นคอขวดในการประยุกต์ใช้ของคุณแล้วคุณควรเริ่มต้น profiling เพื่อหาขนาดของบัฟเฟอร์ที่ดีที่สุด หากคุณเลือกขนาดที่เล็กเกินไปคุณจะเสียเวลาในการดำเนินการ I / O พิเศษและการเรียกใช้ฟังก์ชั่นพิเศษ หากคุณเลือกขนาดที่ใหญ่เกินไปคุณจะเริ่มเห็นการพลาดแคชจำนวนมากซึ่งจะทำให้คุณช้าลงอย่างมาก อย่าใช้บัฟเฟอร์ที่ใหญ่กว่าขนาดแคช L2 ของคุณ


4

ในกรณีที่เหมาะสมที่สุดเราควรมีหน่วยความจำเพียงพอที่จะอ่านไฟล์ในการดำเนินการอ่านครั้งเดียว นั่นจะเป็นนักแสดงที่ดีที่สุดเพราะเราปล่อยให้ระบบจัดการระบบไฟล์หน่วยการจัดสรรและ HDD ตามความประสงค์ ในทางปฏิบัติคุณโชคดีที่รู้ขนาดไฟล์ล่วงหน้าเพียงใช้ขนาดไฟล์เฉลี่ยที่ปัดเศษเป็น 4K (หน่วยการจัดสรรเริ่มต้นบน NTFS) และที่ดีที่สุดคือสร้างเกณฑ์มาตรฐานเพื่อทดสอบตัวเลือกหลายตัว


คุณหมายถึงขนาดบัฟเฟอร์ที่ดีที่สุดสำหรับการอ่านและเขียนในไฟล์คือ 4k?
BattleTested

4

คุณสามารถใช้ BufferedStreams / เครื่องอ่านแล้วใช้ขนาดบัฟเฟอร์ของพวกเขา

ฉันเชื่อว่า BufferedXStreams กำลังใช้ 8192 เป็นขนาดบัฟเฟอร์ แต่อย่าง Ovidiu กล่าวว่าคุณควรทำการทดสอบกับตัวเลือกทั้งหมด มันจะขึ้นอยู่กับระบบไฟล์และการตั้งค่าของดิสก์เป็นขนาดที่เหมาะสมที่สุด


4

การอ่านไฟล์โดยใช้ FileChannel ของ Java NIO และ MappedByteBuffer จะส่งผลให้เกิดโซลูชันที่เร็วกว่าโซลูชันใด ๆ ที่เกี่ยวข้องกับ FileInputStream โดยทั่วไปหน่วยความจำจะแมปไฟล์ขนาดใหญ่และใช้บัฟเฟอร์โดยตรงสำหรับไฟล์ขนาดเล็ก


4

ในแหล่งที่มาของ BufferedInputStream คุณจะพบกับ: private static int DEFAULT_BUFFER_SIZE = 8192;
ดังนั้นคุณสามารถใช้ค่าเริ่มต้นนั้นได้
แต่ถ้าคุณสามารถหาข้อมูลเพิ่มเติมได้คุณจะได้รับคำตอบที่มีค่ามากกว่า
ตัวอย่างเช่น adsl ของคุณอาจบัฟเฟอร์ล่วงหน้าที่ 1454 ไบต์นั่นเป็นเพราะเพย์โหลดของ TCP / IP สำหรับดิสก์คุณอาจใช้ค่าที่ตรงกับขนาดบล็อกของดิสก์


1

ดังที่ได้กล่าวไปแล้วในคำตอบอื่น ๆ ให้ใช้ BufferedInputStreams

หลังจากนั้นฉันเดาว่าขนาดบัฟเฟอร์นั้นไม่สำคัญ ไม่ว่าจะเป็นโปรแกรม I / O และขนาดบัฟเฟอร์ที่เพิ่มขึ้นจากค่าเริ่มต้นของ BIS จะไม่ส่งผลกระทบอย่างมากต่อประสิทธิภาพการทำงาน

หรือโปรแกรมนั้นถูกผูกไว้กับ CPU ภายใน MessageDigest.update () และเวลาส่วนใหญ่ไม่ได้ใช้ในรหัสแอปพลิเคชันดังนั้นการปรับแต่งจะไม่ช่วยได้

(อืม ... มีหลายแกนกระทู้อาจช่วยได้)


0

1024 เหมาะสำหรับสถานการณ์ที่หลากหลายแม้ว่าในทางปฏิบัติคุณอาจเห็นประสิทธิภาพที่ดีขึ้นด้วยขนาดบัฟเฟอร์ที่ใหญ่กว่าหรือเล็กกว่า

สิ่งนี้จะขึ้นอยู่กับปัจจัยหลายประการรวมถึงขนาดบล็อกระบบไฟล์และฮาร์ดแวร์ของ CPU

นอกจากนี้ยังเป็นเรื่องธรรมดาที่จะเลือกกำลัง 2 สำหรับขนาดบัฟเฟอร์เนื่องจากฮาร์ดแวร์พื้นฐานส่วนใหญ่มีโครงสร้างด้วยขนาดบล็อกและขนาดแคชที่เป็นกำลัง 2 คลาสบัฟเฟอร์ที่อนุญาตให้คุณระบุขนาดบัฟเฟอร์ในตัวสร้าง หากไม่มีการจัดเตรียมไว้พวกเขาจะใช้ค่าเริ่มต้นซึ่งเป็นกำลัง 2 ใน JVM ส่วนใหญ่

ไม่ว่าคุณจะเลือกขนาดบัฟเฟอร์ขนาดใดการเพิ่มประสิทธิภาพที่ยิ่งใหญ่ที่สุดที่คุณจะเห็นก็คือการย้ายจากการเข้าถึงไฟล์แบบไม่มีบัฟเฟอร์ไปเป็นการบัฟเฟอร์ การปรับขนาดบัฟเฟอร์อาจปรับปรุงประสิทธิภาพเล็กน้อย แต่ถ้าคุณใช้ขนาดบัฟเฟอร์ที่เล็กมากหรือใหญ่มากมันไม่น่าจะมีผลกระทบที่สำคัญ

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.