如何从文件流中加速15M整数的加载?

dru*_*sta 13 c++ stl stream

我有一个预先计算的整数数组,它的固定大小为15M.我需要在程序启动时加载这些值.目前加载最多需要2分钟,文件大小约为130MB.是加速加载的方法吗?我也可以自由更改保存过程.

std::array<int, 15000000> keys;

std::string config = "config.dat";

// how array is saved
std::ofstream out(config.c_str());
std::copy(keys.cbegin(), keys.cend(),
  std::ostream_iterator<int>(out, "\n"));

// load of array
std::ifstream in(config.c_str());
std::copy(std::istream_iterator<int>(in),
  std::istream_iterator<int>(), keys.begin());
in_ranks.close();
Run Code Online (Sandbox Code Playgroud)

提前致谢.

解决了.使用接受答案中提出的方法.现在它只需要一眨眼.

全心全意感谢您的见解.

jos*_*rry 12

关于写入和读取操作的速度,您有两个问题.

首先,std :: copy在写入output_iterator时无法执行块复制优化,因为它无法直接访问底层目标.

其次,你将整数写成ascii而不是二进制,所以对于你的每次迭代,你的output_iterator都会创建一个int的ascii表示,并且在读取时它必须将文本解析成整数.我相信这是你的表现问题的主要原因.

你的数组的原始存储(假设一个4字节int)应该只有60MB,但由于ascii中整数的每个字符都是1字节,任何超过4个字符的整数都将大于二进制存储,因此你的130MB文件.

有一种简单的方法可以轻松地解决您的速度问题(以便可以在不同的endian或int大小的机器上读取文件)或使用std :: copy时.最简单的方法是将整个数组转储到磁盘上,然后使用fstream.write将其全部读回来,只需记住它不是严格可移植的.

来写:

std::fstream out(config.c_str(), ios::out | ios::binary);
out.write( keys.data(), keys.size() * sizeof(int) );
Run Code Online (Sandbox Code Playgroud)

阅读:

std::fstream in(config.c_str(), ios::in | ios::binary);
in.read( keys.data(), keys.size() * sizeof(int) );
Run Code Online (Sandbox Code Playgroud)

---- ----更新

如果您真的关心可移植性,可以在分发工件中轻松使用可移植格式(如初始的ascii版本),然后在程序首次运行时,它可以将该可移植格式转换为本地优化版本,以便在后续执行期间使用.

也许这样的东西:

std::array<int, 15000000> keys;

// data.txt are the ascii values and data.bin is the binary version
if(!file_exists("data.bin")) {
    std::ifstream in("data.txt");
    std::copy(std::istream_iterator<int>(in),
         std::istream_iterator<int>(), keys.begin());
    in.close();

    std::fstream out("data.bin", ios::out | ios::binary);
    out.write( keys.data(), keys.size() * sizeof(int) );
} else {
    std::fstream in("data.bin", ios::in | ios::binary);
    in.read( keys.data(), keys.size() * sizeof(int) );
}
Run Code Online (Sandbox Code Playgroud)

如果你有一个安装过程,那么这个预处理也可以在那时完成......

  • 马丁,不要误会我的意思,我完全同意,虽然我觉得我用适当的警告回答了他的问题.软件开发始终是妥协,应始终通过适当的信息和预防措施来达成妥协. (2认同)

Ste*_*owe 6

如果整数以二进制格式保存并且您不关心Endian问题,请尝试立即将整个文件读入内存(fread)并将指针强制转换为int*

  • 你甚至可以`mmap`文件并将mmap转换为`int*`. (7认同)

Aar*_*aid 6

您可以将数组预编译为.o文件,除非数据发生更改,否则无需重新编译.o文件.

thedata.hpp:

static const int NUM_ENTRIES = 5;
extern int thedata[NUM_ENTRIES];
Run Code Online (Sandbox Code Playgroud)

thedata.cpp:

#include "thedata.hpp"
int thedata[NUM_ENTRIES] = {
10
,200
,3000
,40000
,500000
};
Run Code Online (Sandbox Code Playgroud)

要编译这个:

# make thedata.o
Run Code Online (Sandbox Code Playgroud)

然后你的主应用程序看起来像:

#include "thedata.hpp"
using namespace std;
int main() {
  for (int i=0; i<NUM_ENTRIES; i++) {
    cout << thedata[i] << endl;
  }
}
Run Code Online (Sandbox Code Playgroud)

假设数据不经常更改,并且您可以处理数据以创建数据.cpp,那么这实际上是即时加载时间.我不知道编译器是否会阻塞这么大的文字数组!


Nor*_*ame 6

注意.现实检查:

从大文本文件中读取整数是一个IO绑定操作,除非你做了一些完全错误的事情(比如使用C++流).当文件已经被缓冲时,从文本文件加载15M整数在AMD64 @ 3GHZ上花费不到2秒(如果必须从足够快的磁盘中取出,则只需要一点长).这是一个快速而简单的例程来证明我的观点(这就是为什么我不检查整数格式中的所有可能错误,也不会在最后关闭我的文件,因为我还是退出()).

$ wc nums.txt
 15000000  15000000 156979060 nums.txt

$ head -n 5 nums.txt
730547560
-226810937
607950954
640895092
884005970

$ g++ -O2 read.cc
$ time ./a.out <nums.txt
=>1752547657

real    0m1.781s
user    0m1.651s
sys     0m0.114s

$ cat read.cc 
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <vector>

int main()
{
        char c;
        int num=0;
        int pos=1;
        int line=1;
        std::vector<int> res;
        while(c=getchar(),c!=EOF)
        {
                if (c>='0' && c<='9')
                        num=num*10+c-'0';
                else if (c=='-') 
                        pos=0;
                else if (c=='\n')
                {
                        res.push_back(pos?num:-num);
                        num=0;
                        pos=1;
                        line++;
                }
                else
                {
                        printf("I've got a problem with this file at line %d\n",line);
                        exit(1);
                }
        }
        // make sure the optimizer does not throw vector away, also a check.
        unsigned sum=0;
    for (int i=0;i<res.size();i++) 
    {
    sum=sum+(unsigned)res[i];
    }
    printf("=>%d\n",sum); 
}
Run Code Online (Sandbox Code Playgroud)

更新:这是使用mmap 读取文本文件(非二进制)时的结果:

$ g++ -O2 mread.cc
$ time ./a.out nums.txt
=>1752547657

real    0m0.559s
user    0m0.478s
sys     0m0.081s
Run Code Online (Sandbox Code Playgroud)

在pastebin上的代码:

我有什么建议

1-2秒是用于加载此数据的典型桌面计算机的实际下限.2分钟听起来更像是从便宜的SD卡读取60 Mhz微控制器.因此,要么您有未检测到/未提及的硬件条件,要么您的C++流实现在某种程度上被破坏或无法使用.我建议通过运行示例代码在您的计算机上为此任务建立下限.