当前位置:   article > 正文

几种文件读写方式的性能比较_c读写70m文件耗时几分钟

c读写70m文件耗时几分钟
文件读写性能受到诸多因素的影响,其中缓冲区是不容忽视的因素

下面看几个示例来比较一下各种文件操作的性能对比

1、单字节拷贝

将一个37M的文件test1.txt读出来然后写入到dest.txt,实现文件的拷贝。
在这里插入图片描述
示例代码:

	@Test
	public void perByteOperation() {
		LOGGER.info("开始单字节单字节复制文件...");
		long start = System.currentTimeMillis();
		try (FileInputStream fis = new FileInputStream(TEST_PATH + "test1.txt");
		     FileOutputStream fos = new FileOutputStream(TEST_PATH + "dest.txt")) {
			int i;
			while ((i = fis.read()) != -1) {
				fos.write(i);
			}
		} catch (Exception e) {
			LOGGER.error("单字节单字节复制文件异常", e);
		}
		LOGGER.info("结束单字节单字节复制文件...,耗时:{}ms", System.currentTimeMillis() - start);
	}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
[INFO ][2020/09/08 08:55:03][main] - 开始单字节单字节复制文件...
[INFO ][2020/09/08 08:57:31][main] - 结束单字节单字节复制文件...,耗时:148090ms
  • 1
  • 2

耗时148s,可怕!

2、采用100个字节的缓冲区

示例代码:

	@Test
	public void bufferOperationWith100() {
		LOGGER.info("开始带100字节缓冲区复制文件...");
		long start = System.currentTimeMillis();
		try (FileInputStream fis = new FileInputStream(TEST_PATH + "test1.txt");
		     FileOutputStream fos = new FileOutputStream(TEST_PATH + "dest.txt")) {
			byte[] buf = new byte[100];
			int len;
			while ((len = fis.read(buf)) != -1) {
				fos.write(buf, 0, len);
			}
		} catch (Exception e) {
			LOGGER.error("带100字节缓冲区复制文件", e);
		}
		LOGGER.info("开始带100字节缓冲区复制文件...,耗时:{}ms", System.currentTimeMillis() - start);
	}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
[INFO ][2020/09/08 08:59:05][main] - 开始带100字节缓冲区复制文件...
[INFO ][2020/09/08 08:59:07][main] - 开始带100字节缓冲区复制文件...,耗时:2237ms
  • 1
  • 2

可见,增加了缓冲区之后,速度大大提高了。

3、采用BufferedStream

BufferedStream采用了 private static int DEFAULT_BUFFER_SIZE = 8192; 8Kb的缓冲区。
示例代码:

	@Test
	public void bufferStreamByteOperation() {
		LOGGER.info("开始带缓冲区复制文件...");
		long start = System.currentTimeMillis();
		try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(TEST_PATH + "test1.txt"));
		     BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(TEST_PATH + "dest.txt"))) {
			int i;
			while ((i = bis.read()) != -1) {
				bos.write(i);
			}
		} catch (Exception e) {
			LOGGER.error("带缓冲区复制文件文件异常", e);
		}
		LOGGER.info("开始带缓冲区复制文件...,耗时:{}ms", System.currentTimeMillis() - start);
	}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
[INFO ][2020/09/08 09:01:18][main] - 开始带缓冲区复制文件...
[INFO ][2020/09/08 09:01:20][main] - 开始带缓冲区复制文件...,耗时:2350ms
  • 1
  • 2

为啥我们使用了缓冲流,没有明显提高呢?原因是虽然读取写入使用了缓冲流,但bos.write(i);方法是按单字节写入的,调用次数过多。

4、在BufferedStream基础上,写入的时候新增一个8KB的缓冲
	@Test
	public void bufferStreamBufferOperation() {
		LOGGER.info("bufferStreamBufferOperation开始带缓冲区复制文件...");
		long start = System.currentTimeMillis();
		try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(TEST_PATH + "test1.txt"));
		     BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(TEST_PATH + "dest.txt"))) {
			byte[] buf = new byte[8196];
			int i;
			while ((i = bis.read(buf)) != -1) {
				bos.write(buf, 0, i);
			}
		} catch (Exception e) {
			LOGGER.error("带缓冲区复制文件文件异常", e);
		}
		LOGGER.info("开始带缓冲区复制文件...,耗时:{}ms", System.currentTimeMillis() - start);
	}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
[INFO ][2020/09/08 09:05:02][main] - bufferStreamBufferOperation开始带缓冲区复制文件...
[INFO ][2020/09/08 09:05:02][main] - 开始带缓冲区复制文件...,耗时:132ms
  • 1
  • 2
5、不采用缓冲流,只新增8KB的缓冲数组
	@Test
	public void streamBufferOperation() {
		LOGGER.info("streamBufferOperation开始带缓冲区复制文件...");
		long start = System.currentTimeMillis();
		try (FileInputStream fis = new FileInputStream(TEST_PATH + "test1.txt");
		     FileOutputStream fos = new FileOutputStream(TEST_PATH + "dest.txt")) {
			byte[] buf = new byte[8196];
			int i;
			while ((i = fis.read(buf)) != -1) {
				fos.write(buf, 0, i);
			}
		} catch (Exception e) {
			LOGGER.error("带缓冲区复制文件文件异常", e);
		}
		LOGGER.info("开始带缓冲区复制文件...,耗时:{}ms", System.currentTimeMillis() - start);
	}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
[INFO ][2020/09/08 09:06:27][main] - streamBufferOperation开始带缓冲区复制文件...
[INFO ][2020/09/08 09:06:27][main] - 开始带缓冲区复制文件...,耗时:120ms
  • 1
  • 2
总结:

我们可以看到4,5花费的时间差不多,但我们推荐第4种方案。但在实际代码中每次需要读
取的字节数很可能不是固定的,有的时候读取几个字节,有的时候读取几百字节,这个时候有一个固定大小较大的缓冲,也就是使用 BufferedInputStream 和
BufferedOutputStream 做为后备的稳定的二次缓冲,就非常有意义了。

拓展:

DMA(直接内存访问),数据从磁盘经过总线直接发送到目标文件,无需经过内存和 CPU 进行数据中转,这种方式更快。

	/**
	 * @Description 采用的是DMA(直接内存访问)也就是数据从磁盘经过总线直接发送到目标
	 * 文件,无需经过内存和 CPU 进行数据中转:
	 * @author chenwb
	 * @date 2020/9/8 9:21
	 */
	@Test
	public void fileChannelOperation() throws IOException {
		LOGGER.info("fileChannelOperation开始复制文件...");
		long start = System.currentTimeMillis();
		FileChannel in = FileChannel.open(Paths.get(TEST_PATH + "test1.txt"), StandardOpenOption.READ);
		FileChannel out = FileChannel.open(Paths.get(TEST_PATH + "dest.txt"), StandardOpenOption.CREATE,
				StandardOpenOption.WRITE);
		in.transferTo(0, in.size(), out);
		LOGGER.info("结束复制文件...,耗时:{}ms", System.currentTimeMillis() - start);
	}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
[INFO ][2020/09/08 09:22:30][main] - fileChannelOperation开始复制文件...
[INFO ][2020/09/08 09:22:30][main] - 结束复制文件...,耗时:48ms
  • 1
  • 2
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/从前慢现在也慢/article/detail/452497
推荐阅读
相关标签
  

闽ICP备14008679号