为什么使用 istreambuf 迭代器读取文件会随着重复执行而变得更快?

Why reading a file using istreambuf iterators gets faster with repeated execution?

我正在寻找一种将整个文件读入字符串的方法。在网上找了几个技巧,决定对其中两个进行测试,但是结果很奇怪。

我在 Windows 10 笔记本电脑上使用 Visual Studio Community 2019(版本 16.0.3)。文件 "my_text.txt" 的长度为 2,235,259 个字符,大小为 2.183 MB。

完整代码如下:

#include <chrono>
#include <fstream>
#include <iostream>
#include <string>

// first technique
void read_string_1(std::ifstream& fstr, std::string& result)
{
    fstr.seekg(0, std::ios::end);
    size_t length = fstr.tellg();
    fstr.seekg(0);
    result = std::string(length + 1, '[=10=]');
    fstr.read(&result[0], length);
}

// second technique
void read_string_2(std::ifstream& fstr, std::string& result)
{
    result = std::string( (std::istreambuf_iterator<char>(fstr)), (std::istreambuf_iterator<char>()) );
}

int main()
{
    std::ifstream ifile{ "my_text.txt", std::ios_base::binary };
    if (!ifile)
        throw std::runtime_error("Error!");

    std::string content;

    for (int i = 0; i < 10; ++i)
    {
        std::chrono::high_resolution_clock::time_point p1 = std::chrono::high_resolution_clock::now();
        read_string_1(ifile, content);
        std::chrono::high_resolution_clock::time_point p2 = std::chrono::high_resolution_clock::now();
        auto duration1 = std::chrono::duration_cast<std::chrono::microseconds>(p2 - p1).count();
        std::cout << "M1:" << duration1 << std::endl;
    }

    for (int i = 0; i < 10; ++i)
    {
        std::chrono::high_resolution_clock::time_point p3 = std::chrono::high_resolution_clock::now();
        read_string_2(ifile, content);
        std::chrono::high_resolution_clock::time_point p4 = std::chrono::high_resolution_clock::now();
        auto duration2 = std::chrono::duration_cast<std::chrono::microseconds>(p4 - p3).count();
        std::cout << "M2:" << duration2 << std::endl;
    }

    return 0;
}

结果如下:

情况一:先调用read_string_1(),再调用read_string_2()。

M1:7389
M1:8821
M1:6303
M1:6725
M1:5951
M1:8097
M1:5651
M1:6156
M1:6110
M1:5848
M2:827
M2:15
M2:15
M2:15
M2:14
M2:13
M2:14
M2:13
M2:14
M2:14

情况 2:先调用 read_string_2(),然后调用 read_string_1()。

M1:940311
M1:352
M1:16
M1:13
M1:15
M1:15
M1:13
M1:13
M1:14
M1:14
M2:4668
M2:4761
M2:4881
M2:7446
M2:5050
M2:5572
M2:5255
M2:5108
M2:5234
M2:5072

当然每次的结果都不同,但它们遵循一个通用的模式。如您所见,read_string_1() 非常一致,但 read_string_2() 的执行时间令人费解。为什么在这两种情况下,重复执行都会变得更快?为什么在第2个案例中,第一个运行执行的时间这么长?后台发生了什么?难道我做错了什么?最后,哪个函数更快,read_string_1() 或 read_string_2()?

由于缓存,执行变得更快。

搜索时,浏览文件需要时间。所以虽然有些东西被缓存了,但区别并没有那么大。通过直接读取,可以缓存文件内容本身。所以再次读取它只是指向缓存内存的指针。

第一次尝试需要多长时间取决于缓存中的内容和操作本身。