c performance locking stdio mingw32

Gran diferencia de velocidad de fprintf sin "-std=c99"



performance locking (4)

Desde MinGW32 3.15, las funciones de printf compatibles están disponibles para usar en lugar de las que se encuentran en el tiempo de ejecución de Microsoft C (CRT). Las nuevas funciones de printf se utilizan al compilar en estrictos modos ANSI, POSIX y / o C99.

Para más información vea el registro de cambios de mingw32.

Puede usar __msvcrt_fprintf() para usar la función rápida (no compatible).

Había estado luchando durante semanas con un traductor de bajo rendimiento que había escrito. En el siguiente bechmark simple

#include<stdio.h> int main() { int x; char buf[2048]; FILE *test = fopen("test.out", "wb"); setvbuf(test, buf, _IOFBF, sizeof buf); for(x=0;x<1024*1024; x++) fprintf(test, "%04d", x); fclose(test); return 0 }

vemos el siguiente resultado

bash-3.1$ gcc -O2 -static test.c -o test bash-3.1$ time ./test real 0m0.334s user 0m0.015s sys 0m0.016s

Como puede ver, en el momento en que se agrega el indicador "-std = c99", el rendimiento se derrumba:

bash-3.1$ gcc -O2 -static -std=c99 test.c -o test bash-3.1$ time ./test real 0m2.477s user 0m0.015s sys 0m0.000s

El compilador que estoy usando es gcc 4.6.2 mingw32.

El archivo generado es de aproximadamente 12M, por lo que esta es una diferencia de aproximadamente 21MB / s entre los dos.

La ejecución de diff muestra que los archivos generados son idénticos.

Supuse que esto tiene algo que ver con el bloqueo de archivos en fprintf , de los cuales el programa hace un uso intensivo, pero no he podido encontrar una manera de desactivarlo en la versión C99.

Intenté flockfile en el flujo que uso al principio del programa, y ​​un funlockfile correspondiente al final, pero funlockfile errores del compilador sobre declaraciones implícitas y errores de vinculador que afirman referencias indefinidas a esas funciones.

¿Podría haber otra explicación para este problema y, lo que es más importante, hay alguna forma de usar C99 en Windows sin pagar un precio tan alto por el rendimiento?

Editar:

Después de ver el código generado por estas opciones, parece que en las versiones lentas, mingw se pega en lo siguiente:

_fprintf: LFB0: .cfi_startproc subl $28, %esp .cfi_def_cfa_offset 32 leal 40(%esp), %eax movl %eax, 8(%esp) movl 36(%esp), %eax movl %eax, 4(%esp) movl 32(%esp), %eax movl %eax, (%esp) call ___mingw_vfprintf addl $28, %esp .cfi_def_cfa_offset 4 ret .cfi_endproc

En la versión rápida, esto simplemente no existe; De lo contrario, ambos son exactamente iguales. Supongo que __mingw_vfprintf parece ser el lenguaje lento aquí, pero no tengo idea de qué comportamiento debe emular, lo que lo hace tan lento.


Después de considerar a su ensamblador, parece que la versión lenta está usando la implementación *printf() de MinGW, basada indudablemente en la versión GCC, mientras que la versión rápida está usando la implementación de Microsoft desde msvcrt.dll .

Ahora, el MS one es notablemente por carecer de muchas características, que el GCC implementa. Algunas de estas son extensiones GNU, pero otras son para conformidad con C99. Y como está utilizando -std=c99 , está solicitando la conformidad.

Pero ¿por qué tan lento? Bueno, un factor es la simplicidad, la versión MS es mucho más simple, por lo que se espera que se ejecute más rápido, incluso en los casos triviales. Otro factor es que se está ejecutando bajo Windows, por lo que se espera que la versión de MS sea más eficiente que la copia del mundo Unix.

¿Explica un factor de x10? Probablemente no...

Otra cosa que puedes probar:

  • Reemplace fprintf() con sprintf() , imprimiendo en un búfer de memoria sin tocar el archivo en absoluto. Entonces puedes intentar hacer fwrite() sin printfing . De esa manera puede adivinar si la pérdida está en el formato de los datos o en la escritura del FILE .

Después de investigar el código fuente, descubrí por qué la función MinGW es terriblemente lenta:

Al comienzo de un [v,f,s]printf en MinGW, hay un código de inicialización de aspecto inocente:

__pformat_t stream = { dest, /* output goes to here */ flags &= PFORMAT_TO_FILE | PFORMAT_NOLIMIT, /* only these valid initially */ PFORMAT_IGNORE, /* no field width yet */ PFORMAT_IGNORE, /* nor any precision spec */ PFORMAT_RPINIT, /* radix point uninitialised */ (wchar_t)(0), /* leave it unspecified */ 0, /* zero output char count */ max, /* establish output limit */ PFORMAT_MINEXP /* exponent chars preferred */ };

Sin embargo, PFORMAT_MINEXP no es lo que parece ser:

#ifdef _WIN32 # define PFORMAT_MINEXP __pformat_exponent_digits() # ifndef _TWO_DIGIT_EXPONENT # define _get_output_format() 0 # define _TWO_DIGIT_EXPONENT 1 # endif static __inline__ __attribute__((__always_inline__)) int __pformat_exponent_digits( void ) { char *exponent_digits = getenv( "PRINTF_EXPONENT_DIGITS" ); return ((exponent_digits != NULL) && ((unsigned)(*exponent_digits - ''0'') < 3)) || (_get_output_format() & _TWO_DIGIT_EXPONENT) ? 2 : 3 ; }

Esto termina siendo llamado cada vez que quiero imprimir, y getenv en Windows no debe ser muy rápido. Reemplazar esa definición con un 2 devuelve el tiempo de ejecución a donde debería estar.

Entonces, la respuesta se reduce a esto: cuando se usa -std=c99 o cualquier modo compatible con ANSI, MinGW cambia el tiempo de ejecución de CRT por su propia cuenta. Normalmente, esto no sería un problema, pero la biblioteca de MinGW tenía un error que ralentizaba sus funciones de formato mucho más allá de cualquier cosa imaginable.


Usando -std=c99 deshabilita todas las extensiones GNU.

Con las extensiones de GNU y la optimización, su fprintf(test, "B") probablemente sea reemplazado por una fputc(''B'', test)

Tenga en cuenta que esta respuesta es obsoleta, consulte https://.com/a/13973562/611560 y https://.com/a/13973933/611560