如何改善Linux中的SSD I/O吞吐量并发



下面的程序在文件中读取并解析它们。可能会更快。另一方面,如果我有几个核心和几个文件要处理,那就没关系了。我可以并行运行工作。

不幸的是,这似乎在我的拱机机器上不起作用。运行该程序的两个副本仅比运行一份副本(见下文(略快(如果有的话(,而我的驱动器能够少于20%。在具有相同硬件的Ubuntu机器上,情况有点好一些。我获得了3-4个内核的线性缩放,但我仍然以SSD Drive容量的50%的命中率。

哪些障碍会随着核心数量的增加而阻止I/O吞吐量的线性缩放,以及可以在软件/OS方面提高I/O并发的方法?

P.S。 - 对于提到下面的硬件,单个核心足够快,如果我移动到单独的线程中,则读取将是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

编辑:甚至更多详细信息

i清除内存缓存(免费页面缓存,齿条和inodes(,然后在运行上述基准测试之前,以防止Linux从缓存中拉入页面。

我的过程似乎是由CPU结合的;通过PubSetBuf切换到MMAP或更改缓冲区大小对记录的吞吐量没有明显影响。

另一方面,缩放是io-bound。如果我在运行程序之前将所有文件都带入内存缓存,请使用核心数量(现在是通过执行时间测量的,因为iostat看不到它(与内核数线性缩放。

我真正要理解的是,当我使用多个顺序读取过程从磁盘阅读时,为什么不将吞吐量刻度与最大最大读取速度接近的过程数量线性线性线性呢?为什么我要碰到I/O绑定而不饱和吞吐量?当我这样做时,如何取决于我正在运行的OS/软件堆栈?

您没有比较类似的东西。

您正在比较

Copying a file to /dev/dull with dd:

(我假设您是指/dev/null ...(

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;
}

第一个刚刚读取了原始字节,而不必关心它们是什么,然后将它们丢入位桶中。您的代码逐行读取,需要识别,然后将它们拆分为向量。

您阅读数据的方式,阅读了一条线,然后花时间处理。dd命令您将代码比较不花时间做除读取数据以外的其他事情 - 它不必读取然后读取然后读取然后读取处理...

我相信这里至少有三个问题:

1(我的读取过于定期。

我正在阅读的文件具有可预测的长度线,并具有可预测的分界符。通过随机引入1微秒延迟一千次,我能够将多个内核之间的吞吐量提高到约45mb/s。

2(我对PubSetBuf的实现实际上没有设置缓冲区大小。

如本链接所述,当指定零缓冲区大小时,标准仅指定PubSetBuf关闭缓冲区(谢谢,@andrew Henle(;所有其他行为都是实现定义的。显然,我的实现使用了8191的缓冲区大小(由strace验证(,无论我设置了什么值。

我太懒了,无法实现我自己的流缓冲以进行测试目的,我将代码重写以将1000行读取为矢量,然后尝试以第二个循环进行解析,然后重复整个过程,直到文件结束(有没有随机延迟(。这使我可以扩展到约50MB/s。

3(我的I/O调度程序和设置不适合我的驱动器和应用程序。

显然,Arch Linux默认情况下使用cfq IO调度程序对我的SSD驱动器,其中适用于HDD驱动器的参数。如下所述,将slice_sync设置为0(请参阅Mikko Rantalainen的答案,以及链接的文章(,或切换到noop调度程序,如下所述,原始代码的最大值约为60mb/s的最大吞吐量,运行四个核心。此链接也很有帮助。

使用noop调度,缩放似乎几乎是线性的,直到我的机器的四个物理内核(我有八个带有超线程(。

最新更新