向GNU C ++ FileBuf :: UnderFlof()与FileBuf :: SeekOff()进行交互说明更改
题
我公司的产品以许多合格的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').
因此,这不是为了修复错误。
其他提示
好吧,我不知道更改的确切原因,但显然是为了进行更改(请参阅 GCC 3.4系列Changelog):
- 简化的StreamBuf,FileBuf,与C标准I/O StreamBuf单独同步。
- 大型文件支持(在32位系统上大于2 GB的文件)。
我怀疑大型文件支持是需要这样的更改的重要功能,因为iostreams不再认为它可以将整个文件映射到内存中。
正确同步 cstdio
这也是一个可能需要更多冲洗磁盘的操作。您可以禁用使用 std::sync_with_stdio
.