2011-10-24 9 views
7

Estaba ejecutando algunos benchmarks para encontrar la manera más eficiente de escribir una gran matriz en un archivo en C++ (más de 1Go en ASCII).Perforaciones muy sorprendentes de fprintf vs std :: ofstream (fprintf es muy lento)

Por lo tanto, en comparación con std :: ofstream fprintf (véase el interruptor utilicé más adelante)

case 0: { 
     std::ofstream out(title, std::ios::out | std::ios::trunc); 
     if (out) { 
      ok = true; 
      for (i=0; i<M; i++) { 
       for (j=0; j<N; j++) { 
        out<<A[i][j]<<" "; 
       } 
       out<<"\n"; 
      } 
      out.close(); 
     } else { 
      std::cout<<"Error with file : "<<title<<"\n"; 
     } 
     break; 
    } 
    case 1: { 
     FILE *out = fopen(title.c_str(), "w"); 
     if (out!=NULL) { 
      ok = true; 
      for (i=0; i<M; i++) { 
       for (j=0; j<N; j++) { 
        fprintf(out, "%d ", A[i][j]); 
       } 
       fprintf(out, "\n"); 
      } 
      fclose(out); 
     } else { 
      std::cout<<"Error with file : "<<title<<"\n"; 
     } 
     break; 
    } 

Y mi gran problema es que fprintf parece ser más thant 12x más lento en comparación con std :: ofstream. ¿Tiene una idea de cuál es el origen del problema en mi código? O tal vez std :: ofstream está muy optimizado en comparación con fprintf?

(y otro pregunta: ¿Cómo sabe otra manera más rápida de escribir un archivo)

Muchas gracias

(detalle: yo estaba compilando con g ++ -Wall -O3)

+0

creo que debería usar fputs en lugar de fprintf para obtener un comportamiento más similar –

+1

también consulte 'ostream :: write()': http://www.cplusplus.com/reference/iostream/ostream/write/ – Nim

+1

@ AndersK .: No. fputs es el equivalente de un streambuf (no formateado); fprintf es la contraparte adecuada de ostream. – MSalters

Respuesta

15

