普陀网站开发培训学校,网站备案每年一次,基于PHP的家教网站开发环境,短视频运营公司网站建设许多应用程序将一系列事件记录到基于文件的存储中#xff0c;以供以后使用。 从日志记录和审核#xff0c;直到在事件源设计或其紧密相关的CQRS中保留事务重做日志#xff0c;这都可以是任何东西。 Java具有多种方法#xff0c;可以通过这些方法将文件顺序写入或重新读取。… 许多应用程序将一系列事件记录到基于文件的存储中以供以后使用。 从日志记录和审核直到在事件源设计或其紧密相关的CQRS中保留事务重做日志这都可以是任何东西。 Java具有多种方法可以通过这些方法将文件顺序写入或重新读取。 本文探讨了其中一些机制以了解其性能特征。 对于本文的范围我将使用预分配的文件因为我想关注性能。 不断扩展文件会带来很大的性能开销并给应用程序增加抖动从而导致高度可变的延迟。 “为什么预分配的文件性能更好”我听到您问。 好吧在磁盘上文件是由一系列包含数据的块/页面组成的。 首先重要的是这些块是连续的以提供快速的顺序访问。 其次必须分配元数据来描述此文件在磁盘上并保存在文件系统中。 典型的大文件将分配许多“间接”块以描述包含组成该元数据一部分的文件内容的数据块链。 我将其留给读者或以后的文章来练习以探讨不预先分配数据文件对性能的影响。 如果您使用过数据库则可能已经注意到它预先分配了所需的文件。 考试 我想尝试2种文件大小。 一个足够大可以测试顺序访问但可以轻松放入文件系统缓存中另一个更大可以使缓存子系统被迫退出页面以便可以加载新页面。 对于这两种情况我将分别使用400MB和8GB。 我还将遍历文件多次以显示预热和预热特性。 我将测试4种顺序写入和读取文件的方式 使用页面大小的普通字节[]的RandomAccessFile 。 缓冲的FileInputStream和FileOutputStream 。 具有页面大小的ByteBuffer的NIO FileChannel 。 使用NIO和直接MappedByteBuffer映射文件的内存。 这些测试在具有8GB RAM的2.0Ghz Sandybridge CPU具有ext4文件系统的Fedora Core 15 64位Linux上的Intel 320 SSD和Oracle JDK 1.6.0_30上运行。 编码 import java.io.*;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;import static java.lang.Integer.MAX_VALUE;
import static java.lang.System.out;
import static java.nio.channels.FileChannel.MapMode.READ_ONLY;
import static java.nio.channels.FileChannel.MapMode.READ_WRITE;public final class TestSequentialIoPerf
{public static final int PAGE_SIZE 1024 * 4;public static final long FILE_SIZE PAGE_SIZE * 2000L * 1000L;public static final String FILE_NAME test.dat;public static final byte[] BLANK_PAGE new byte[PAGE_SIZE];public static void main(final String[] arg) throws Exception{preallocateTestFile(FILE_NAME);for (final PerfTestCase testCase : testCases){for (int i 0; i 5; i){System.gc();long writeDurationMs testCase.test(PerfTestCase.Type.WRITE,FILE_NAME);System.gc();long readDurationMs testCase.test(PerfTestCase.Type.READ,FILE_NAME);long bytesReadPerSec (FILE_SIZE * 1000L) / readDurationMs;long bytesWrittenPerSec (FILE_SIZE * 1000L) / writeDurationMs;out.format(%s\twrite%,d\tread%,d bytes/sec\n,testCase.getName(),bytesWrittenPerSec, bytesReadPerSec);}}deleteFile(FILE_NAME);}private static void preallocateTestFile(final String fileName)throws Exception{RandomAccessFile file new RandomAccessFile(fileName, rw);for (long i 0; i FILE_SIZE; i PAGE_SIZE){file.write(BLANK_PAGE, 0, PAGE_SIZE);}file.close();}private static void deleteFile(final String testFileName) throws Exception{File file new File(testFileName);if (!file.delete()){out.println(Failed to delete test file testFileName);out.println(Windows does not allow mapped files to be deleted.);}}public abstract static class PerfTestCase{public enum Type { READ, WRITE }private final String name;private int checkSum;public PerfTestCase(final String name){this.name name;}public String getName(){return name;}public long test(final Type type, final String fileName){long start System.currentTimeMillis();try{switch (type){case WRITE:{checkSum testWrite(fileName);break;}case READ:{final int checkSum testRead(fileName);if (checkSum ! this.checkSum){final String msg getName() expected this.checkSum got checkSum;throw new IllegalStateException(msg);}break;}}}catch (Exception ex){ex.printStackTrace();}return System.currentTimeMillis() - start;}public abstract int testWrite(final String fileName) throws Exception;public abstract int testRead(final String fileName) throws Exception;}private static PerfTestCase[] testCases {new PerfTestCase(RandomAccessFile){public int testWrite(final String fileName) throws Exception{RandomAccessFile file new RandomAccessFile(fileName, rw);final byte[] buffer new byte[PAGE_SIZE];int pos 0;int checkSum 0;for (long i 0; i FILE_SIZE; i){byte b (byte)i;checkSum b;buffer[pos] b;if (PAGE_SIZE pos){file.write(buffer, 0, PAGE_SIZE);pos 0;}}file.close();return checkSum;}public int testRead(final String fileName) throws Exception{RandomAccessFile file new RandomAccessFile(fileName, r);final byte[] buffer new byte[PAGE_SIZE];int checkSum 0;int bytesRead;while (-1 ! (bytesRead file.read(buffer))){for (int i 0; i bytesRead; i){checkSum buffer[i];}}file.close();return checkSum;}},new PerfTestCase(BufferedStreamFile){public int testWrite(final String fileName) throws Exception{int checkSum 0;OutputStream out new BufferedOutputStream(new FileOutputStream(fileName));for (long i 0; i FILE_SIZE; i){byte b (byte)i;checkSum b;out.write(b);}out.close();return checkSum;}public int testRead(final String fileName) throws Exception{int checkSum 0;InputStream in new BufferedInputStream(new FileInputStream(fileName));int b;while (-1 ! (b in.read())){checkSum (byte)b;}in.close();return checkSum;}},new PerfTestCase(BufferedChannelFile){public int testWrite(final String fileName) throws Exception{FileChannel channel new RandomAccessFile(fileName, rw).getChannel();ByteBuffer buffer ByteBuffer.allocate(PAGE_SIZE);int checkSum 0;for (long i 0; i FILE_SIZE; i){byte b (byte)i;checkSum b;buffer.put(b);if (!buffer.hasRemaining()){buffer.flip();channel.write(buffer);buffer.clear();}}channel.close();return checkSum;}public int testRead(final String fileName) throws Exception{FileChannel channel new RandomAccessFile(fileName, rw).getChannel();ByteBuffer buffer ByteBuffer.allocate(PAGE_SIZE);int checkSum 0;while (-1 ! (channel.read(buffer))){buffer.flip();while (buffer.hasRemaining()){checkSum buffer.get();}buffer.clear();}return checkSum;}},new PerfTestCase(MemoryMappedFile){public int testWrite(final String fileName) throws Exception{FileChannel channel new RandomAccessFile(fileName, rw).getChannel();MappedByteBuffer buffer channel.map(READ_WRITE, 0,Math.min(channel.size(), MAX_VALUE));int checkSum 0;for (long i 0; i FILE_SIZE; i){if (!buffer.hasRemaining()){buffer channel.map(READ_WRITE, i,Math.min(channel.size() - i , MAX_VALUE));}byte b (byte)i;checkSum b;buffer.put(b);}channel.close();return checkSum;}public int testRead(final String fileName) throws Exception{FileChannel channel new RandomAccessFile(fileName, rw).getChannel();MappedByteBuffer buffer channel.map(READ_ONLY, 0,Math.min(channel.size(), MAX_VALUE));int checkSum 0;for (long i 0; i FILE_SIZE; i){if (!buffer.hasRemaining()){buffer channel.map(READ_WRITE, i,Math.min(channel.size() - i , MAX_VALUE));}checkSum buffer.get();}channel.close();return checkSum;}},};
} 结果 400MB file RandomAccessFile write294,041,636 read1,494,890,510 bytes/sec BufferedStreamFile写入 98,178,331读取 286,433,566字节/秒 BufferedStreamFile写入 100,244,738读取 288,857,545字节/秒 BufferedStreamFile写入 82,948,562读取 154,100,827字节/秒 BufferedStreamFile写入 108,503,311读取 153,869,271字节/秒 BufferedStreamFile写入 113,055,478读取 152,608,047字节/秒 BufferedChannelFile写入 228,443,948读取 356,173,913字节/秒 BufferedChannelFile写入 265,629,053读取 374,063,926字节/秒 BufferedChannelFile写 223,825,136读 1,539,849,624字节/秒BufferedChannelFile写 232,992,036读 1,539,849,624字节/秒BufferedChannelFile写 212,779,220读 1,534,082,397字节/秒 MemoryMappedFile写入 300,955,180读取 305,899,925字节/秒 MemoryMappedFile写入 313,149,847读取 310,538,286字节/秒 MemoryMappedFile写入 326,374,501读取 303,857,566字节/秒 MemoryMappedFile写入 327,680,000读取 304,535,315字节/秒 MemoryMappedFile写入 326895450读取 303632320字节/秒 8GB文件 RandomAccessFile写入 167,402,321读取 251,922,012字节/秒 RandomAccessFile写入 193,934,802读取 257,052,307字节/秒 RandomAccessFile写入 192,948,159读取 248,460,768字节/秒 RandomAccessFile写入 191,814,180读取 245,225,408字节/秒 RandomAccessFile写入 190,635,762读取 275,315,073字节/秒 BufferedStreamFile写入 154,823,102读取 248,355,313字节/秒 BufferedStreamFile写入 152,083,913读取 253,418,301字节/秒 BufferedStreamFile写入 133,099,369读取 146,056,197字节/秒 BufferedStreamFile写入 131,065,708读取 146,217,827字节/秒 BufferedStreamFile写入 132694052读取 148116004字节/秒 BufferedChannelFile写入 186703740读取 215075218字节/秒 BufferedChannelFile写入 190,591,410读取 211,030,680字节/秒BufferedChannelFile写入 187,220,038读取 223,087,606字节/秒 BufferedChannelFile写入 191,585,397读取 221,297,747字节/秒 BufferedChannelFile写入 192,653,214读取 211,789,038字节/秒 MemoryMappedFile写入 123,023,322读取 231,530,156字节/秒 MemoryMappedFile写入 121,961,023读取 230,403,600字节/秒 MemoryMappedFile写入 123,317,778读取 229,899,250字节/秒 MemoryMappedFile写入 121,472,738读取 231,739,745字节/秒 MemoryMappedFile写入 120,362,615读取 231,190,382字节/秒 分析 多年来我一直是直接使用RandomAccessFile的忠实拥护者 因为它提供了控制和可预测的执行。 从性能的角度来看我从来没有发现使用缓冲流会很有用而且情况似乎仍然如此。 在最近的测试中我发现使用NIO FileChannel和ByteBuffer的性能要好得多。 使用Java 7此编程方法的灵活性已得到改善可以使用SeekableByteChannel进行随机访问。 似乎在某些情况下读取RandomAccessFile和NIO可以很好地使Memory Mapped文件赢得写操作。 我看到这些结果因平台而异。 文件系统操作系统存储设备和可用内存都会产生重大影响。 在某些情况下我看到内存映射文件的性能明显优于其他文件但这需要在您的平台上进行测试因为您的行程可能会有所不同…… 推送最大吞吐量时应特别注意使用内存映射的大文件。 我经常发现操作系统可能由于虚拟内存子系统上的压力而变得无响应。 结论 从Java执行顺序文件IO的不同方法在性能上存在显着差异。 并非所有方法都遥遥相等。 对于大多数IO我发现使用ByteBuffers和Channels是IO库中最优化的部分。 如果缓冲流是您的IO库的选择那么值得进行分支并熟悉Channel和Buffer的实现甚至可以使用旧的RandomAccessFile进行回退。 参考 Mechanical Sympathy博客上的JCG合作伙伴 Martin Thompson提供的Java顺序IO性能 。 翻译自: https://www.javacodegeeks.com/2012/07/java-sequential-io-performance.html