2012-02-22 19 views
5

Espero que no haya diferencias cuando se trata de estructuras que tienen un tamaño de hasta 8 bytes, pero ¿qué pasa con los tipos de POD más grandes? ¿Pasa el valor se vuelve más caro en el momento en que el tamaño del texto excede el tamaño de la palabra de la máquina o hay algo más (como el tamaño de la línea de caché) que puede afectar el rendimiento?¿Cuándo sizeof (myPOD) es demasiado grande para pasar por valor en x64?

Estoy interesado principalmente en x64, pero también puede incluir algunos números para x86.

Aclaraciones:

  • estoy pensando probablemente demasiado estrecho porque no estoy al tanto de todo lo que juega un papel en esto (registros, convenciones de llamada, las optimizaciones del compilador). Estoy interesado principalmente en el compilador C++ de Microsoft y solo usa __fastcall.
  • Me interesa saber si hay algún tipo de recomendación general cuando se trata de pasar parámetros conociendo la arquitectura, tamaño de letra, tamaño de caché, etc. Algo así como: "Prefiere pasar el tipo por valor cuando es menor que N bytes. " donde N es algo que puede derivarse de las cosas que conocemos.
+0

En algún punto, más allá de 8 bytes, el compilador genera código para crear una copia local y pasa un puntero a esa copia. Pruébelo, mire el código máquina generado. –

Respuesta

7

Estás confundiendo dos problemas diferentes. Puede pasar cualquier objeto por valor (siempre que se pueda copiar).

Si se aprobará o no en un registro o en la pila depende de la implementación y, específicamente, de la convención de llamadas utilizada.

En algunas convenciones de llamada, los parámetros de más de 8 bytes (el tamaño de registro de propósito general) se pasarán en la pila. En virtud de otras convenciones de llamadas, es posible que simplemente se dividan en varios registros.

Debajo de algunos, es posible que los objetos sean nunca pasados ​​en registros, independientemente de su tamaño.

Del mismo modo, los valores SIMD (SSE/AVX) pueden ser pasados ​​en registros en algunas convenciones de llamada, pero siempre se ponen en la pila en otros. Y lo mismo puede ser cierto para los valores escalares de coma flotante.

Pero lo que estás preguntando realmente no puede ser respondido de manera significativa. La velocidad de copia de un objeto se ve afectada por el tamaño del objeto, sí.Si el objeto es un tipo POD, y cabe en un registro, entonces puede copiarse con una simple instrucción mov. Si el compilador hará o no do que depende del compilador.

Y, obviamente, cuanto mayor sea el objeto, más espacio de memoria caché ocupará, lo que significa que obtendrá más errores de caché.

Pero esto es tan vago que es inútil. No sabemos cómo se ve su objeto, y no sabemos cuál es su código con. Si tiene un tipo específico en mente, escriba un punto de referencia para ver cómo lo maneja el compilador.

En respuesta a su edición

me interesa si hay algún tipo de recomendación general cuando se trata de parámetros de traspaso conocer la arquitectura, tamaño de letra, tamaño de caché, etc. Algo así como: "Prefiero pasar el tipo de valor cuando es menor que N bytes.

en primer lugar, confía en tu compilador. se optimizará agresiva copias de distancia, en muchas situaciones, por lo que incluso si hace pasar un objeto grande en valor, eso Es poco probable que sea un problema mensurable.

En segundo lugar, está buscando una microoptimización que es poco probable que haga una notable diferencia en ambos sentidos. Para objetos pequeños, pasar por valor evita un indirecto de puntero, por lo que es probablemente un poco más rápido. En algún punto, esto se ve abrumado por el costo de copia (suponiendo que el objeto es copiado, ver arriba). Para muy objetos grandes (para el argumento, digamos 500 bytes o más, tan grande que los objetos normalmente no lo alcanzan), definitivamente debe pasar por referencia.

¿Pero para objetos de 8, 16, 24, 40 bytes? ¿Quién sabe? ¿A quien le importa? Es poco probable que se haga una diferencia medible en el código real.

que me lleva a las dos reglas de oro:

  1. hacer lo que parece natural: si pasando por copia hace que su código más simple o más limpio, hacer eso.
  2. si el rendimiento es importante, entonces (1) asegúrese de que lo que está viendo en realidad tiene cualquier impacto notable en su rendimiento. Mídelo. Si afecta el rendimiento, entonces puede medirse. Si no se puede medir, entonces la diferencia en el rendimiento, por definición, no puede ser perceptible.

Así, en pocas palabras:

  • para este tipo de primitve, pasan por valor.
  • para tipos muy grandes, pase por referencia.
  • para todo lo demás, deja de preocuparte y pasa tu tiempo en algo productivo.
+0

Soy consciente de que cualquier objeto puede pasarse por valor, solo quería algunos números duros siempre que conozca todos los detalles. Parece que no sé todo lo que entra en juego. Ver mis aclaraciones. –

+0

Si quieres números duros, mídelos. :) Por cierto, esa es también la única regla que vale la pena seguir si te preocupa el rendimiento: si necesitas saber cuál es más rápido, mídelo. Si no puedes medir la diferencia, no importa. – jalf

+2

Creo que lo que estaba buscando era documentación para la convención de llamadas x64. http://msdn.microsoft.com/en-us/library/ms235286(v=vs.100).aspx explica esto muy bien: "Cualquier argumento que no cabe en 8 bytes, o no es 1, 2, 4 , u 8 bytes, deben pasarse por referencia. No hay ningún intento de extender un único argumento a través de múltiples registros ". –

0

Usted debe preocuparse de dos cosas - la copia de datos y la pila uso.

La copia de datos lleva su tiempo. Cuanto más grande sea la estructura, más tiempo llevará copiarla. Si se trata de un rendimiento o no, depende de la frecuencia con que lo haga y cuáles son los requisitos de rendimiento de su código.

La pila es grande, pero no es infinita. Pasar estructuras grandes por valor, especialmente si se combina con la recursión, puede hacer que se desborde fácilmente.

Con x86_64 (usando las convenciones de WIN64 o Linux), existe el punto más pequeño de pasar datos en los registros. Si los parámetros son de hasta 8 bytes cada uno, los primeros 6 se pasan en los registros, que es más rápido. Con x86, la mayoría de las convenciones no lo hacen (el kernel de Linux, sin embargo, usa 3 registros para los parámetros).
El uso de reigsters es algo más rápido. Pero la diferencia entre pasar 8 bytes usando stack o un registro es pequeña, en comparación con la diferencia entre copiar 8 bytes y 1000 bytes.

+0

"x86 no lo hace de forma predeterminada"? ¿Qué? x86 no hace nada por defecto, por lo que no hay nada para "anular". Depende de los compiladores decidir sobre una convención de llamadas. – jalf

+0

@jalf, sí, no era exacto. Las convenciones normales de llamadas x86, es decir, Linux y Windows, no lo hacen. – ugoren

Cuestiones relacionadas