java File Stream と FileChannel の速度比較

こんにちわ、猫好きリーマンのほげPGです。
今更ながらFileChannelがどれくらいのものか分からなかったので調べてみました。
◇それぞれの特徴
Javaには、ファイル操作に使用できるさまざまなクラスとAPIがあります。その中でも、FileStreamとFileChannelはファイルの読み書きに使用される主要なクラスです。
FileInputStream/FileOutputStream (ファイルストリーム):
FileInputStreamとFileOutputStreamは、バイト単位でファイルを読み書きするためのストリームクラスです。これらのクラスは、シンプルで直感的な方法でファイルにアクセスできます。読み込んだり書き込んだりするデータは、バイト配列として処理されます。
FileChannel (ファイルチャネル):
FileChannelは、NIO(New Input/Output)パッケージで提供されるクラスです。ファイルの読み書きを行うための高度な制御を提供します。FileChannelは、ByteBufferを使用してデータの読み書きを行います。また、非同期IOやメモリマッピングファイルなどの高度な機能も提供されます。
主な違いは次のとおりです:
APIのレベル:
FileInputStream/FileOutputStreamは、古いI/O(InputStream/OutputStream)APIに基づいており、シンプルなバイトストリームの読み書きを提供します。FileChannelは、より新しいNIOパッケージに基づいており、ByteBufferを使用してデータの読み書きを行う高度な制御を提供します。
データの扱い方:
FileInputStream/FileOutputStreamはバイト配列を使用してデータを読み書きします。FileChannelはByteBufferを使用してデータの読み書きを行います。ByteBufferは、効率的なバッファリングや直接メモリアクセスなどの機能を提供します。
機能:
FileChannelは、非同期IOやメモリマッピングファイルなどの高度な機能を提供します。一方、FileInputStream/FileOutputStreamは、シンプルなファイルの読み書きに特化しています。
要約すると、FileInputStream/FileOutputStreamはシンプルなファイルの読み書きに使用され、FileChannelはより高度な制御と機能が必要な場合に使用されます。選択は、プロジェクトの要件とデータ操作のニーズに基づいて行う必要があります。
◇いざ速度比較
比較パターンは以下
- 1ファイルの書き込み読み込み速度比較
- 複数ファイルの書き込み読み込み速度比較
1, 1ファイル(200MB)の書き込み、読み込み速度比較
File Stream (msec)
書き込み 1回目:344
書き込み 2回目:343
書き込み 3回目:344
読み込み 1回目:219
読み込み 2回目:360
読み込み 3回目:172
FileChannel (msec)
書き込み 1回目:281
書き込み 2回目:249
書き込み 3回目:234
読み込み 1回目:125
読み込み 2回目:125
読み込み 3回目:126
2, 複数ファイル(1000ファイル)の書き込み、読み込み速度比較
File Stream (msec)
書き込み:3315
読み込み:776
FileChannel (msec)
書き込み:3304
読み込み:385
◇結論
FileChannel はサイズが大きいファイルは読み込み書き込みともに速い。
さいファイルでも書き込みは変わらないが読み込みは速い。
よってガンガン使うべし。
◇計測に使ったコード
Hoge.java
import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.nio.file.Files; import java.nio.file.Paths; import java.nio.file.StandardOpenOption; import java.util.Arrays; public class Hoge { public static void main(String[] args) throws Exception { log("start ..."); // 100MB 3回 // 1KB 1000ファイル // 書き込み 読み込み Files.createDirectories(Paths.get("work")); testStream1(); testChannel1(); testStream2(); testChannel2(); log("end ..."); } static void testStream1() throws IOException { log("---testStream1---"); byte[] buf = new byte[200 * 1024 * 1024]; Arrays.fill(buf, (byte)35); for (int i=0; i<3; i++) { long t1 = System.currentTimeMillis(); File f = new File("work/ts1-" + i + ".txt"); writeFileByStream(f, buf); long t2 = System.currentTimeMillis(); log("write time: " + (t2-t1)); } for (int i=0; i<3; i++) { long t1 = System.currentTimeMillis(); File f = new File("work/ts1-" + i + ".txt"); readFileByStream(f); long t2 = System.currentTimeMillis(); log("read time: " + (t2-t1)); } } static void testChannel1() throws IOException { log("---testChannel1---"); byte[] buf = new byte[200 * 1024 * 1024]; Arrays.fill(buf, (byte)35); for (int i=0; i<3; i++) { long t1 = System.currentTimeMillis(); File f = new File("work/tc1-" + i + ".txt"); writeFileByChannel(f, buf); long t2 = System.currentTimeMillis(); log("write time:: " + (t2-t1)); } for (int i=0; i<3; i++) { long t1 = System.currentTimeMillis(); File f = new File("work/tc1-" + i + ".txt"); readFileByChannel(f); long t2 = System.currentTimeMillis(); log("read time: " + (t2-t1)); } } static void testStream2() throws IOException { log("---testStream2---"); byte[] buf = new byte[1 * 1024]; Arrays.fill(buf, (byte)35); long t1 = System.currentTimeMillis(); for (int i=0; i<1000; i++) { File f = new File("work/ts2-" + i + ".txt"); writeFileByStream(f, buf); } long t2 = System.currentTimeMillis(); log("write time: " + (t2-t1)); long t3 = System.currentTimeMillis(); for (int i=0; i<1000; i++) { File f = new File("work/ts2-" + i + ".txt"); readFileByStream(f); } long t4 = System.currentTimeMillis(); log("read time: " + (t4-t3)); } static void testChannel2() throws IOException { log("---testChannel2---"); byte[] buf = new byte[1 * 1024]; Arrays.fill(buf, (byte)35); long t1 = System.currentTimeMillis(); for (int i=0; i<1000; i++) { File f = new File("work/tc2-" + i + ".txt"); writeFileByChannel(f, buf); } long t2 = System.currentTimeMillis(); log("write time: " + (t2-t1)); long t3 = System.currentTimeMillis(); for (int i=0; i<1000; i++) { File f = new File("work/tc2-" + i + ".txt"); readFileByChannel(f); } long t4 = System.currentTimeMillis(); log("read time: " + (t4-t3)); } static void writeFileByStream(File f, byte[] val) throws IOException { try (FileOutputStream out = new FileOutputStream (f, false)) { out.write(val); } } static byte[] readFileByStream(File f) throws IOException { byte[] val = new byte[(int)f.length()]; try (FileInputStream in = new FileInputStream(f)) { in.read(val); } return val; } static void writeFileByChannel(File f, byte[] val) throws IOException { try (FileChannel fc = FileChannel.open(f.toPath(), StandardOpenOption.CREATE, StandardOpenOption.WRITE)) { ByteBuffer buf = ByteBuffer.wrap(val); fc.write(buf); } } static byte[] readFileByChannel(File f) throws IOException { try (FileChannel fc = FileChannel.open(f.toPath(), StandardOpenOption.READ)) { byte[] bytes = new byte[(int)fc.size()]; ByteBuffer buf = ByteBuffer.wrap(bytes); fc.read(buf); return bytes; } } static void log(String msg) { System.out.println(msg); }