valores valor retornan referencia que punteros por paso parametros funciones ejercicios datos c++ pointers pass-by-reference pass-by-value pass-by-pointer

c++ - retornan - Costo de rendimiento de pasar por valor vs. por referencia o por puntero?



paso por valor y por referencia c++ (4)

¿Eso significa que si foo es del tipo float, entonces es más eficiente simplemente pasar foo por valor?

Pasar un flotador por valor podría ser más eficiente. Espero que sea más eficiente, en parte por lo que dijo: un flotador es más pequeño que un puntero en un sistema que usted describe. Pero además, cuando copia el puntero, aún necesita desreferirlo para obtener el valor dentro de la función. La indirección agregada por el puntero podría tener un efecto significativo en el rendimiento.

La diferencia de eficiencia podría ser despreciable. En particular, si la función puede estar en línea y la optimización está habilitada, es probable que no haya ninguna diferencia.

Puede averiguar si hay alguna ganancia de rendimiento al pasar el valor flotante por valor en su caso midiendo. Puedes medir la eficiencia con una herramienta de perfilado.

Puede sustituir el puntero por referencia y la respuesta se aplicará igualmente bien.

¿Hay algún tipo de sobrecarga en el uso de una referencia, la forma en que hay cuando un puntero debe ser referenciado?

Sí. Es probable que una referencia tenga exactamente las mismas características de rendimiento que un puntero. Si es posible escribir un programa semánticamente equivalente usando referencias o punteros, es probable que ambos generen un ensamblaje idéntico.

Si pasar un objeto pequeño por un puntero sería más rápido que copiarlo, entonces seguramente sería cierto para un objeto del mismo tamaño, ¿no le parece? ¿Qué hay de un puntero a un puntero, que es aproximadamente del tamaño de un puntero, ¿verdad? (Es exactamente del mismo tamaño). Oh, pero los punteros también son objetos. Por lo tanto, si pasar un objeto (como un puntero) por un puntero es más rápido que copiar el objeto (el puntero), entonces pasar un puntero a un puntero a un puntero a un puntero ... a un puntero sería más rápido que el elemento principal con menos punteros que es aún más rápido que el que no usó punteros ... Perhap, hemos encontrado una fuente infinita de eficiencia aquí :)

Consideremos un objeto foo (que puede ser un int , un double , una struct personalizada, una class , lo que sea). Tengo entendido que pasar foo por referencia a una función (o simplemente pasar un puntero a foo ) conduce a un mayor rendimiento, ya que evitamos hacer una copia local (lo que podría ser costoso si foo es grande).

Sin embargo, de la respuesta here parece que se puede esperar que los punteros en un sistema de 64 bits en la práctica tengan un tamaño de 8 bytes, independientemente de lo que se apunte. En mi sistema, un float es de 4 bytes. ¿Eso significa que si foo es del tipo float , entonces es más eficiente simplemente pasar foo por valor en lugar de darle un indicador (suponiendo que no haya otras restricciones que hagan que usar una sea más eficiente que la otra dentro de la función)?


Debe probar cualquier escenario dado donde el rendimiento sea absolutamente crítico, pero tenga mucho cuidado al tratar de forzar al compilador a generar código de una manera específica.

El optimizador del compilador tiene permitido reescribir su código de la forma que elija, siempre y cuando el resultado final sea el mismo, lo que puede llevar a algunas optimizaciones muy agradables.

Considere que pasar un valor flotante por valor requiere hacer una copia del valor flotante, pero en las condiciones correctas, pasar un valor flotante por referencia podría permitir almacenar el flotador original en un registro de punto flotante de la CPU, y tratar ese registro como el parámetro "referencia" a la función. Por el contrario, si pasa una copia, el compilador tiene que encontrar un lugar para guardar la copia con el fin de preservar el contenido del registro, o incluso peor, es posible que no pueda utilizar un registro en absoluto debido a la necesidad de preservar el original (esto es especialmente cierto en funciones recursivas).

Esta diferencia también es importante si está pasando la referencia a una función que podría estar en línea, donde la referencia puede reducir el costo de la inscripción, ya que el compilador no tiene que garantizar que un parámetro copiado no pueda modificar el original.

