在C ++中使用线程读取文件

时间:2014-01-20 07:34:40

标签: c++ linux io pthreads getline

我编写了一个程序(使用FFTW)对OpenFOAM编写的一些数据文件进行傅里叶变换。

程序首先找到每个数据文件的路径(我当前示例中的501个文件),然后在线程之间拆分路径,这样thread0得到路径0-> 61,thread1得到62-> 123左右等,然后在最后串行运行剩余的文件。

我已经在整个代码中实现了定时器,以尝试查看瓶颈的位置,因为串行运行每个文件需要大约3.5秒,并且并行运行8个文件所需的时间大约是21秒(从8x3.5减少到28s) (连续时间),但不是那么多)

我的代码中有问题的部分在

下面
if (DIAG_timers) {readTimer = timerNow();}
for (yindex=0; yindex<ycells; yindex++)
{
    for (xindex=0; xindex<xcells; xindex++)
    {
        getline(alphaFile, alphaStringValue);
        convertToNumber(alphaStringValue, alphaValue[xindex][yindex]);
    }
}
if (DIAG_timers) {endTimerP(readTimer, tid, "reading value and converting", false);}

这里,timerNow()返回时钟值,endTimerP计算以ms为单位的时间。 (其余参数与它在并行线程中运行有关,以避免为每个循环输出8行等,以及定时器测量的描述)。

convertToNumber获取alphaStringValue上的值,并将其转换为double,然后将其存储在alphaValue数组中。

alphaFile是一个std :: ifstream对象,alphaStringValue是一个std :: string,用于存储每行的文本。

要读取的文件大约每个40MB(只有几行超过5120000,每行只包含一个值,介于0和1之间(大多数情况下==(0 || 1)),我有16GB的文件RAM,所以将所有文件复制到内存当然是可能的,因为只有8个(每个线程1个)应该一次打开。我不确定mmap是否能做得更好?stackoverflow上的几个线程争论mmap的优点与更多直接的读操作,特别是顺序访问,所以我不知道这是否有益。

我尝试使用互斥锁围绕代码块,以便只有一个线程可以同时运行块,以防多个文件读取导致通过模糊随机访问导致缓慢的IO,但这只是将进程简化为大致串行速度次。

任何允许我更快地运行此部分的建议,可能是通过复制文件,或者其他任何其他内容,我们将不胜感激。

编辑:

template<class T> inline void convertToNumber(std::string const& s, T &result)
{
    std::istringstream i(s);
    T x;
    if (!(i >> x))
        throw BadConversion("convertToNumber(\"" + s + "\")");
    result = x;
}

原来是慢节。我假设这是由于每个文件创建了500万个字符串流,然后测试了500万个条件?用TonyD的建议替换它可能会消除捕获错误的可能性,但会节省大量(至少在这种受控情况下)不必要的操作。

2 个答案:

答案 0 :(得分:1)

  

要读取的文件大约每个40MB(只有几行超过5120000,每行只包含一个值,介于0和1之间(大多数情况下==(0 || 1)),我有16GB的文件RAM,所以当然可以将所有文件复制到内存中,

是。但是加载它们仍然会计入你的进程的挂钟时间,除非它们之前已经被其他进程读过了。

  

因为一次只能打开8个(每个线程1个)。

由于在进程启动之前未加载到内存中的任何文件都必须加载,因此加载将计入进程挂钟时间,一次打开多少文件并不重要。任何非缓存都会减慢进程。

  

我不确定mmap是否会做得更好?

不,它不会。 mmap 更快,但是因为它将副本从内核缓冲区保存到应用程序缓冲区和一些系统调用开销(阅读时,您为每个页面执行内核条目,而使用预读的mmap页面不会导致进一步的页面错误)。但如果它们尚未缓存,它将为您节省从磁盘读取文件的时间。

mmap不会在内存中加载任何内容。内核将数据从磁盘加载到内部缓冲区,即页面缓存。 read将数据从那里复制到您的应用程序缓冲区,而mmap直接在您的地址空间中公开部分页面缓存。但是在任何一种情况下,数据都是在第一次访问时获取并保留在那里直到内存管理器将它们丢弃以重用内存。页面缓存是全局的,因此如果一个进程导致某些数据被缓存,则下一个进程将使它们更快。但是如果它在较长时间后首次访问,则必须读取数据,这将以完全相同的方式影响readmmap

由于并行化过程并没有大大改善时间,因此大多数情况下似乎是实际的I / O.所以你可以进一步优化,mmap可以提供帮助,但不要期望太多。改善I / O时间的唯一方法是获得更快的磁盘。


您应该能够让系统告诉您在CPU上花了多少时间以及使用getrusage(2)等待数据(I / O)花费了多少时间(在每个线程结束时调用它)获取该线程的数据)。因此,您可以确认I / O花费了多少时间。

答案 1 :(得分:0)

mmap无疑是将大量数据存入内存的最有效方法。这里的主要好处是不涉及额外的复制。

但是,它确实使代码稍微复杂一些,因为你不能直接使用文件I / O函数来使用mmap(如果使用"m",主要好处就会丢失stdio函数的模式,因为你现在至少得到一个副本)。从我过去做过的实验中,mmap比其他文件阅读变量更胜一筹。多少取决于等待磁盘所花费的总时间的比例,以及实际处理文件内容所花费的时间。