Chapel语言读取文件执行时间波动原因及能否实现多次执行耗时一致
问题背景
我在分析Chapel语言串行I/O性能时,通过读取行数不同的文件并统计耗时绘制性能图表,发现即使读取行数完全相同的同一文件,每次程序运行的读取耗时都存在差异。
两次程序执行得到的性能图表如下:

每次执行测试时,测试文件的行数从10开始,按步长50递增到50000。
测试使用的代码如下:
use Time; config const quiet: bool = false; var myfile=open("/home/sayanc/Cpp_programs/t2.txt",iomode.r); var myreadingChannel=myfile.reader(locking=false);//reading channel with locking disabled var x:string; var count:int=0; //timer variable var t:Timer; t.start(); for line in myfile.lines() { count=count+1; } t.stop(); // this is the file where the time is being stored. var fout = open("/home/sayanc/Cpp_programs/chapel_locking_vs_nolocking_file_reading time.txt", iomode.rw); // Position a writing channel at the end of the file var cout = fout.openAppender(); cout.writeln("Without Locking:",t.elapsed()); cout.close(); fout.close(); myreadingChannel.close(); myfile.close(); //reading the same file this time with locking enabled myfile=open("/home/sayanc/Cpp_programs/t2.txt",iomode.r); //locking enabled myreadingChannel=myfile.reader(); count=0; var t1:Timer; t1.start(); for line in myfile.lines() { count=count+1; } t1.stop(); //writeln(count); fout = open("/home/sayanc/Cpp_programs/chapel_locking_vs_nolocking_file_reading time.txt", iomode.rw); // Position a writing channel at the end of the file cout = fout.openAppender(); cout.writeln("With locking:",t1.elapsed()); cout.close(); fout.close(); myreadingChannel.close(); myfile.close(); /* Create a writer channel with a starting offset at the end of the file */ proc file.openAppender() { var writer = this.writer(start=this.size); return writer; }
上述是读取文件的Chapel代码,展示的图表是使用locking=false选项测试得到的结果,测试用的输入文件由C++程序生成。
我的问题是:为什么相同行数的文件读取耗时在多次执行时会出现差异?是否可以实现多次执行的读取耗时保持一致?
问题解答
耗时出现差异的核心原因
- 操作系统页缓存影响:首次读取文件时,数据需要从磁盘加载到内存页缓存中,耗时较高;后续读取如果缓存未被清空/替换,会直接从内存读取,耗时大幅降低。如果测试过程中其他进程的IO操作占用了缓存空间,目标文件的缓存被替换,下一次读取又会回到磁盘IO的耗时水平,这是波动的最主要来源。
- 系统资源竞争:操作系统后台的定时任务、日志写入、其他进程的CPU/IO占用,都会抢占测试程序的运行资源,导致IO调度、代码执行的延迟出现波动。
- 运行时与调度开销:Chapel运行时的内存分配、进程调度的时间片分配差异、CPU动态调频带来的算力波动,也会带来小范围的耗时差异。
- 测试代码逻辑缺陷:你当前的测试代码中,创建了配置了locking参数的reader后,实际迭代行用的是
myfile.lines(),这个调用会生成全新的默认reader,你设置的locking参数实际没有生效,也可能带来测试结果的不可控。
降低耗时波动的可行方案
完全消除耗时差异是不可能的,操作系统层面的调度、资源分配本身存在不可避免的随机波动,但通过以下方法可以将波动控制在可接受的极小范围内:
- 统一缓存状态:如果测试冷读性能,每次测试前执行
sync && echo 3 > /proc/sys/vm/drop_caches(需要root权限)清空系统页缓存;如果测试热读性能,提前读取一次目标文件将数据加载到缓存后再开始测试。 - 隔离测试环境:测试时关闭所有无关后台进程,避免同时运行其他占用CPU、磁盘IO的任务,还可以通过
chrt命令给测试进程设置更高的调度优先级,减少调度干扰。 - 优化测试逻辑:
- 单组测试重复执行至少10次,去掉最高、最低的异常值后取平均值作为统计结果,排除单次波动的影响
- 修复代码逻辑,将
myfile.lines()改为myreadingChannel.lines(),保证locking配置生效 - 计时范围仅保留核心的行读取逻辑,将文件打开、关闭、结果写入等操作放到计时范围外,减少无关操作的干扰
- 固定硬件配置:关闭CPU动态调频、Turbo Boost功能,将CPU固定为最高性能模式,避免算力波动带来的耗时差异。
内容的提问来源于stack exchange,提问作者Sayan Barrie Mccullum
相关产品推荐
相关产品推荐

