Spiegare il cambiamento di GNU C ++ filebuf :: underflow () che interagiscono con filebuf :: seekoff ()

StackOverflow https://stackoverflow.com/questions/3678010

  •  01-10-2019
  •  | 
  •  

Domanda

mia azienda prodotti eseguiti su un certo numero di configurazioni hardware / software qualificati Linux. Storicamente, il compilatore utilizzato è stato GNU C ++. Ai fini di questo post, consideriamo la versione 3.2.3 la linea di base, come il nostro software 'ha lavorato come previsto' attraverso tale versione.

Con l'introduzione di una piattaforma più recente qualificato, utilizzando la versione 3.4.4 GNU C ++, abbiamo cominciato a osservare alcuni problemi di prestazioni che non avevamo mai visto prima. Dopo un po 'di scavo, uno dei nostri tecnici si avvicinò con questo programma di test:

#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;
}

Abbiamo corso contro un file di registro di circa 751KB caratteri. Nelle configurazioni precedenti, abbiamo ottenuto il risultato:

$ buftest
pos=768058
read chars=768058
underflows=0

Nella versione più recente, il risultato è il seguente:

$ buftest
pos=768058
read chars=768058
underflows=768059

Commento il pubseekoff (0, ios :: corr) di chiamata e l'eccessiva underflow () chiamate andare via. Quindi, chiaramente, nelle versioni più recenti di g ++, chiamando pubseekoff () 'invalida' il buffer, costringendo una chiamata a underflow () .

Ho letto il documento standard, e la verbosità su pubseekoff () è certamente ambiguo. Qual è la relazione tra la posizione del puntatore file sottostante a quello di gptr () , per esempio? Prima o dopo una chiamata a underflow () ? Indipendentemente da ciò, trovo irritante che g ++ 'cavalli cambiati a metà del guado', per così dire. Inoltre, anche se a Generale seekoff () richiesta invalidare i puntatori di buffer, perché dovrebbe l'equivalente di ftell ()

qualcuno mi può puntare a un thread di discussione tra gli implementatori che ha portato fino a questo cambiamento di comportamento? Avete una succinta descrizione delle scelte e compromessi coinvolti?

Credit Extra

Chiaramente io davvero non so cosa sto facendo. Stavo sperimentando per determinare se ci fosse un modo, però non portatile, per bypassare l'invalidazione nel caso in cui offset è 0 e seekdir è ios :: cur . Sono venuto con la seguente hack, accedendo direttamente al filebuf membro di dati _M_file (questo voleva solo per compilare con la versione 3.4.4 sulla mia macchina):

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);
}

Tuttavia, la diagnostica per stampare la posizione ogni cento seekoff tentativi rendimenti 8192 ogni volta. Eh? Poiché questo è il FILE membro * del filebuf per sé, mi aspetto che del puntatore posizione di file per essere in sintonia con qualsiasi underflow () le chiamate effettuate dal filebuf . Perché mi sbaglio?

Aggiorna

In primo luogo, vorrei sottolineare che ho capito questa parte successiva del mio post è tutto su hack non portatili. Eppure, senza capire il nocciolo qui. Ho provato a chiamare

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

, invece, e questo felicemente progredisce attraverso il file di esempio, invece di rimanere bloccati al 8192.

Aggiornamento finale

Internamente alla mia azienda, abbiamo fatto alcune soluzioni che riducono le prestazioni colpito abbastanza possiamo vivere con esso.

Esternamente, David Krauss ha presentato una bug contro flussi di GNU libstdc ++ , e di recente, Paolo Carlini registrato una correzione. Il consenso era che il comportamento indesiderato è nell'ambito della norma, ma che c'era una soluzione ragionevole per il caso limite ho descritto.

Quindi grazie, StackOverflow, David Krauss, Paolo Carlini, e tutti gli sviluppatori GNU!

È stato utile?

Soluzione

I requisiti di seekoff certamente sono confuse, ma seekoff(0, ios::cur) si suppone che sia un caso speciale che non fa nulla di sincronizzazione. Quindi questo potrebbe probabilmente essere considerato un bug.

E succede ancora in GCC 4.2.1 e 4.5 ...

Il problema è che non è speciale (0, ios::cur)-carter in _M_seek, che utilizza seekoff a chiamata fseek per ottenere il suo valore di ritorno. Finché che riesce, _M_seek chiama incondizionatamente _M_set_buffer(-1);, che invalida prevedibilmente buffer interno. L'operazione di lettura successiva provoca underflow.

Trovato il diff! Vedi cambiamento -473,41 +486,26. Commento era

    (seekoff): Simplify, set _M_reading, _M_writing to false, call
    _M_set_buffer(-1) ('uncommitted').

Quindi questo non è stato fatto per correggere un bug.

bug Archiviato: http://gcc.gnu.org/bugzilla/ show_bug.cgi? id = 45628

Altri suggerimenti

Beh, non so il motivo esatto per il cambiamento, ma a quanto pare i cambiamenti sono stati fatti per (Vedere la GCC 3.4 Serie Changelog ):

  • streambuf snella, filebuf, separare sincronizzati con C I / O standard streambuf.
  • Grande supporto di file (file più grandi di 2 GB su sistemi a 32 bit).

ho il sospetto che il supporto di file di grandi dimensioni è la grande caratteristica che richiederebbe un cambiamento come questo, perché iostreams non può più assumere può mappare l'intero file in memoria.

sincronizzazione corretta con cstdio è anche un'operazione che potrebbe richiedere un numero maggiore di vampate al disco. È possibile disattivare che l'utilizzo di std::sync_with_stdio .

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top