Объясните Изменение в GNU C ++ filebuf :: underflow (), взаимодействующее с filebuf :: seekoff () - PullRequest
4 голосов
/ 09 сентября 2010

Продукты моей компании работают на ряде квалифицированных аппаратных / программных конфигураций 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разработчики!

Ответы [ 2 ]

1 голос
/ 10 сентября 2010

Требования 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').

Так что это не было сделано, чтобы исправить ошибку.

Ошибка Filed: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=45628

1 голос
/ 09 сентября 2010

Ну, я не знаю точную причину изменения, но, видимо, изменения были сделаны для (см. GCC 3.4 Series Changelog ):

  • Оптимизированный streambuf, filebuf, отдельно синхронизируется со стандартным потоком ввода-вывода C.
  • Поддержка больших файлов (файлы размером более 2 ГБ в 32-разрядных системах).

Я подозреваю, что поддержка больших файловэто большая функция, которая потребует такого изменения, потому что IOStreams больше не может предполагать, что он может отобразить весь файл в память.

Правильная синхронизация с cstdio также является операцией, которая может потребовать большего количествасбрасывает на диск.Вы можете отключить это, используя std::sync_with_stdio.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...