Продукты моей компании работают на нескольких квалифицированных аппаратных и программных конфигурациях 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;
}
Мы запустили его с файлом журнала размером примерно 751 КБ. В предыдущих конфигурациях мы получили результат:
$ 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()?
Может ли кто-нибудь указать мне на ветку обсуждения среди разработчиков, которая привела к этому изменению в поведении? Есть ли у вас краткое описание выбора и компромиссов?
Дополнительный кредит
Очевидно, я действительно не знаю, что делаю. Я экспериментировал, чтобы определить, есть ли способ, хотя и не переносимый, обойти аннулирование в случае, когда offset равен 0, а seekdir — 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);
}
Однако диагностика для вывода позиции каждые сто попыток seekoff каждый раз дает 8192. Хм? Поскольку это элемент FILE *
самого filebuf, я ожидаю, что его указатель позиции в файле будет синхронизирован с любым underflow() вызовы, сделанные filebuf. Почему я ошибаюсь?
Обновлять
Во-первых, позвольте мне подчеркнуть, что я понимаю, что эта последняя часть моего поста посвящена непортативным хакам. И все же, не разбираясь в мелочах здесь. я пытался позвонить
pos_type pos = _M_file.seekoff(0,ios::cur);
вместо этого, и это счастливо проходит через образец файла, а не застревает на 8192.
Окончательное обновление
Внутри моей компании мы сделали несколько обходных путей, которые снижают производительность настолько, что мы можем с этим смириться.
Извне Дэвид Краусс зарегистрировал ошибку в потоках GNU libstdc++, а недавно , Паоло Карлини проверил исправление. Все пришли к единому мнению, что нежелательное поведение находится в рамках Стандарта, но существует разумное решение для описанного мною пограничного случая.
Так что спасибо, StackOverflow, Дэвиду Крауссу, Паоло Карлини и всем разработчикам GNU!
3.4.4
— это новая платформа ;) - person Billy ONeal   schedule 09.09.2010