2012-01-23 24 views
12

Estoy depurando una aplicación que se está ejecutando bastante más lenta cuando se compila como un ejecutable Linux ELF de 64 bits que como un ejecutable Linux ELF de 32 bits. Utilizando Rational (IBM) Quantify, rastreé gran parte de la diferencia de rendimiento hasta (tambor de arrastre ...) memset. Curiosamente, memset toma un lote durante más tiempo en el ejecutable de 64 bits.Problema de rendimiento de Linux de 64 bits con memset

soy incluso capaz de ver esto con una pequeña, sencilla aplicación:

#include <stdlib.h> 
#include <string.h> 

#define BUFFER_LENGTH 8000000 

int main() 
{ 
    unsigned char* buffer = malloc(BUFFER_LENGTH * sizeof(unsigned char)); 
    for(int i = 0; i < 10000; i++) 
    memset(buffer, 0, BUFFER_LENGTH * sizeof(unsigned char)); 
} 

construyo como esto:
$ gcc -m32 -std=gnu99 -g -O3 ms.c
y
$ gcc -m64 -std=gnu99 -g -O3 ms.c

La hora del reloj de pared según lo informado por time es más largo para la compilación -m64 y Quantify confirma que el tiempo extra se está gastando en memset.

Hasta ahora he probado en VirtualBox y VMWare (pero no en el Linux básico, me doy cuenta de que necesito hacer eso a continuación). La cantidad de tiempo extra invertido parece variar un poco de un sistema a otro.

¿Qué está pasando aquí? ¿Existe algún problema conocido que mi Google-foo no pueda descubrir?

EDIT: El desmontaje (gcc ... -S) en mi sistema muestra que memset se invoca como una función externa:

32 bits:

.LBB2: 
    .loc 1 14 0 
    movl $8000000, 8(%esp) 
    .loc 1 12 0 
    addl $1, %ebx 
    .loc 1 14 0 
    movl $0, 4(%esp) 
    movl %esi, (%esp) 
    call memset 

64-bit:

.LBB2: 
    .loc 1 14 0 
    xorl %esi, %esi 
    movl $8000000, %edx 
    movq %rbp, %rdi 
.LVL1: 
    .loc 1 12 0 
    addl $1, %ebx 
    .loc 1 14 0 
    call memset 

Sistema:

  • CentOS 5.7 2.6.18-274.17.1.el5 x86_64
  • GCC 4.1.2
  • Intel (R) Core (TM) i7-2600K CPU @ 3.40GHz/VirtualBox
    (discrepancia es peor en un Xeon E5620 @ 2.40GHz/VMWare)
+1

¿Qué arquitectura es esta? ¿Qué modelo de procesador? ¿Podemos ver algún desmontaje? – Mysticial

+1

En mi caja ejecutando Ubuntu de 64 bits con gcc 4.4.5, el tiempo de ejecución de las dos compilaciones es indistinguible. – NPE

+0

@Mysticial: agregué algunos detalles del sistema. ¿Cómo sugeriría el desmontaje que incluye 'memset'? Por supuesto 'gcc -S' solo mostrará la llamada a memset. –

Respuesta

0

al compilar el código de ejemplo, el compilador ve el tamaño de bloque fijo (~ 8 MB) y decide utilizar la versión de la biblioteca. Pruebe el código para bloques mucho más pequeños (para memset'ing solo unos pocos bytes) - compare el desmontaje.

Aunque no sé por qué la versión x64 es más lenta. Supongo que hay un problema en su código de medición del tiempo.

Desde el changelog of gcc 4.3: generación

Código de movimiento de bloque (memcpy) y el conjunto de bloques (memset) se reescribió. GCC ahora puede elegir el mejor algoritmo (bucle, bucle desenrollado, instrucción con prefijo de repetición o una llamada de biblioteca) según el tamaño del bloque que se está copiando y la CPU optimizada. Se ha agregado una nueva opción -minline-stringops-dynamically.Con esta opción, las operaciones de cadena de tamaño desconocido se expanden de manera que los bloques pequeños se copian mediante el código en línea, mientras que para los bloques grandes se usa una llamada a la biblioteca. Esto da como resultado un código más rápido que -minline-all-stringops cuando la implementación de la biblioteca es capaz de utilizar sugerencias de jerarquía de caché. La heurística que elige el algoritmo particular se puede sobrescribir a través de -mstringop-strategy. Recientemente también memset de valores diferentes de 0 está inline.

Hope esto explica lo que los diseñadores de compiladores tratan de hacerlo (incluso si se trata de otra versión) ;-)

1

puedo confirmar que en mi sistema Mandriva Linux no virtualizado la versión x86_64 es ligeramente (aproximadamente 7%) más lento. En ambos casos, se llama a la función de biblioteca memset(), independientemente del tamaño de palabra de conjunto de instrucciones.

Un vistazo casual al código de ensamblaje de ambas implementaciones de la biblioteca revela que la versión x86_64 es significativamente más compleja. Supongo que esto tiene que ver principalmente con el hecho de que la versión de 32 bits tiene que tratar solo con 4 posibles casos de alineación, frente a los 8 posibles casos de alineación de la versión de 64 bits. También parece que el bucle x86_64 memset() se ha desenrollado más extensamente, quizás debido a diferentes optimizaciones del compilador.

Otro factor que podría explicar las operaciones más lentas es la mayor carga de E/S asociada con el uso de un tamaño de palabra de 64 bits. Tanto el código como los metadatos (punteros e.t.c.) generalmente se vuelven más grandes en las aplicaciones de 64 bits.

Además, tenga en cuenta que las implementaciones de bibliotecas incluidas en la mayoría de las distribuciones están dirigidas a cualquier CPU que los mantenedores consideren que es el mínimo común denominador actual para cada familia de procesadores. Esto puede dejar a los procesadores de 64 bits en desventaja, ya que el conjunto de instrucciones de 32 bits se ha mantenido estable durante algún tiempo.

+0

¿También en una máquina virtual? – zvrba

+1

@zvrba: No, este es un sistema Linux apropiado, que se ejecuta en un Intel Core 2 Q9550 ... – thkala

+0

¿Qué tan grande es la discrepancia? – zvrba

1

Creo que la virtualización es la culpable: he estado ejecutando algunos puntos de referencia por mi cuenta (generación aleatoria de números a granel, búsquedas secuenciales, también de 64 bits) y descubrí que el código corre ~ 2 veces más lento dentro de Linux en VirtualBox que de forma nativa bajo windows. Lo curioso es que el código no tiene E/S (excepto printf simple de vez en cuando, entre timings) y usa poca memoria (todos los datos caben en caché L1), por lo que podría pensarse que podría excluirse la administración de la tabla de páginas y los gastos generales de TLB.

Esto es misterioso de hecho. Me he dado cuenta de que VirtualBox informa a la máquina virtual que las instrucciones SSE 4.1 y SSE 4.2 no son compatibles, aunque la CPU las admite, y el programa que las usa funciona bien (!) En una máquina virtual. No tengo tiempo para investigar más el tema, pero REALMENTE debe cronometrarlo en una máquina real. Lamentablemente, mi programa no se ejecutará en 32 bits, por lo que no pude probar la ralentización en el modo de 32 bits.

Cuestiones relacionadas