gpt4 book ai didi

c++ - 解释 GNU C++ filebuf::underflow() 与 filebuf::seekoff() 交互的变化

转载 作者:塔克拉玛干 更新时间:2023-11-03 02:13:26 25 4
gpt4 key购买 nike

我公司的产品运行在许多合格的 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++ “在中途换马”很烦人。此外,即使 general seekoff() 需要使缓冲区指针无效,为什么要等同于 ftell()

谁能指出导致这种行为变化的实现者之间的讨论线索?您是否简要描述了所涉及的选择和权衡?

额外学分

显然我真的不知道我在做什么。我正在尝试确定是否有一种方法(无论多么不可移植)在偏移量为 0 且 seekdir 为 ios::cur 的情况下绕过失效。我想到了以下 hack,直接访问 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);
}

但是,每百次 seekoff 尝试打印出位置的诊断每次都会产生 8192。嗯?由于这是 filebuf 本身的 FILE * 成员,我希望它的文件位置指针与任何 同步filebuf 进行的 underflow() 调用。为什么我错了?

更新

首先,让我强调一下,我明白我帖子的后半部分是关于非可移植黑客的。仍然,不了解这里的细节。我试着打电话

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

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

最终更新

在我的公司内部,我们已经制定了一些解决方法来降低性能影响,我们可以忍受它。

在外部,David Krauss 提交了一份针对 GNU 的 libstdc++ 流的 bug,最近,Paolo Carlini checkin 了一个修复程序。一致认为,不良行为在标准范围内,但对于我描述的边缘情况有合理的修复。

非常感谢 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);,这可以预见会使内部缓冲区无效。下一个读取操作导致下溢

Found the diff! 查看更改 -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

关于c++ - 解释 GNU C++ filebuf::underflow() 与 filebuf::seekoff() 交互的变化,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3678010/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com