【问题标题】:Performance of creating a C++ std::string from an input iterator从输入迭代器创建 C++ std::string 的性能
【发布时间】:2010-10-06 04:37:02
【问题描述】:

我正在做一些非常简单的事情:将整个文本文件从磁盘读取到std::string。我当前的代码基本上是这样做的:

std::ifstream f(filename);
return std::string(std::istreambuf_iterator<char>(f), std::istreambuf_iterator<char>());

这不太可能对程序的性能产生任何影响,但我仍然很好奇这是否是一种缓慢的方式。

字符串的构造是否存在涉及大量重新分配的风险?在读取之前使用seekg()/tellg() 计算文件大小和reserve() 字符串中的那么多空间会更好(即更快)吗?

【问题讨论】:

标签: c++ string stream


【解决方案1】:

我对您的实现 (1)、我的 (2) 以及我在 stackoverflow 上找到的另外两个(3 和 4)进行了基准测试。

结果(100 次运行的平均值;使用 gettimeofday 计时,文件为 40 段 lorem ipsum):

  • 读取文件1:764
  • 读取文件2:104
  • 读取文件3:129
  • 读取文件4:402

实现:

string readFile1(const string &fileName)
{
    ifstream f(fileName.c_str());
    return string(std::istreambuf_iterator<char>(f),
            std::istreambuf_iterator<char>());
}

string readFile2(const string &fileName)
{
    ifstream ifs(fileName.c_str(), ios::in | ios::binary | ios::ate);

    ifstream::pos_type fileSize = ifs.tellg();
    ifs.seekg(0, ios::beg);

    vector<char> bytes(fileSize);
    ifs.read(&bytes[0], fileSize);

    return string(&bytes[0], fileSize);
}

string readFile3(const string &fileName)
{
    string data;
    ifstream in(fileName.c_str());
    getline(in, data, string::traits_type::to_char_type(
                      string::traits_type::eof()));
    return data;
}

string readFile4(const std::string& filename)
{
    ifstream file(filename.c_str(), ios::in | ios::binary | ios::ate);

    string data;
    data.reserve(file.tellg());
    file.seekg(0, ios::beg);
    data.append(istreambuf_iterator<char>(file.rdbuf()),
                istreambuf_iterator<char>());
    return data;
}

【讨论】:

  • @CTT:我还对这些进行了基准测试,平均运行 100 次与 Moby Dick (1.3M) 的文本。我测试了上面显示的四个函数以及我在 SO 上找到的另外一个函数。 readFile2 的执行速度提高了大约 20%。
  • 谢谢。一个草率的基准测试在我的机器上给出了大致相同的结果。
  • 至少在 Windows 上,readFile1 和 3 不会返回与 readFile2 和 4 相同的内容。前者会将 CRLF 转换为 LF,而不是后者。
  • readFile2 可以通过将 vector 替换为: string file(filesize,'\0'); ifs.read(&file[0], 文件大小); return file;//使用 NRVO 避免复制构造函数
  • @velcrow - 在 C++11 中这是一个有效的改进。在 C++03 中,std::string 不能保证是连续的。
【解决方案2】:

如果您尝试这样做,性能会怎样?而不是问“哪种方式更快?”你可以想“嘿,我可以测量这个。”

设置一个循环,读取给定大小的文件 10000 次或其他内容,并对其计时。然后使用reserve() 方法并计时。尝试几种不同的文件大小(从小到大),看看你会得到什么。

【讨论】:

    【解决方案3】:

    老实说,我不确定,但从我所读到的内容来看,这确实取决于迭代器。对于来自文件流的迭代器,它可能没有内置方法来测量开始和结束迭代器之间的文件长度。

    如果这是正确的,它将在每次空间用完时通过类似于将其内部存储大小加倍的方式进行操作。在这种情况下,对于文件中的 n 个字符,除了将字符复制到字符串中之外,还会有 Log[n,2] 内存分配和内存删除,以及 n*Log[n,2] 个单独的字符副本。

    正如 Greg 指出的那样,您不妨对其进行测试。正如他所说,尝试两种技术的各种文件大小。此外,您可以使用以下方法来获得一些定量时间。

    #include<time.h>
    #include<iostream>
    
    ...
    
    clock_t time1=0, time2=0, delta;
    float seconds;
    
    time1=clock();
    
    //Put code to be timed here
    
    time2=clock();
    
    delta= time2-time1;
    
    seconds =(((float)delta)/((float)CLOCKS_PER_SEC));
    
    std::cout<<"The operation took: "<<seconds<<" seconds."<<std::endl;
    
    ...
    

    这应该可以解决时间问题。

    【讨论】:

      猜你喜欢
      • 2021-10-27
      • 2014-11-08
      • 1970-01-01
      • 2021-08-13
      • 1970-01-01
      • 2014-09-14
      • 2013-11-08
      • 2015-04-29
      • 2017-01-07
      相关资源
      最近更新 更多