Puedo ver por qué el auto
tipo en C ++ 11 mejora la corrección y la facilidad de mantenimiento. He leído que también puede mejorar el rendimiento ( Casi siempre automático de Herb Sutter), pero echo de menos una buena explicación.
- ¿Cómo puede
auto
mejorar el rendimiento? - ¿Alguien puede dar un ejemplo?
c++
performance
c++11
auto
DaBrain
fuente
fuente
Respuestas:
auto
puede ayudar al rendimiento al evitar conversiones implícitas silenciosas . Un ejemplo que encuentro convincente es el siguiente.¿Ves el error? Aquí estamos, pensando que estamos tomando elegantemente cada elemento del mapa por referencia constante y usando la nueva expresión de rango para aclarar nuestra intención, pero en realidad estamos copiando cada elemento. Esto se debe a que
std::map<Key, Val>::value_type
esstd::pair<const Key, Val>
, nostd::pair<Key, Val>
. Por lo tanto, cuando (implícitamente) tenemos:En lugar de tomar una referencia a un objeto existente y dejarlo así, tenemos que hacer una conversión de tipo. Se le permite tomar una referencia constante a un objeto (o temporal) de un tipo diferente siempre que haya una conversión implícita disponible, por ejemplo:
La conversión de tipo es una conversión implícita permitida por la misma razón por la que puede convertir a
const Key
aKey
, pero tenemos que construir una temporal del nuevo tipo para permitir eso. Por lo tanto, efectivamente nuestro ciclo hace:(Por supuesto, en realidad no hay un
__tmp
objeto, solo está allí para ilustración, en realidad, el temporal sin nombre está destinado aitem
durar toda la vida).Solo cambiando a:
simplemente nos ahorró una tonelada de copias; ahora el tipo de referencia coincide con el tipo de inicializador, por lo que no es necesario realizar una conversión temporal, solo podemos hacer una referencia directa.
fuente
std::pair<const Key, Val> const &
como unstd::pair<Key, Val> const &
? Nuevo en C ++ 11, no estoy seguro de cómo es el rango yauto
juega en esto.auto
que aumentan el rendimiento. Así que lo escribiré en mis propias palabras a continuación.auto
mejora el rendimiento". Es solo un ejemplo que "auto
ayuda a prevenir errores del programador que destruyen el rendimiento". Sostengo que hay una distinción sutil pero importante entre los dos. Aún así, +1.Como
auto
deduce el tipo de la expresión de inicialización, no hay conversión de tipo involucrada. Combinado con algoritmos con plantilla, esto significa que puede obtener un cálculo más directo que si fuera a inventar un tipo usted mismo, ¡especialmente cuando se trata de expresiones cuyo tipo no puede nombrar!Un ejemplo típico proviene de (ab) usando
std::function
:Con
cmp2
ycmp3
, todo el algoritmo puede alinear la llamada de comparación, mientras que si construye unstd::function
objeto, no solo no se puede alinear la llamada, sino que también tiene que pasar por la búsqueda polimórfica en el interior borrado de tipo del contenedor de funciones.Otra variante de este tema es que puedes decir:
Esto siempre es una referencia, vinculada al valor de la expresión de llamada de función, y nunca construye ningún objeto adicional. Si no conocía el tipo del valor devuelto, podría verse obligado a construir un nuevo objeto (quizás como temporal) a través de algo como
T && f = MakeAThing()
. (Además,auto &&
incluso funciona cuando el tipo de retorno no es móvil y el valor de retorno es un prvalue).fuente
auto
. Su otra variante es "evitar copias accidentales", pero necesita adornos; ¿Por quéauto
te da velocidad simplemente escribiendo el tipo allí? (Creo que la respuesta es "te equivocas y se convierte silenciosamente") Lo que lo convierte en un ejemplo menos explicado de la respuesta de Barry, ¿no? Es decir, hay dos casos básicos: automático para evitar el borrado de tipos y automático para evitar errores de tipo silencioso que se convierten accidentalmente, los cuales tienen un costo de tiempo de ejecución.std::bind
,std::function
ystd::stable_partition
todas han sido alineadas? ¿O simplemente que en la práctica ningún compilador de C ++ se alineará de forma lo suficientemente agresiva como para resolver el desastre?std::function
constructor, será muy complejo ver a través de la llamada real, especialmente con optimizaciones de función pequeña (por lo que no desea la desvirtualización). Por supuesto, en principio todo es como si ...Hay dos categorias.
auto
Puede evitar el borrado de tipo. Hay tipos no innombrables (como lambdas), y tipos casi innombrables (como el resultado destd::bind
u otra plantilla de expresión como cosas).Sin
auto
, terminas teniendo que borrar los datos a algo asístd::function
. El borrado de tipo tiene costos.task1
tiene sobrecarga de borrado de tipo: una posible asignación de montón, dificultad para alinearlo y sobrecarga de invocación de tabla de funciones virtuales.task2
no tiene ninguno Lambdas necesita auto u otras formas de deducción de tipo para almacenar sin borrado de tipo; otros tipos pueden ser tan complejos que solo lo necesitan en la práctica.En segundo lugar, puede obtener tipos incorrectos. En algunos casos, el tipo incorrecto funcionará aparentemente perfectamente, pero causará una copia.
se compilará si se
expression()
devuelveBar const&
oBar
inclusoBar&
, desde dóndeFoo
se puede construirBar
. SeFoo
creará un temporal , luego se vincularáf
, y su vida útil se extenderá hasta quef
desaparezca.El programador puede haber querido
Bar const& f
hacer una copia allí, y no lo hizo, pero se hace una copia independientemente.El ejemplo más común es el tipo de
*std::map<A,B>::const_iterator
, questd::pair<A const, B> const&
no lo esstd::pair<A,B> const&
, pero el error es una categoría de errores que silenciosamente cuestan rendimiento. Puedes construir unstd::pair<A, B>
de astd::pair<const A, B>
. (La clave en un mapa es constante, porque editarlo es una mala idea)Tanto @Barry como @KerrekSB ilustraron por primera vez estos dos principios en sus respuestas. Esto es simplemente un intento de resaltar los dos problemas en una sola respuesta, con una redacción que apunta al problema en lugar de centrarse en el ejemplo.
fuente
Las tres respuestas existentes dan ejemplos en los que el uso de las
auto
ayudas “hace que sea menos probable que se pesen involuntariamente” efectivamente, lo que lo hace "mejorar el rendimiento".Hay un reverso de la moneda. El uso
auto
con objetos que tienen operadores que no devuelven el objeto básico puede dar como resultado un código incorrecto (aún compilable y ejecutable). Por ejemplo, esta pregunta pregunta cómo usarauto
dio resultados diferentes (incorrectos) usando la biblioteca Eigen, es decir , las siguientes líneasresultó en una salida diferente. Es cierto que esto se debe principalmente a la evaluación perezosa de Eigens, pero ese código es / debería ser transparente para el usuario (de la biblioteca).
Si bien el rendimiento no se ha visto muy afectado aquí, el uso
auto
para evitar pesimismo involuntario podría clasificarse como optimización prematura, o al menos incorrecta;).fuente