代码之家  ›  专栏  ›  技术社区  ›  Don Wakefield

解释GNU+C++ FILEBUF::下流()与FILBUF::SekFoF()的交互

  •  4
  • Don Wakefield  · 技术社区  · 16 年前

    随着一个新的合格平台的引入,使用GNU C++版本3.4.4,我们开始观察一些以前没有见过的性能问题。经过一番挖掘,我们的一位工程师想出了这个测试方案:

    #include <fstream>
    #include <iostream>
    
    using namespace std;
    
    class my_filebuf : public filebuf
    {
    public:
    
       my_filebuf() : filebuf(), d_underflows(0) {};
       virtual ~my_filebuf() {};
    
       virtual pos_type seekoff(off_type, ios_base::seekdir,
                                ios_base::openmode mode = ios_base::in | ios_base::out);
    
       virtual int_type underflow();
    
    public:
       unsigned int d_underflows;
    };
    
    filebuf::pos_type my_filebuf::seekoff(
       off_type           off,
       ios_base::seekdir  way,
       ios_base::openmode mode
    )
    {
       return filebuf::seekoff(off, way, mode);
    }
    
    filebuf::int_type my_filebuf::underflow()
    {
       d_underflows++;
    
       return filebuf::underflow();
    }
    
    int main()
    {
       my_filebuf fb;
       fb.open("log", ios_base::in);
       if (!fb.is_open())
       {
          cerr << "need log file" << endl;
          return 1;
       }
    
       int count = 0;
       streampos pos = EOF;
       while (fb.sbumpc() != EOF)
       {
          count++;
    
          // calling pubseekoff(0, ios::cur) *forces* underflow
          pos = fb.pubseekoff(0, ios::cur);
       }
    
       cerr << "pos=" << pos << endl;
       cerr << "read chars=" << count << endl;
       cerr << "underflows=" << fb.d_underflows << endl;
    
       return 0;
    }
    

    我们在一个大约751KB字符的日志文件上运行了它。在前面的配置中,我们得到的结果是:

    $ buftest
    pos=768058
    read chars=768058
    underflows=0
    

    $ buftest
    pos=768058
    read chars=768058
    underflows=768059
    

    注释掉 pubseekoff(0,ios::cur) 电话和电话 下溢() 电话会消失。很明显,在较新版本的g++中 使缓冲区“失效”,强制调用 下溢() .

    我已经阅读了标准文档,以及 当然是模棱两可的。底层文件指针位置与 比如说?在或之前 之后 打电话给 下溢() 一般的 希科夫() ftell()

    额外学分

    ios::cur 数据成员 _M\U文件

    int sc(0);
    filebuf::pos_type my_filebuf::seekoff(
       off_type           off,
       ios_base::seekdir  way,
       ios_base::openmode mode
    )
    {
       if ((off == 0) && (way == ios::cur))
       {
          FILE *file =_M_file.file();
          pos_type pos = pos_type(ftell(file));
    
          sc++;
          if ((sc % 100) == 0) {
             cerr << "POS IS " << pos << endl;
          }
    
          return pos;
       }
    
       return filebuf::seekoff(off, way, mode);
    }
    

    但是,诊断程序需要每一百次打印一次位置 西科夫 每次尝试产生8192次。呵呵?既然这是 文件 * 文件buf 我希望它的文件位置指针与任何 下溢() 电话 . 为什么我错了?

    首先,让我强调一下,我明白我的文章后面的部分是关于非便携黑客的。不过,我还是不明白这里的实质。我试过打电话

    pos_type pos = _M_file.seekoff(0,ios::cur);
    

    相反,这个 在示例文件中进行,而不是停留在8192处。

    在我的公司内部,我们已经做了一些变通办法,以减少性能的影响,我们可以接受它。

    bug 针对GNU的libstdc++流,最近Paolo Carlini检查了一个补丁。一致的意见是,不希望出现的行为在标准的范围内,但是对于我描述的边缘情况有一个合理的解决方案。

    感谢StackOverflow、David Krauss、Paolo Carlini和所有GNU开发者!

    2 回复  |  直到 16 年前
        1
  •  1
  •   Potatoswatter    16 年前

    的要求 seekoff seekoff(0, ios::cur) 应该是一个特例,不同步任何东西。所以这可能被认为是一个bug。

    在GCC 4.2.1和4.5中仍然会发生这种情况

    问题是 (0, ios::cur) _M_seek ,其中 fseek 获取其返回值。只要成功, 无条件呼叫 _M_set_buffer(-1); ,这会使内部缓冲区失效。下一次读取操作导致 underflow

    Found the diff! -473,41 +486,26 . 评论是

        (seekoff): Simplify, set _M_reading, _M_writing to false, call
        _M_set_buffer(-1) ('uncommitted').
    

    所以这不是为了修复一个bug。

    http://gcc.gnu.org/bugzilla/show_bug.cgi?id=45628

        2
  •  1
  •   Billy ONeal IS4    16 年前

    GCC 3.4 Series Changelog ):

    • 大文件支持(32位系统上大于2 GB的文件)。

    我怀疑大文件支持是需要这样更改的重要特性,因为IOStreams不能再假设它可以将整个文件映射到内存中。

    正确同步 cstdio 还有一个操作可能需要对磁盘进行更多的刷新。你可以使用 std::sync_with_stdio .