向GNU C ++ FileBuf :: UnderFlof()与FileBuf :: SeekOff()进行交互说明更改

StackOverflow https://stackoverflow.com/questions/3678010

  •  01-10-2019
  •  | 
  •  

我公司的产品以许多合格的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;
}

我们使用约751kb字符的日志文件运行。在以前的配置中,我们得到了结果:

$ buftest
pos=768058
read chars=768058
underflows=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为0的情况下是否有办法(无论便携式)是否有办法(无论可移植) ios :: cur. 。我想到了以下黑客,直接访问 filebuf 数据成员 _m_file (这只想在我的计算机上使用3.4.4版本编译):

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。嗯?因为这是 文件 * 成员 filebuf 本身,我希望它的文件位置指针与任何 Underflow()filebuf. 。我为什么错了?

更新

首先,让我强调,我理解我的帖子后期部分是关于不可携带的黑客的。尽管如此,在这里不了解小事。我尝试打电话

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

相反,这个 快乐 通过示例文件进行进展,而不是卡在8192。

最终更新

在我公司的内部,我们进行了一些解决方法,以减少表现的打击,我们可以忍受它。

在外部,大卫·克劳斯(David Krauss)提交了 漏洞 与GNU的LibstDC ++流相反,最近,Paolo Carlini检查了一个修复程序。共识是,不希望的行为在标准的范围内,但我描述的边缘情况有一个合理的修复。

因此,谢谢Stackoverflow,David Krauss,Paolo Carlini和所有GNU开发人员!

有帮助吗?

解决方案

要求 seekoff 当然令人困惑,但是 seekoff(0, ios::cur) 应该是一种不同步任何东西的特殊情况。因此,这可能被认为是一个错误。

它仍然发生在GCC 4.2.1和4.5…中

问题是 (0, ios::cur) 不是特别 _M_seek, , 哪个 seekoff 用来打电话 fseek 获取其返回值。只要成功, _M_seek 无条件打电话 _M_set_buffer(-1);, ,这可以预见,内部缓冲区无效。下一个读取操作原因 underflow.

找到了差异! 请参阅更改 -473,41 +486,26. 。评论是

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

因此,这不是为了修复错误。

提交错误: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=45628

其他提示

好吧,我不知道更改的确切原因,但显然是为了进行更改(请参阅 GCC 3.4系列Changelog):

  • 简化的StreamBuf,FileBuf,与C标准I/O StreamBuf单独同步。
  • 大型文件支持(在32位系统上大于2 GB的文件)。

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

正确同步 cstdio 这也是一个可能需要更多冲洗磁盘的操作。您可以禁用使用 std::sync_with_stdio.

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top