首页 > 解决方案 > 为什么逐个字符地读取字符比遍历整个文件字符串要快?

问题描述

我有一个词法分析器,它逐个字符地使用文件,寻找标记。我尝试了两种方法NextChar(),第一种直接从ifstreamthrough读取ifstream::get(ch),第二种将整个文件加载到 astd::stringstream 中以避免磁盘 I/O 开销。

获取()方法:

inline void Scanner::NextChar()
{
    inputStream.get(unscannedChar);
    currentCol++;

    while (unscannedChar == ' ')
    {
        inputStream.get(unscannedChar);
        currentCol++;
    }

    if (inputStream.eof()) {
        unscannedChar = std::char_traits<char>::eof();
    }

}

stringstream方法:虽然加载文件stringstream不需要时间,但索引非常慢。

inline void Scanner::NextChar()
{
    unscannedChar = buffer.str()[counter++];
    currentCol++;

    while (unscannedChar == ' ')
    {
        unscannedChar = buffer.str()[counter++];
        currentCol++;
    }

    
    if (counter > buffer.str().size())
    {
        unscannedChar = std::char_traits<char>::eof();
    }

}

我预计第二种方法会快得多,因为它迭代的是内存中的字符而不是磁盘上的字符,但我错了,这是我的一些测试:

| tokens    | ifstream::get()   | stringstream::str()[]     |
|--------   |-----------------  |-----------------------    |
| 5         | 0.001 (sec)       | 0.001 (sec)               |
| 800       | 0.002 (sec)       | 0.295 (sec)               |
| 21000     | 0.044 (sec)       | 693.403 (sec)             |    

NextChar()对我的项目来说非常重要,我需要尽可能快地完成它,我会很感激解释为什么我会得到以前的结果?

标签: c++iolexer

解决方案


std::ifstream已经在做自己的内部缓冲,所以不必每次调用时都必须出去等待硬盘驱动器响应get(ch);99.99% 的情况下,它已经在其内部读取缓冲区中提供了您的下一个字符,并且只需复制一个字节即可将其交给您的代码。

鉴于此,将整个文件复制到您自己的单独 RAM 缓冲区中不会获得额外的加速;确实,这样做可能会使事情变慢,因为这意味着在整个文件被读入 RAM 之前,您无法开始解析数据(而使用ifstream较小的预读缓冲区,您的代码可以尽快开始解析字符由于文件的第一部分已加载,并且解析可以在一定程度上与之后的磁盘读取并行继续)

最重要的是,每次调用它时都会按值stringstream::str()返回一个对象,如果返回很大,这可能会非常昂贵。(即,您正在为文件内容制作一个内存副本,然后为您解析的每个字符将其丢弃!)stringstring


推荐阅读