下面的程序从文件中读取一堆行并解析它们。它可能会更快。另一方面,如果我有多个核心和多个要处理的文件,那么这就不太重要了;我可以并行运行作业。
不幸的是,在我的 arch 机器上似乎无法正常工作。运行两个程序副本仅比运行一个副本稍微快一些(如果有的话),而且不到我的驱动器能力的20%。在具有相同硬件的 ubuntu 机器上,情况略有改善。我获得3-4个内核的线性扩展,但我仍然达到了SSD驱动器容量的约50%。
是什么障碍阻止了I/O吞吐量随着内核数量的增加而呈线性扩展,并且在软件/操作系统方面如何提高I/O并发性?
附言 - 对于下面提到的硬件,单个内核足够快,如果我将解析移到单独的线程中,则读取将成为I/O限制。还有其他优化可用于提高单核性能。但是,对于这个问题,我想专注于并发性以及我的编码和操作系统选择如何影响它。
运行我的程序:
同时运行两个程序实例,读取不同的文件:
不幸的是,在我的 arch 机器上似乎无法正常工作。运行两个程序副本仅比运行一个副本稍微快一些(如果有的话),而且不到我的驱动器能力的20%。在具有相同硬件的 ubuntu 机器上,情况略有改善。我获得3-4个内核的线性扩展,但我仍然达到了SSD驱动器容量的约50%。
是什么障碍阻止了I/O吞吐量随着内核数量的增加而呈线性扩展,并且在软件/操作系统方面如何提高I/O并发性?
附言 - 对于下面提到的硬件,单个内核足够快,如果我将解析移到单独的线程中,则读取将成为I/O限制。还有其他优化可用于提高单核性能。但是,对于这个问题,我想专注于并发性以及我的编码和操作系统选择如何影响它。
细节:
这里是几行iostat -x 1
的输出:
使用dd将文件复制到/dev/null:
Device: rrqm/s wrqm/s r/s w/s rkB/s wkB/s avgrq-sz avgqu-sz await r_await w_await svctm %util
sda 0.00 0.00 883.00 0.00 113024.00 0.00 256.00 1.80 2.04 2.04 0.00 1.13 100.00
运行我的程序:
Device: rrqm/s wrqm/s r/s w/s rkB/s wkB/s avgrq-sz avgqu-sz await r_await w_await svctm %util
sda 1.00 1.00 141.00 2.00 18176.00 12.00 254.38 0.17 1.08 0.71 27.00 0.96 13.70
同时运行两个程序实例,读取不同的文件:
Device: rrqm/s wrqm/s r/s w/s rkB/s wkB/s avgrq-sz avgqu-sz await r_await w_await svctm %util
sda 11.00 0.00 139.00 0.00 19200.00 0.00 276.26 1.16 8.16 8.16 0.00 6.96 96.70
我只能说这个效果微乎其微!增加核心数量并不能提高吞吐量,事实上它开始恶化并变得不那么一致。
这里有一个我的程序实例和一个dd实例:
Device: rrqm/s wrqm/s r/s w/s rkB/s wkB/s avgrq-sz avgqu-sz await r_await w_await svctm %util
sda 9.00 0.00 468.00 0.00 61056.00 0.00 260.92 2.07 4.37 4.37 0.00 2.14 100.00
这是我的代码:
#include <string>
#include <boost/filesystem/path.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/filesystem/operations.hpp>
#include <boost/filesystem/fstream.hpp>
typedef boost::filesystem::path path;
typedef boost::filesystem::ifstream ifstream;
int main(int argc, char ** argv) {
path p{std::string(argv[1])};
ifstream f(p);
std::string line;
std::vector<boost::iterator_range<std::string::iterator>> fields;
for (getline(f,line); !f.eof(); getline(f,line)) {
boost::split (fields, line, boost::is_any_of (","));
}
f.close();
return 0;
}
这是我编译它的步骤:
g++ -std=c++14 -lboost_filesystem -o gah.o -c gah.cxx
g++ -std=c++14 -lboost_filesystem -lboost_system -lboost_iostreams -o gah gah.o
编辑:更多细节
在运行上述基准测试之前,我清除内存缓存(释放页面缓存、目录项和索引节点),以避免Linux从缓存中拉取页面。
我的进程似乎受到CPU限制;切换到mmap或通过pubsetbuf更改缓冲区大小对记录的吞吐量没有明显影响。
另一方面,扩展性受到IO限制。如果我在运行程序之前将所有文件都带入内存缓存中,则吞吐量(现在通过执行时间来测量,因为iostat
无法查看它)与核心数成线性比例增长。
我真正想了解的是,当我使用多个顺序读取进程从磁盘读取时,为什么吞吐量不会随着进程数量线性增长,直到接近驱动器的最大读取速度?为什么我会达到I/O限制而没有饱和吞吐量,以及我何时达到这种状态取决于我运行的操作系统/软件堆栈?