fprintf("%d" requiere un análisis en tiempo de ejecución de la cadena de formato, una vez por entero. ostream& operator<<(ostream&, int) es resuelto por el compilador, una vez por compilación.

1

¿Ha establecido sync_with_stdio en algún lugar anterior al código que ha mostrado?

Mientras que lo que informa es opuesto al de lo que se ve empíricamente, la mayoría de las personas piensa y cree que lo que ve debería ser la norma. iostreams son seguros para tipos, mientras que la familia de funciones printf son funciones variadas que tienen que inferir los tipos de va_list del especificador de formato.

4

Bueno, fprintf() tiene que trabajar un poco más en el tiempo de ejecución, ya que tiene que analizar y procesar la cadena de formato. Sin embargo, dado el tamaño de su archivo de salida, espero que esas diferencias sean de poca importancia, y espero que el código esté vinculado a E/S.

Por lo tanto, sospecho que su punto de referencia es defectuoso de alguna manera.

  1. ¿Constantemente obtiene una diferencia de 12x si realiza las pruebas repetidamente?
  2. ¿Qué ocurre con los tiempos si invierte el orden en que ejecuta las pruebas?
  3. ¿Qué ocurre si llama al fsync()/sync() al final?
2

Hay un búfer de archivo en el flujo, esto puede disminuir los tiempos de acceso al disco. Además, fprintf es una función con parámetros variables que llamará a algunas funciones va_ #, pero de corriente no. Creo que puede usar fwrite() o putc() para realizar una prueba.

+0

putc sería más lento ya que solo escribe un carácter, será más lento. –

1

Aquí presento una manera muy optimizada para escribir enteros en un archivo de texto usando las funciones de unix abiertas, leídas y escritas. También están disponibles en Windows, solo dale una advertencia con la que puedes trabajar. Esta implementación solo funciona con 32 bits enteros.

En su archivo de inclusión:

class FastIntegerWriter 
{ 
private: 

    const int bufferSize; 
    int offset; 
    int file; 
    char* buffer; 

public: 

    FastIntegerWriter(int bufferSize = 4096); 
    int Open(const char *filename); 
    void Close(); 
    virtual ~FastIntegerWriter(); 
    void Flush(); 
    void Writeline(int value); 
}; 

En el archivo de origen

#ifdef _MSC_VER 
# include <io.h> 
# define open _open 
# define write _write 
# define read _read 
# define close _close 
#else 
# include <unistd.h> 
#endif 
#include <fcntl.h> 

FastIntegerWriter::FastIntegerWriter(int bufferSize) : 
    bufferSize(bufferSize), 
    buffer(new char[bufferSize]), 
    offset(0), 
    file(0) 
{ 
} 

int FastIntegerWriter::Open(const char* filename) 
{ 
    this->Close(); 
    if (filename != NULL) 
     this->file = open(filename, O_WRONLY | O_CREAT | O_TRUNC); 
    return this->file; 
} 

void FastIntegerWriter::Close() 
{ 
    this->Flush(); 
    if (this->file > 0) 
    { 
     close(this->file); 
     this->file = 0; 
    } 
} 

FastIntegerWriter::~FastIntegerWriter() 
{ 
    this->Close(); 
    delete[] this->buffer; 
} 

void FastIntegerWriter::Flush() 
{ 
    if (this->offset != 0) 
    { 
     write(this->file, this->buffer, this->offset); 
     this->offset = 0; 
    } 
} 

void FastIntegerWriter::Writeline(int value) 
{ 
    if (this->offset >= this->bufferSize - 12) 
    { 
     this->Flush(); 
    } 

    // Compute number of required digits 

    char* output = this->buffer + this->offset; 

    if (value < 0) 
    { 
     if (value == -2147483648) 
     { 
      // Special case, the minimum integer does not have a corresponding positive value. 
      // We use an hard coded string and copy it directly to the buffer. 
      // (Thanks to Eugene Ryabtsev for the suggestion). 

      static const char s[] = "-2147483648\n"; 
      for (int i = 0; i < 12; ++i) 
       output[i] = s[i]; 
      this->offset += 12; 
      return; 
     } 

     *output = '-'; 
     ++output; 
     ++this->offset; 
     value = -value; 
    } 

    // Compute number of digits (log base 10(value) + 1) 

    int digits = 
     (value >= 1000000000) ? 10 : (value >= 100000000) ? 9 : (value >= 10000000) ? 8 : 
     (value >= 1000000) ? 7 : (value >= 100000) ? 6 : (value >= 10000) ? 5 : 
     (value >= 1000) ? 4 : (value >= 100) ? 3 : (value >= 10) ? 2 : 1; 

    // Convert number to string 

    output[digits] = '\n'; 
    for (int i = digits - 1; i >= 0; --i) 
    { 
     output[i] = value % 10 + '0'; 
     value /= 10; 
    } 

    this->offset += digits + 1; 
} 

supongo que esto va a superar a todos los demás métodos para escribir en un archivo ASCII :) usted puede conseguir un poco más de rendimiento utilizando windows low level apis WriteFile y ReadFile, pero no vale la pena el esfuerzo.

Para usarlo ...

int main() 
{ 
    FastIntegerWriter fw; 
    fw.Open("test.txt"); 

    for (int i = -2000; i < 1000000; ++i) 
     fw.Writeline(i); 

    return 0; 
} 

Si no se especifica ningún archivo que utiliza la salida estándar (consola).

+1

Tenga en cuenta que 'value = -value' funcionaría incorrectamente si se pasa el entero más negativo ya que no hay un valor positivo correspondiente. Consulte http://stackoverflow.com/a/5165813/1353187 –

+0

Correcto. No pensé en escribir ese código. La mejor y más sencilla forma de manejarlo es codificar en una cadena el entero más negativo y escribir un if (valor == most_negative_integer) write_the_string –