• 浅谈文件操作并行化


    前段时间做了个raytracing的程序,过程是从两个文件分别读入所有的ray和triangle,通过计算把所有的交点输出到一个文件。这里不打算讨论计算过程中算法的优化,主要是谈谈关于文件操作的一些想法。

    一开始用的是C++标准库提供的ifstream和ofstream, 读写操作类似于:

    ifstream input;
    input >> x >> y >> z;
    ...
    ofstream output;
    output << x << y << z;

    结果发现这样的文件读写相当慢,比如说,在我双核2.16GHz,7200rpm的机子上,写2M的数据花了将近1s。


    多个文件操作的并行

    第一个尝试是将文件操作并行化,在我双核的机子上,我用两个线程并行的来读ray和triangle文件。但结果发现,这比串行来读这两个文件要多花至少5倍以上的时间。一番研究,发现应该是这两个原因引起的:

    • ifstream类的实现中用了很多lock,当多个线程同时执行到这些带锁的代码时,很多时间花在了相互等待中。这里有些不错的讨论。
    • 在单个硬盘的系统中,并行的磁盘操作会让磁头来回seek疲于奔命,从而是大大的降低的性能,而不是想象中的提升。

    所以,在不是SSD或者RAID0的系统上,让多个文件操作并行不是件明智的事情。


    将文件一次性读入或写出

    第二个尝试是注意到1s钟才写2M不合常理,在这样的硬件上应该说1s钟能写100M左右才对。分析后发现,其实ofstream在用这种方式写数据的时候,还存在一个字符串格式化的操作:将x,y,z格式化成字符串,再写到文件,这存在两个问题:

    • "格式化-写文件"这两种操作的频繁切换。
    • 多次写文件,每次只写一点点。

    于是换了个方法,先将所有的数据格式化成字符串放在一个buff中,然后一次性写入文件,速度果然提高不少,写2M的数据减少到了24ms。其实,这还有另外一个好处 - 提取出来的数据格式化的操作可以并行化了。当然,这对读文件也是一样的道理,我们只需先把所有的文件内容读入内容,然后并行的分析数据。


    文件操作与计算的并行

    我们可以注意到,第二个尝试中,当我在读文件或者写文件的时候,哪怕机器有8个核,此时也会有7个闲在那。理想的状况当然是谁都别闲着,让文件读写与计算并行:

    • 对于读,先读入一部分数据,然后另起线程开始计算,边读边算;
    • 对于写,先计算出一部分数据,然后另起一个线程开始写,遍算边写;

    但这里有两点需要注意,一是需要一部分"启动资金"(先读入一部分数据),此时文件操作与计算还未开始并行,这应该是无法避免的;二是过程中可能会有等待,要计算或者要写出的数据没跟上,这通过合理的安排应该可以解决。

    但是,并不是所有的情况都适用于文件操作与计算并行的。假设机子有n个核,计算所需的总时间为x,文件操作的总时间为y,从理论上来讲,可以推出一个公式来表示什么时候适合并行而什么时候适合串行:
    并行所需总时间:y 或者 x/(n-1)
    串行所需总时间:x/n + y
    我们只要比较并行和串行,哪种情况下总时间比较少即可,这里分两种情况来讨论:

    1. 如果y>=x/(n-1), 那么并行所需的总时间是y,与串行所需的x/n+y相比,很明显是要小(y<x/n+y),所以此时(x<=(n-1)y)并行是最佳选择。
    2. 如果y<x/(n-1) ,那么并行所需的总时间为x/(n-1)。与串行相比,化解这个等式:
      x/(n-1) ==  x/n + y
      nx == (n-1)x + n(n-1)y
      x == n(n-1)y
      所以当x>=n(n-1)y时,串行是最佳选择;x<n(n-1)y时,并行是最佳选择。

    综合以上1,2,可以得出下表:

    情况 串/并行 时间
    x<=(n-1)y 并行 y
    x<n(n-1)y 并行 x/(n-1)
    x>=n(n-1)y 串行 x/n + y

    可以看出,n(n-1)y是个分界点,如果x比她小,就是并行;如果比它大,那就是串行。

    假设在一个8核的机子上,计算时间需要10s,写文件需要1s,因为10 < 8*(8-1)*1,所以需要并行。我们来验证一下:并行所需的时间为10/7=1.43s;串行所需的时间为10/8+1=2.25s,没错;
    假设在一个8核的机子上,计算时间需要100s,写文件需要1s,因为100 > 8*(8-1)*1,所以需要串行。验证一下:并行所需的时间为100/7=14.3s;串行所需的时间为100/8+1=13.5s,也没问题。


    其实,说白了就是看单独开一个核来做文件操作值不值得,如果计算量很大而文件操作量很小,那就很不值得了。

  • 相关阅读:
    直道相思了无益 你既无心我便休
    c#与XML
    ASP.NET读取Excel文件的三大方法浅析
    当前标识(NT AUTHORITY\NETWORK SERVICE)没有对“C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\Temporary ASP.NET Files”的写访问权限。
    比较这2段HTML代码
    编码的一点思维
    代码修改的一个范例
    在aspx.cs中不出现中文?
    规则先行
    设计模式——UML简介
  • 原文地址:https://www.cnblogs.com/baiyanhuang/p/1730714.html
Copyright © 2020-2023  润新知