【问题标题】:read input files, fastest way possible?读取输入文件,最快的方式可能吗?
【发布时间】:2011-07-19 22:57:31
【问题描述】:

我有大量浮点数形式的数据文本文件。我正在寻找用 C++ 读取它们的最快方法。如果这是最快的,我可以将文件更改为二进制文件。

如果您能给我提示或将我引至具有完整解释的网站,那就太好了。我不知道是否有任何图书馆可以快速完成工作。即使有任何开源软件可以完成这项工作,也会有所帮助。

【问题讨论】:

    标签: c++ input binary


    【解决方案1】:

    拥有二进制文件是最快的选择。您不仅可以在单个操作中使用原始istream::read 直接在数组中读取它(这非常快),而且如果您的操作系统支持,您甚至可以将文件映射到内存中;您可以在 POSIX 系统上使用 open/mmap,在 Windows 上使用 CreateFile/CreateFileMapping/MapViewOfFile,甚至可以使用 Boost 跨平台解决方案(感谢 @Cory Nelson 指出)。

    快速而肮脏的示例,假设文件包含一些 floats 的原始表示:

    “正常”读作:

    #include <fstream>
    #include <vector>
    
    // ...
    
    // Open the stream
    std::ifstream is("input.dat");
    // Determine the file length
    is.seekg(0, std::ios_base::end);
    std::size_t size=is.tellg();
    is.seekg(0, std::ios_base::beg);
    // Create a vector to store the data
    std::vector<float> v(size/sizeof(float));
    // Load the data
    is.read((char*) &v[0], size);
    // Close the file
    is.close();
    

    使用共享内存:

    #include <boost/interprocess/file_mapping.hpp>
    #include <boost/interprocess/mapped_region.hpp>
    
    using boost::interprocess;
    
    // ....
    
    // Create the file mapping
    file_mapping fm("input.dat", read_only);
    // Map the file in memory
    mapped_region region(fm, read_only);
    // Get the address where the file has been mapped
    float * addr = (float *)region.get_address();
    std::size_t elements  = region.get_size()/sizeof(float);
    

    【讨论】:

    • Boost Interprocess 提供跨平台内存映射文件。
    • @Cory:嗯,很好,不知道 Boost 也有这个。
    • 如果文件已经被缓存,内存映射将会非常快。如果没有,read 会打败它。
    • @yi_H:我认为mmap 不会比read 慢。
    • 我不知道这是否有帮助,但我已经用“正常读取”和“共享内存读取”进行了读取 1000 万浮点数的测试。使用 O3 优化,正常读取需要 185 毫秒,而增强读取需要 328 毫秒。 O3 将正​​常读取速度提高 100%,但不会改变加速读取速度。
    【解决方案2】:

    您的瓶颈在于 I/O。您希望程序以最少的 I/O 调用将尽可能多的数据读入内存。例如用一个fread 读取256 个数字比一个数字的256 个fread 快。

    如果可以的话,格式化数据文件以匹配目标平台的内部浮点表示,或者至少是你的程序的表示。这减少了将文本表示转换为内部表示的开销。

    如果可能,绕过操作系统并使用 DMA 控制器读取文件数据。 DMA 芯片将数据读入内存的负担从处理器的肩上卸下。

    压缩你的数据文件。数据文件希望位于磁盘上的一组连续扇区中。这将减少在物理盘片上寻找不同区域所花费的时间。

    您的程序是否要求对磁盘资源和处理器进行独占控制。阻止所有其他不重要的任务;提高程序执行的优先级。

    使用多个缓冲区来保持磁盘驱动器旋转。大部分时间都花在等待硬盘驱动器加速和减速上。您的程序可能正在处理数据,而其他东西正在将数据存储到缓冲区中,这会导致...

    多线程。创建一个线程读取数据并在缓冲区不为空时提醒处理任务。

    这些应该会让你忙一阵子。所有其他优化将导致可忽略不计的性能提升。 (例如直接访问硬盘控制器以传输到您的缓冲区之一。)

    【讨论】:

    • OP 有一个文本文件。加速#2,将文本文件转换为二进制文件,将极大地加快速度。跟随#1,一口气阅读尽可能多的内容。之后的一切都是肉汁。
    【解决方案3】:

    另外注意编译模式。 我试过用 1M 行解析一个文件。调试模式需要 50 秒来解析数据并附加到我的容器中。释放模式消耗至少快十倍,大约 4 秒。 下面的代码是在使用 istringstream 将数据解析为 2D 点 (,) 之前读取整个文件。

    vector <float> in_data;
    string raw_data;
    
    ifstream ifs;
    ifs.open(_file_in.c_str(), ios::binary);
    ifs.seekg(0, ios::end);
    long length = ifs.tellg();
    ifs.seekg(0, ios::beg);
    char * buffer;
    buffer = new char[length];
    ifs.read(buffer, length);
    raw_data = buffer;
    ifs.close();
    delete[]buffer;
    cout << "Size: " << raw_data.length()/1024/1024.0 << "Mb" << endl;
    istringstream _sstr(raw_data);
    string _line;
    
    while (getline(_sstr, _line)){
        istringstream _ss(_line);
        vector <float> record;
        //maybe using boost/Tokenizer is a good idea ...
        while (_ss)
        {
            string s;
            if (!getline(_ss, s, ',')) break;
            record.push_back(atof(s.c_str()));
        }
        in_data.push_back(record[0]);
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-02-12
      • 1970-01-01
      • 1970-01-01
      • 2012-08-26
      • 1970-01-01
      相关资源
      最近更新 更多