Продукты моей компании работают на ряде квалифицированных аппаратных / программных конфигураций 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 ++ «изменил лошадей в середине», так сказать.Более того, даже если general seekoff () требует аннулирования указателей буфера, почему эквивалентен ftell () ?
Может кто-нибудьуказать мне на дискуссию среди разработчиков, которая привела к этому изменению в поведении?У вас есть краткое описание вариантов и компромиссов?
Дополнительные кредиты
Очевидно, я действительно не знаю, что я делаю.Я экспериментировал, чтобы определить, был ли способ, хотя и непереносимый, обойти аннулирование в случае, когда смещение равно 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);
}
Однако, диагностика для распечатывания позиции каждые сто перебросок попыток дает 8192 каждый раз.А?Поскольку это FILE *
члена самого filebuf , я бы ожидал, что его указатель положения файла будет синхронизирован с любыми вызовами underflow () сделанный filebuf .Почему я не прав?
Обновление
Во-первых, позвольте мне подчеркнуть, что я понимаю, что эта более поздняя часть моего поста посвящена непереносимым хакерам.Тем не менее, не понимая мельчайших здесь.Я попытался позвонить
pos_type pos = _M_file.seekoff(0,ios::cur);
вместо этого, и это счастливо проходит через файл примера, а не застревает на 8192.
Окончательное обновление
Внутри моей компании мы сделали несколько обходных путей, которые настолько снижают производительность, что мы можем с ней жить.
Внешне Дэвид Краусс подал ошибку против потоков GNU libstdc ++, а недавно,Паоло Карлини проверил исправление.Все согласились с тем, что нежелательное поведение входит в сферу действия Стандарта, но есть разумное решение для описанного мною крайнего случая.
Так что спасибо, StackOverflow, Дэвид Краусс, Паоло Карлини и все GNUразработчики!