C++ 解释对GNU C和x2B的更改+;filebuf::underflow()与filebuf::seekoff()交互
我公司的产品运行在许多合格的Linux硬件/软件配置上。历史上,编译器使用的是GNU C++。为了这篇文章的目的,让我们考虑一下版本3.2.3的基线,因为我们的软件是通过该版本工作的。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
引入了一个新的合格平台,使用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的文件)
与
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').