`
blueswind8306
  • 浏览: 126966 次
  • 来自: ...
社区版块
存档分类
最新评论

并发编程系列-多线程IO vs 单线程IO

阅读更多
当有很多个文件需要进行处理的时候,我们为了提高程序执行的性能,往往想当然的开多个线程并行执行文件的读/写动作。但是其实这种“想当然”是错误的,下面我们就来看看,对于磁盘IO密集型的应用,多线程到底带来了什么?

首先,我写了一段读文件的程序,这个程序支持用单线程/多线程两种方式读入多个文件,并且记录整个读文件的耗时,最后来比较一下单线程/多线程两种模型在读文件上的性能差别:
public class TestMultiThreadIO {

	/**
	 * @param args
	 * @throws IOException 
	 */
	public static void main(String[] args) throws Exception {
		if (args.length != 2) {
			throw new IllegalArgumentException("Usage: isSingle[true|false] filenames(split with ,)");
		}
		
		long startTime = System.currentTimeMillis();
		
		boolean isSingle = Boolean.parseBoolean(args[0]);
		String[] filenames = args[1].split(",");
		// 主线程先打开这组文件,为了排除掉单线程与多线程打开文件的性能差异,关注点是读文件的过程
		InputStream[] inputFiles = new InputStream[filenames.length];
		for (int i = 0; i < inputFiles.length; i++) {
			inputFiles[i] = new BufferedInputStream(new FileInputStream(filenames[i]));
		}
		
		if (isSingle) {
			System.out.println("single thread cost: " + singleThread(inputFiles) + " ms");
		} else {
			System.out.println("multi thread cost: " + multiThread(inputFiles) + " ms");
		}
		
		for (int i = 0; i < inputFiles.length; i++) {
			inputFiles[i].close();
		}
		
		System.out.println("finished, total cost: " + (System.currentTimeMillis() - startTime));
	}
	
	private static long singleThread(InputStream[] inputFiles) throws IOException {
		long start = System.currentTimeMillis();
		for (InputStream in : inputFiles) {
			while (in.read() != -1) {
			}
		}
		return System.currentTimeMillis() - start;
	}
	
	private static long multiThread(final InputStream[] inputFiles) throws Exception {
		int threadCount = inputFiles.length;
		final CyclicBarrier barrier = new CyclicBarrier(threadCount + 1);
		final CountDownLatch latch = new CountDownLatch(threadCount);
		for (final InputStream in : inputFiles) {
			Thread t = new Thread(new Runnable() {
				@Override
				public void run() {
					try {
						barrier.await();
						while (in.read() != -1) {
						}
					} catch (Exception e) {
						e.printStackTrace();
					} finally {
						latch.countDown();
					}
				}
			});
			t.start();
		}
		
		long start = System.currentTimeMillis();
		barrier.await();
		latch.await();
		return (System.currentTimeMillis() - start);
	}

}


程序写好了,下面介绍一下我的测试环境:
CPU: 24核(Intel(R) Xeon(R) CPU E5-2620 0 @ 2.00GHz)
内存:32GB
系统:64位 CentOS release 5.8

在测试之前,需要说明一下,Linux系统为了提高IO性能,对于文件的读写会由操作系统缓存起来,这就是cached的作用:
$ free -m
             total       used       free     shared    buffers     cached
Mem:         32144        818      31325          0          0          8
-/+ buffers/cache:        809      31334
Swap:         4096         38       4057

这里我先准备好了一个文件,文件名叫“0”,我们下面尝试读入这个文件:
$ dd if=0 of=/dev/null bs=1024b count=100

再用free -m看,可以看到cached空间增长了50MB:
$ free -m
             total       used       free     shared    buffers     cached
Mem:         32144        868      31276          0          0         58
-/+ buffers/cache:        808      31335
Swap:         4096         38       4057

所以,我们在测试时,为了排除系统缓存对测试的影响,应该在每次测试完成后都主动将系统缓存清空:
sync && echo 3 > /proc/sys/vm/drop_caches && echo 0 > /proc/sys/vm/drop_caches

清空后可以看到cached确实还原了:
$ free -m
             total       used       free     shared    buffers     cached
Mem:         32144        818      31326          0          0          8
-/+ buffers/cache:        809      31334
Swap:         4096         38       4057

具体有关cached/buffers的信息有兴趣的同学可以google一下。

下面开始正式测试
测试用例1:
先生成一批文件,为了方便,文件名都按照0、1、2...的序号来命名,每个文件内容不同(用dd+urandom生成),大小相同都是50MB的文件。
然后开始进行单线程读取10个文件的测试:
$ java TestMultiThreadIO true 0,1,2,3,4,5,6,7,8,9 && sync && echo 3 > /proc/sys/vm/drop_caches && echo 0 > /proc/sys/vm/drop_caches
single thread cost: 20312 ms
finished, total cost: 20322

下面是测试进行过程中,系统的各项资源开销:
----total-cpu-usage---- -dsk/total- -net/total- ---paging-- ---system--
usr sys idl wai hiq siq| read  writ| recv  send|  in   out | int   csw 
  0   0  98   2   0   0|  17M    0 | 384B  412B|   0     0 |1139   374 
  2   0  96   2   0   0|  38M    0 | 686B  522B|   0     0 |1502  1083 
  4   0  96   0   0   0|  51M    0 | 448B  412B|   0     0 |1223   194 
  4   0  96   0   0   0|  51M    0 | 448B  412B|   0     0 |1220   186 
  4   0  95   0   0   0|  50M    0 | 812B  412B|   0     0 |1215   235 
  4   0  96   0   0   0|  48M  240k| 448B  412B|   0     0 |1218   286 
  4   0  96   0   0   0|  49M   24k| 512B  412B|   0     0 |1213   213 
  4   0  96   0   0   0|  50M    0 | 622B  476B|   0     0 |1218   200 
  4   0  96   0   0   0|  51M    0 | 384B  412B|   0     0 |1215   188 
  4   0  96   0   0   0|  51M    0 | 448B  412B|   0     0 |1219   174 
  4   0  94   2   0   0|  44M    0 | 448B  412B|   0     0 |1194   339 
  4   0  94   2   0   0|  49M   24k| 862B  412B|   0     0 |1219   366 
  4   0  96   0   0   0|  51M    0 | 384B  886B|   0     0 |1215   194 
  4   0  96   0   0   0|  50M    0 | 512B 1112B|   0     0 |1218   194 
  4   0  96   0   0   0|  51M    0 | 448B  412B|   0     0 |1216   184 
  4   0  95   1   0   0|  49M   48k| 558B  540B|   0     0 |1216   298 
  4   0  96   0   0   0|  50M   24k| 384B  412B|   0     0 |1215   272 
  4   0  96   0   0   0|  50M   48k| 448B  412B|   0     0 |1220   236 
  4   0  96   0   0   0|  50M  168k| 448B  412B|   0     0 |1225   187 
  4   0  96   0   0   0|  51M    0 | 448B  476B|   0     0 |1217   178 
  4   0  96   1   0   0|  44M    0 | 384B  412B|   0     0 |1187   180 
  3   0  96   1   0   0|  38M  240k| 448B  412B|   0     0 |1208   315 

小结:程序总共耗时20s,由于是单线程读,所以cpu开销非常小,usr在4%,基本没有iowait。上下文切换开销在200~300左右。


测试用例2:
同样是这10个文件,用多线程读取(每个文件一个线程):
$ java TestMultiThreadIO false 0,1,2,3,4,5,6,7,8,9 && sync && echo 3 > /proc/sys/vm/drop_caches && echo 0 > /proc/sys/vm/drop_caches
multi thread cost: 19124 ms
finished, total cost: 19144

下面是测试进行过程中,系统的各项资源开销:
----total-cpu-usage---- -dsk/total- -net/total- ---paging-- ---system--
usr sys idl wai hiq siq| read  writ| recv  send|  in   out | int   csw 
  1   0  95   4   0   0|  29M    0 | 512B  412B|   0     0 |1515  1402 
  5   0  75  20   0   0|  54M    0 | 622B  428B|   0     0 |1253   652 
  4   0  74  21   0   0|  56M    0 | 320B  318B|   0     0 |1245   601 
  5   0  76  20   0   0|  56M    0 | 448B  318B|   0     0 |1242   602 
  4   0  84  12   0   0|  52M    0 | 622B  476B|   0     0 |1228   600 
  4   0  85  12   0   0|  45M  264k| 384B  412B|   0     0 |1201   539 
  4   0  83  12   0   0|  53M    0 | 798B  886B|   0     0 |1225   588 
  4   0  81  15   0   0|  54M    0 | 384B  412B|   0     0 |1234   596 
  4   0  86  10   0   0|  52M   16k| 384B  412B|   0     0 |1233   597 
  4   0  86   9   0   0|  53M    0 | 448B  412B|   0     0 |1237   582 
  4   0  80  16   0   0|  53M    0 | 896B  412B|   0     0 |1227   593 
  4   0  85  10   0   0|  52M   88k| 448B  412B|   0     0 |1238   607 
  4   0  83  13   0   0|  54M    0 | 384B  412B|   0     0 |1236   607 
  4   0  75  20   0   0|  55M    0 | 384B  412B|   0     0 |1238   587 
  4   0  80  15   0   0|  54M    0 | 448B  412B|   0     0 |1236   588 
  4   0  83  13   0   0|  46M    0 | 558B  476B|   0     0 |1200   528 
  4   0  77  19   0   0|  51M   16k| 448B  412B|   0     0 |1227   605 
  4   0  82  14   0   0|  52M 8192B| 448B  412B|   0     0 |1227   571 
  4   0  88   7   0   0|  56M    0 | 448B  412B|   0     0 |1245   615 
  4   0  94   2   0   0|  54M    0 | 384B  412B|   0     0 |1231   480 
  0   0  99   1   0   0|1056k  584k| 512B  884B|   0     0 |1092   235 

由于wai比较高,所以再来看一下iostat的状况(iostat命令详解参考这里):
iostat -x 1
Device:  rrqm/s  wrqm/s  r/s  w/s  rsec/s  wsec/s  avgrq-sz  avgqu-sz  await  svctm  %util
sda  103.00  2.00 228.00  0.00 54088.00     0.00   237.23     9.28   62.40   4.39 100.10
sda1  0.00  0.00  0.00  0.00     0.00     0.00     0.00     0.00    0.00   0.00   0.00
sda2  0.00  2.00  0.00  0.00     0.00     0.00     0.00     0.10    0.00   0.00   9.50
sda3  0.00  0.00  0.00  0.00     0.00     0.00     0.00     0.00    0.00   0.00   0.00
sda4  0.00  0.00  0.00  0.00     0.00     0.00     0.00     0.00    0.00   0.00   0.00
sda5  103.00  0.00 228.00  0.00 54088.00     0.00   237.23     9.18   62.40   4.39 100.10

avg-cpu:  %user   %nice %system %iowait  %steal   %idle
           3.67    0.00    0.00    5.29    0.00   91.05

小结:程序总耗时19s,usr占用依然在4%(由于我们的测试程序基本没有什么计算工作,只是简单的读文件)。但通过iostat看到util已经到达100%,每次IO等待时间达到62ms,上下文开销也增长到500~600。
可以看到,1个线程增加到10个线程,执行时间仅仅降低了1s,但系统开销大了很多,主要是阻塞在IO操作上。

如果再加大线程数会发生什么呢?下面是用10/20/50/100个线程测试的结果:


总结:可以看出,当测试文件增多时,在单线程情况下,性能没有降低。但多线程情况下,性能降低的很明显,由于IO阻塞导致CPU基本被吃满。所以在实际编码过程中,如果遇到文件读写操作,最好用一个单独的线程做,其它线程可以分配给计算/网络IO等其它地方。而且要注意多个进程之间的文件IO的影响,如果多个进程分别做顺序IO,其实全局来看(如果是一块磁盘),就变成了随机IO,也会影响系统性能。
  • 大小: 35.2 KB
分享到:
评论

相关推荐

    pandas-1.3.5-cp37-cp37m-macosx_10_9_x86_64.zip

    pandas whl安装包,对应各个python版本和系统(具体看资源名字),找准自己对应的下载即可! 下载后解压出来是已.whl为后缀的安装包,进入终端,直接pip install pandas-xxx.whl即可,非常方便。 再也不用担心pip联网下载网络超时,各种安装不成功的问题。

    基于java的大学生兼职信息系统答辩PPT.pptx

    基于java的大学生兼职信息系统答辩PPT.pptx

    基于java的乐校园二手书交易管理系统答辩PPT.pptx

    基于java的乐校园二手书交易管理系统答辩PPT.pptx

    tornado-6.4-cp38-abi3-musllinux_1_1_i686.whl

    tornado-6.4-cp38-abi3-musllinux_1_1_i686.whl

    Android Studio Ladybug(android-studio-2024.2.1.10-mac.zip.002)

    Android Studio Ladybug 2024.2.1(android-studio-2024.2.1.10-mac.dmg)适用于macOS Intel系统,文件使用360压缩软件分割成两个压缩包,必须一起下载使用: part1: https://download.csdn.net/download/weixin_43800734/89954174 part2: https://download.csdn.net/download/weixin_43800734/89954175

    基于ssm框架+mysql+jsp实现的监考安排与查询系统

    有学生和教师两种角色 登录和注册模块 考场信息模块 考试信息模块 点我收藏 功能 监考安排模块 考场类型模块 系统公告模块 个人中心模块: 1、修改个人信息,可以上传图片 2、我的收藏列表 账号管理模块 服务模块 eclipse或者idea 均可以运行 jdk1.8 apache-maven-3.6 mysql5.7及以上 tomcat 8.0及以上版本

    tornado-6.1b2-cp38-cp38-macosx_10_9_x86_64.whl

    tornado-6.1b2-cp38-cp38-macosx_10_9_x86_64.whl

    Android Studio Ladybug(android-studio-2024.2.1.10-mac.zip.001)

    Android Studio Ladybug 2024.2.1(android-studio-2024.2.1.10-mac.dmg)适用于macOS Intel系统,文件使用360压缩软件分割成两个压缩包,必须一起下载使用: part1: https://download.csdn.net/download/weixin_43800734/89954174 part2: https://download.csdn.net/download/weixin_43800734/89954175

    基于MATLAB车牌识别代码实现代码【含界面GUI】.zip

    matlab

    基于java的毕业生就业信息管理系统答辩PPT.pptx

    基于java的毕业生就业信息管理系统答辩PPT.pptx

    基于Web的毕业设计选题系统的设计与实现(springboot+vue+mysql+说明文档).zip

    随着高等教育的普及和毕业设计的日益重要,为了方便教师、学生和管理员进行毕业设计的选题和管理,我们开发了这款基于Web的毕业设计选题系统。 该系统主要包括教师管理、院系管理、学生管理等多个模块。在教师管理模块中,管理员可以新增、删除教师信息,并查看教师的详细资料,方便进行教师资源的分配和管理。院系管理模块则允许管理员对各个院系的信息进行管理和维护,确保信息的准确性和完整性。 学生管理模块是系统的核心之一,它提供了学生选题、任务书管理、开题报告管理、开题成绩管理等功能。学生可以在此模块中进行毕业设计的选题,并上传任务书和开题报告,管理员和教师则可以对学生的报告进行审阅和评分。 此外,系统还具备课题分类管理和课题信息管理功能,方便对毕业设计课题进行分类和归档,提高管理效率。在线留言功能则为学生、教师和管理员提供了一个交流互动的平台,可以就毕业设计相关问题进行讨论和解答。 整个系统设计简洁明了,操作便捷,大大提高了毕业设计的选题和管理效率,为高等教育的发展做出了积极贡献。

    机器学习(预测模型):2000年至2015年期间193个国家的预期寿命和相关健康因素的数据

    这个数据集来自世界卫生组织(WHO),包含了2000年至2015年期间193个国家的预期寿命和相关健康因素的数据。它提供了一个全面的视角,用于分析影响全球人口预期寿命的多种因素。数据集涵盖了从婴儿死亡率、GDP、BMI到免疫接种覆盖率等多个维度,为研究者提供了丰富的信息来探索和预测预期寿命。 该数据集的特点在于其跨国家的比较性,使得研究者能够识别出不同国家之间预期寿命的差异,并分析这些差异背后的原因。数据集包含22个特征列和2938行数据,涉及的变量被分为几个大类:免疫相关因素、死亡因素、经济因素和社会因素。这些数据不仅有助于了解全球健康趋势,还可以辅助制定公共卫生政策和社会福利计划。 数据集的处理包括对缺失值的处理、数据类型转换以及去重等步骤,以确保数据的准确性和可靠性。研究者可以使用这个数据集来探索如教育、健康习惯、生活方式等因素如何影响人们的寿命,以及不同国家的经济发展水平如何与预期寿命相关联。此外,数据集还可以用于预测模型的构建,通过回归分析等统计方法来预测预期寿命。 总的来说,这个数据集是研究全球健康和预期寿命变化的宝贵资源,它不仅提供了历史数据,还为未来的研究和政策制

    基于微信小程序的高校毕业论文管理系统小程序答辩PPT.pptx

    基于微信小程序的高校毕业论文管理系统小程序答辩PPT.pptx

    基于java的超市 Pos 收银管理系统答辩PPT.pptx

    基于java的超市 Pos 收银管理系统答辩PPT.pptx

    基于java的网上报名系统答辩PPT.pptx

    基于java的网上报名系统答辩PPT.pptx

    基于java的网上书城答辩PPT.pptx

    基于java的网上书城答辩PPT.pptx

    婚恋网站 SSM毕业设计 附带论文.zip

    婚恋网站 SSM毕业设计 附带论文 启动教程:https://www.bilibili.com/video/BV1GK1iYyE2B

    基于java的戒烟网站答辩PPT.pptx

    基于java的戒烟网站答辩PPT.pptx

    基于微信小程序的“健康早知道”微信小程序答辩PPT.pptx

    基于微信小程序的“健康早知道”微信小程序答辩PPT.pptx

    机器学习(预测模型):自行车共享使用情况的数据集

    Capital Bikeshare 数据集是一个包含从2020年5月到2024年8月的自行车共享使用情况的数据集。这个数据集记录了华盛顿特区Capital Bikeshare项目中自行车的租赁模式,包括了骑行的持续时间、开始和结束日期时间、起始和结束站点、使用的自行车编号、用户类型(注册会员或临时用户)等信息。这些数据可以帮助分析和预测自行车共享系统的需求模式,以及了解用户行为和偏好。 数据集的特点包括: 时间范围:覆盖了四年多的时间,提供了长期的数据观察。 细节丰富:包含了每次骑行的详细信息,如日期、时间、天气条件、季节等,有助于深入分析。 用户分类:数据中区分了注册用户和临时用户,可以分析不同用户群体的使用习惯。 天气和季节因素:包含了天气情况和季节信息,可以研究这些因素对骑行需求的影响。 通过分析这个数据集,可以得出关于自行车共享使用模式的多种见解,比如一天中不同时间段的使用高峰、不同天气条件下的使用差异、季节性变化对骑行需求的影响等。这些信息对于城市规划者、交通管理者以及自行车共享服务提供商来说都是非常宝贵的,可以帮助他们优化服务、提高效率和满足用户需求。同时,这个数据集也

Global site tag (gtag.js) - Google Analytics