Cuanto más un lenguaje le permita enfocarse en describir lo que quiere que se haga en lugar de cómo lo quiere hacer, más puede compilar el compilador formas creativas de hacer el trabajo duro por usted. Especialmente en C ++, generalmente es mejor no preocuparse por el rendimiento y, en cambio, concentrarse en describir lo que desea de la manera más clara y sencilla posible. Al tratar de describir cómo desea que se realice el trabajo, con la frecuencia evitará que el compilador haga su trabajo de optimizar su código para usted.


Depende de lo que quiere decir con "costo" y de las propiedades del sistema host (hardware, sistema operativo) con respecto a las operaciones.

Si su medida de costo es el uso de memoria, entonces el cálculo del costo es obvio: sume los tamaños de lo que se esté copiando.

Si tu medida es la velocidad de ejecución (o "eficiencia"), entonces el juego es diferente. El hardware (y los sistemas operativos y el compilador) tienden a ser optimizados para el rendimiento de las operaciones en la copia de objetos de tamaños particulares, en virtud de circuitos dedicados (registros de máquinas, y cómo se utilizan).

Es común, por ejemplo, que una máquina tenga una arquitectura (registros de máquina, arquitectura de memoria, etc.) que resulte en un "punto dulce": copiar las variables de algún tamaño es más "eficiente", pero copiar variables más grandes O MÁS PEQUEÑAS es menos. Las variables más grandes costarán más copiarlas, ya que puede ser necesario realizar copias múltiples de fragmentos más pequeños. Los más pequeños también pueden costar más, porque el compilador necesita copiar el valor más pequeño en una variable más grande (o registrarse), realizar las operaciones en él y luego copiar el valor de nuevo.

Los ejemplos con punto flotante incluyen algunos supercomputadores cray, que soportan de forma nativa el punto flotante de precisión doble (también conocido como double en C ++), y todas las operaciones en precisión simple (también conocida como float en C ++) están emuladas en el software. Algunas CPU x86 de 32 bits más antiguas también funcionaban internamente con enteros de 32 bits, y las operaciones con enteros de 16 bits requerían más ciclos de reloj debido a la traducción a / desde 32 bits (esto no es cierto con los más modernos de 32 bits o 64 bits). procesadores de bit x86, ya que permiten copiar enteros de 16 bits a / desde registros de 32 bits, y operar en ellos, con menos penalizaciones de este tipo).

Es un poco obvio que copiar una estructura muy grande por valor será menos eficiente que crear y copiar su dirección. Pero, debido a factores como el anterior, el punto de cruce entre "lo mejor para copiar algo de ese tamaño por valor" y "lo mejor para pasar su dirección" es menos claro.

Los punteros y las referencias tienden a implementarse de una manera similar (por ejemplo, pasar por referencia se puede implementar de la misma manera que pasar un puntero) pero eso no está garantizado.

La única manera de estar seguro es medirlo. Y date cuenta que las medidas variarán entre sistemas.


Hay una cosa que nadie mencionó.

Existe una cierta optimización de GCC llamada IPA SRA, que reemplaza automáticamente "pasar por referencia" por "pasar por valor": https://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html (-fipa-sra)

Lo más probable es que esto se haga para los tipos escalares (por ejemplo, int, double, etc.), que no tienen una semántica de copia no predeterminada y pueden caber en los registros de la CPU.

Esto hace

void(const int &f)

probablemente tan rápido (y optimizado para el espacio)

void(int f)

Entonces, con esta optimización habilitada, el uso de referencias para tipos pequeños debería ser tan rápido como pasarlas por valor.

Por otro lado, pasar (por ejemplo) std :: cadena por valor no pudo optimizarse a la velocidad de referencia, ya que se trata de semántica de copia personalizada.

Por lo que entiendo, el uso de pasar por referencia para todo nunca debe ser más lento que seleccionar manualmente qué pasará por valor y qué pasará por referencia.

Esto es extremadamente útil especialmente para las plantillas:

template<class T> void f(const T&) { // Something }

siempre es óptimo