C++ 解释对GNU C和x2B的更改+;filebuf::underflow()与filebuf::seekoff()交互

C++ 解释对GNU C和x2B的更改+;filebuf::underflow()与filebuf::seekoff()交互,c++,g++,underflow,filebuf,C++,G++,Underflow,Filebuf,我公司的产品运行在许多合格的Linux硬件/软件配置上。历史上,编译器使用的是GNU C++。为了这篇文章的目的,让我们考虑一下版本3.2.3的基线,因为我们的软件是通过该版本工作的。 引入了一个新的合格平台,使用GNU C++版本3.4.4,我们开始观察一些以前没有见过的性能问题。经过一些挖掘,我们的一位工程师提出了这个测试方案: #include <fstream> #include <iostream> using namespace std; class my

我公司的产品运行在许多合格的Linux硬件/软件配置上。历史上,编译器使用的是GNU C++。为了这篇文章的目的,让我们考虑一下版本3.2.3的基线,因为我们的软件是通过该版本工作的。

引入了一个新的合格平台,使用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;
}
在较新版本中,结果是:

$ buftest
pos=768058
read chars=768058
underflows=768059
注释掉pubseekoff(0,ios::cur)调用,多余的underflow()调用就会消失。因此,很明显,在较新版本的g++中,调用pubseekoff()会使缓冲区“无效”,从而强制调用underflow()

我已经阅读了标准文档,关于pubseekoff()的措辞肯定是模棱两可的。例如,底层文件指针位置与gptr()的位置之间的关系是什么?在调用underflow()之前或之后?尽管如此,可以说g++“中途换马”让我感到恼火。此外,即使通用的seekoff()需要使缓冲区指针无效,为什么要使用ftell()的等价物呢

有人能给我指出导致这种行为改变的实现者之间的讨论线索吗?你有一个简洁的描述的选择和权衡涉及

额外学分 显然,我真的不知道我在做什么。我正在进行实验,以确定在偏移量为0,seekdir为ios::cur的情况下,是否有一种方法可以绕过失效,尽管这种方法不可移植。我想出了以下方法,直接访问filebuf数据成员_M_文件(只想在我的机器上用3.4.4版本编译):

取而代之的是,这个过程在示例文件中顺利进行,而不是停留在8192

最后更新 在我的公司内部,我们已经制定了一些变通办法,以减少性能损失,使我们能够接受

从外部来看,大卫·克劳斯(David Krauss)对GNU的libstdc++流提交了一份申请,最近,保罗·卡里尼(Paolo Carlini)检查了一份修复方案。一致的意见是,不希望出现的行为在标准的范围内,但对于我描述的边缘情况,有一个合理的解决方案


因此,感谢StackOverflow、David Krauss、Paolo Carlini和所有GNU开发人员

嗯,我不知道更改的确切原因,但显然更改是为了(请参阅):

  • 流线型streambuf,filebuf,与C标准I/O streambuf单独同步
  • 大文件支持(32位系统上大于2 GB的文件)
我怀疑大文件支持是需要这样更改的主要功能,因为IOStreams不能再假设它可以将整个文件映射到内存中


cstdio
正确同步也是一项可能需要对磁盘进行更多刷新的操作。您可以使用禁用该功能。

seekoff的要求肯定令人困惑,但是
seekoff(0,ios::cur)
应该是一种特殊情况,不会同步任何内容。所以这可能被认为是一个bug

在GCC 4.2.1和4.5中仍然会出现这种情况

问题在于
(0,ios::cur)
\u M_seek
中不是特例,它使用
seekoff
调用
fseek
来获取其返回值。只要成功,
\u M\u seek
无条件调用
\u M\u set\u缓冲区(-1),这会使内部缓冲区失效。下一次读取操作导致
下溢

请参见更改
-473,41+486,26
。评论是

    (seekoff): Simplify, set _M_reading, _M_writing to false, call
    _M_set_buffer(-1) ('uncommitted').
所以这并不是为了修复一个bug


归档错误:

当我看到
3.4.4
是新平台时,我仔细考虑了一下;)是的,我知道;^)~。。。我们有一个庞大的历史客户群,他们希望我们支持他们拥有的机器/配置,而不仅仅是今天的销售。我们的发现只是注意到变化出现在两个引用版本之间的某个地方。我尝试将std::sync_设置为_stdio(false),但这对两个平台上的结果都没有影响。不管怎样,谢谢你的建议。在最新的版本中它还在发生吗?因此,考虑到我们的平台需求,我们在很长一段时间内不会得到缓解!你说“seekoff(0,ios::cur)应该是一个特例……”这对我来说当然有意义,但我不记得在标准中看到过任何关于这方面的论述。你有引用吗?谢谢我已经阅读了您提交的bug(§27.8.1.4/11)中引用的标准部分,并认为“下一步,寻求…”使其成为无条件的,不管(o,ios::curr)。此外,从Paolo Carlini发布的bug中的评论来看,他似乎最终得出结论认为它们必须无效,由于它们实现了文件指针和gptr()之间的关系。因此,听起来我们只是不应该在阅读时询问职位,如果我们想要合理的表现……我在错误中添加了一条评论,以回应评论#5,解释为什么我认为他们可以将其作为特例。我希望我做对了!David,我看到您添加了libstdc++。恭喜你,虽然我暂时不能使用这个版本,谢谢!
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);
}
pos_type pos = _M_file.seekoff(0,ios::cur);
    (seekoff): Simplify, set _M_reading, _M_writing to false, call
    _M_set_buffer(-1) ('uncommitted').