El estilo de C++ vs rendimiento?


Estilo C++ vs. rendimiento - es el uso de cosas de estilo C, que son más rápidos que algunos equivalentes de C++, que mala práctica ? Por ejemplo:

  • No use atoi(), itoa(), atol(), etc. ! Use std::stringstream

  • Nunca utilice punteros raw, utilice punteros inteligentes en su lugar-OK, son realmente útiles, todo el mundo lo sabe, lo sé, lo uso todo el tiempo y sé cuánto mejor son que los punteros crudos, pero a veces es completamente seguro usar punteros crudos.. ¿Por qué no? "¿No es estilo C++?

  • No utilice operaciones bitwise-demasiado C-estilo? ¿QUÉ? ¿Por qué no, cuando estás seguro de lo que estás haciendo? Por ejemplo-no hacer intercambio bit a bit de variables (a ^= b; b ^= a; a ^= b;) - utilice el intercambio estándar de 3 pasos. No utilice el desplazamiento izquierdo para multiplicar por dos. Etc, etc.. (OK, eso no es estilo C++ vs. estilo C, pero todavía "no es una buena práctica" )

  • Y por último, el más caro-" No utilice enumeración-s para devolver códigos, es demasiado C-estilo, utilizar excepciones para diferentes errores"? ¿Por qué? OK, cuando estamos hablando de manejo de errores en niveles profundos-OK, pero ¿por qué siempre? ¿Qué hay de malo en esto, por ejemplo - cuando estamos hablando de una función, que devuelve diferentes códigos de error y cuando el manejo de errores se implementará solo en la función, que llama a la primera uno? Quiero decir - no hay necesidad de pasar los códigos de error en un nivel superior. Las excepciones son bastante lentas y son excepciones para situaciones excepcionales, no para .. belleza.

  • Etc., sucesivamente., sucesivamente.

Bien, sé que un buen estilo de codificación es muy, muy importante Optimizaciones de compiladores son muy poderosos. Pero también sé lo caro que es el manejo de excepciones , cómo se implementan (algunos) smart_pointers, y que no hay necesidad de smart_ptr todo el tiempo.. Sé que, por ejemplo, atoi no es tan "seguro" como lo es std::stringstream, pero aún así.. ¿Qué pasa con el rendimiento?


EDIT: No estoy hablando de algunas cosas realmente difíciles, que solo son específicas del estilo C. Quiero decir-no me pregunto para utilizar punteros de función o métodos virtuales y este tipo de cosas, que un programador de C++ puede no saberlo, si nunca usa tales cosas (mientras que los programadores de C hacen esto todo el tiempo). Estoy hablando de algunas cosas más comunes y fáciles, como en los ejemplos.

Author: Kiril Kirov, 2010-11-13

10 answers

En general, lo que te falta es que el camino C a menudo no es más rápido. Simplemente se parece más a un hack, y la gente a menudo piensa hacks son más rápidos.

Nunca use punteros raw, use punteros inteligentes en su lugar - OK, son realmente útiles, todo el mundo lo sabe, yo lo sé, uso el todo el tiempo y sé cuánto mejor son que los punteros raw, pero a veces es completamente seguro usar punteros raw.. ¿Por qué no?

Pongamos la cuestión de cabeza. A veces es seguro usar punteros crudos. ¿Es solo una razón para usarlos? ¿Hay algo acerca de los punteros raw que sea realmente superior a los punteros inteligentes? Depende. Algunos tipos de puntero inteligente son más lentos que los punteros raw. Otros no lo son. ¿Cuál es la razón de rendimiento para usar un puntero raw sobre un std::unique_ptr o un boost::scoped_ptr? Ninguno de ellos tiene gastos generales, solo proporcionan una semántica más segura.

Esto no quiere decir que nunca debas usar punteros raw. Solo que tú no deberías hacerlo solo porque crees que necesitas rendimiento, o simplemente porque "parece seguro". Hágalo cuando necesite representar algo que los punteros inteligentes no pueden. Como regla general, use punteros para señalar cosas, y punteros inteligentes para tomar propiedad de las cosas. Pero es una regla general, no una regla universal. Utilice lo que se ajuste a la tarea en cuestión. Pero no asuma ciegamente que los punteros crudos serán más rápidos. Y cuando utilice punteros inteligentes, asegúrese de estar familiarizado con todos ellos. Demasiado muchas personas simplemente usan shared_ptr para todo, y eso es simplemente horrible, tanto en términos de rendimiento como en la muy vaga semántica de propiedad compartida que termina aplicándose a todo.

No use operaciones bitwise - too C-style? ¿QUÉ? ¿Por qué no, cuando estás seguro de lo que estás haciendo? Por ejemplo-no hagas intercambio de variables a nivel de bits (a ^ = b; b ^ = a; a ^ = b;)-usa el intercambio estándar de 3 pasos. No utilice el desplazamiento izquierdo para multiplicar por dos. Etc, etc.. (OK, eso no es estilo C++ vs. Estilo C, pero todavía "no es una buena práctica")

Eso es correcto. Y la razón es "es más rápido". El intercambio bitwise es problemático de muchas maneras:

  • es más lento en una CPU moderna
  • es más sutil y más fácil equivocarse
  • funciona con un conjunto muy limitado de tipos

Y cuando se multiplica por dos, se multiplica por dos. El compilador conoce este truco, y lo aplicará si es más rápido. Y una vez más, cambiando tiene muchos de los mismos problemas. Puede, en este caso, ser más rápido (por lo que el compilador lo hará por usted), pero aún es más fácil equivocarse, y funciona con un conjunto limitado de tipos. En paticular, puede compilar bien con tipos con los que crees que es seguro hacer este truco... Y luego explotar en la práctica. In particular, bit shifting on negative values is a minefield. Deje que el compilador lo navegue por usted.

Por cierto, esto no tiene nada que ver con el estilo "C". El exactamente el mismo consejo se aplica en C. En C, un intercambio regular es todavía más rápido que el hack a bit, y bitshifting en lugar de una multiplicación será todavía hecho por el compilador si es válido y si es más rápido.

Pero como programador, debe usar operaciones bitwise para una sola cosa: para hacer manipulación bitwise de enteros. Ya tienes un operador de multiplicación, así que usa que cuando quieras multiplicar. Y también tienes una función std::swap. Usa eso si quiero intercambiar dos valores. Uno de los trucos más importantes a la hora de optimizar es, quizás sorprendentemente, escribir código legible y significativo. Eso le permite a su compilador entender el código y optimizarlo. std::swap puede ser especializado para hacer el intercambio más eficiente para el tipo particular en el que se utiliza. Y el compilador conoce varias formas de implementar la multiplicación, y elegirá la más rápida dependiendo de las circunstancias... Si tú lo dices. Si le dices a bit shift en su lugar, estás engañándolo. Dile que multiplique, y te dará la multiplicación más rápida que tiene.

Y finalmente, el más caro - "No utilice enumeración-s para devolver códigos, es demasiado estilo C, usar excepciones para diferentes errores" ?

Depende de a quién le preguntes. La mayoría de los programadores de C++ que conozco encuentran espacio para ambos. Pero tenga en cuenta que una cosa desafortunada acerca de los códigos de devolución es que son fácilmente ignorados. Si eso es inaceptable, entonces tal vez debería preferir una excepción en este caso. Otro punto es que RAII funciona mejor junto con excepciones, y un programador de C++ definitivamente debería usar RAII siempre que sea posible. Desafortunadamente, debido a que los constructores no pueden devolver códigos de error, las excepciones son a menudo la única manera de indicar errores.

Pero aún así.. ¿Qué pasa con el rendimiento?

¿Qué pasa con eso? Cualquier programador de C decente estaría feliz de decirle que no optimice prematuramente.

Su CPU puede ejecutar quizás 8 mil millones instrucciones por segundo. Si usted hace dos llamadas a un std::stringstream en ese segundo, es que va a hacer una mella mensurable en el presupuesto?

No se puede predecir el rendimiento. No se puede hacer una guía de codificación que dará lugar a un código rápido. Incluso si nunca lanzas una sola excepción y nunca usas stringstream, tu código no será automáticamente rápido. Si intentas optimizar mientras escribes el código, entonces vas a gastar el 90% del esfuerzo optimizando el 90% del código que apenas es alguna vez ejecutado. Para obtener una mejora medible, debe centrarse en el 10% del código que representa el 95% del tiempo de ejecución. Tratar de hacer todo rápido solo resulta en una gran cantidad de tiempo perdido con poco que mostrar, y una base de código mucho más fea.

 46
Author: jalf,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-11-13 13:47:30
  1. Desaconsejaría atoi, y atol como regla, pero no solo por motivos de estilo. Hacen que sea esencialmente imposible detectar errores de entrada. Mientras que un stringstream puede hacer el mismo trabajo, strtol (por ejemplo) es lo que me gustaría generalmente aconsejar como el reemplazo directo.
  2. No estoy seguro de quién está dando ese consejo. Use punteros inteligentes cuando sean útiles, pero cuando no lo sean, no hay nada de malo en usar un puntero raw.
  3. Yo realmente no tengo idea de quién piensa no es" buena práctica " usar operadores bitwise en C++. A menos que hubiera algunas condiciones específicas asociadas a ese consejo, diría que fue simplemente incorrecto.
  4. Este depende en gran medida de dónde se traza la línea entre una entrada excepcional y (por ejemplo) una entrada que se espera, pero no se puede usar. En términos generales, si estás aceptando entradas directas del usuario, no puedes (y no deberías) clasificar nada como verdaderamente excepcional. El principal punto bueno de las excepciones (incluso en un situación como esta) está asegurando que los errores no sean simplemente ignorados. OTOH, no creo que ese sea siempre el único criterio, así que no puedes decir que es la manera correcta de manejar cada situación.

En general, me parece que has recibido algunos consejos que son dogmáticos hasta el punto de ignorar la realidad. Probablemente es mejor ignorarlo o al menos verlo como una posición bastante extrema sobre cómo C++ podría escribirse, no necesariamente como siempre (o siempre, necesariamente) debe escribirse.

 14
Author: Jerry Coffin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-11-13 09:55:20

Añadiendo a la respuesta de @Jerry Coffin, que creo que es extremadamente útil, me gustaría presentar algunas observaciones subjetivas.

  • La cosa es que los programadores tienden a ponerse elegantes. Es decir, a la mayoría de nosotros realmente nos gusta escribir código de lujo solo por el bien de ello. Esto está perfectamente bien, siempre y cuando usted está haciendo el proyecto por su cuenta. Recuerde que un buen software es aquel cuyo código binario funciona como se espera y no aquel cuyo código fuente está limpio. Sin embargo, cuando se trata de más grande proyectos que son desarrollados y mantenidos por mucha gente, es económicamente mejor escribir código más simple para que nadie del equipo pierda tiempo para entender lo que querías decir. Incluso a costa del tiempo de ejecución(naturalmente un costo menor). Es por eso que muchas personas, incluyéndome a mí, desalentarían el uso del truco xor en lugar de la asignación(puede que te sorprendas, pero hay muchos programadores por ahí que no han oído hablar del truco xor). El truco xor funciona solo para enteros de todos modos, y el la forma tradicional de intercambiar enteros es muy rápida de todos modos, por lo que usar el truco de xor es solo ser elegante.

  • Usar itoa, atoi, etc. en lugar de streams es más rápido. Sí, lo es. ¿Pero cuánto más rápido? No mucho. A menos que la mayoría de su programa solo haga conversiones de texto a cadena y viceversa, no notará la diferencia. ¿Por qué la gente usa itoa, atoi, etc.? Bueno, algunos de ellos lo hacen, porque no son conscientes de la alternativa de c++. Otro grupo lo hace porque es solo un LOC. Para el grupo anterior-vergüenza en usted, para el último - ¿por qué no boost:: lexical_cast?

  • Excepciones... ah ... sí, pueden ser más lentos que los códigos de retorno, pero en la mayoría de los casos no realmente. Los códigos de devolución pueden contener información, lo cual no es un error. Las excepciones deben utilizarse para informar de errores graves, aquellos que no se puede ignorar. Algunas personas se olvidan de esto y usan excepciones para simular algunos mecanismos extraños de señal / ranura (créeme, lo he visto, yuck). Mi opinión personal es que no hay nada malo usando códigos de retorno, pero los errores graves deben ser reportados con excepciones, a menos que el generador de perfiles haya demostrado que abstenerse de ellos aumentaría considerablemente el rendimiento

  • Punteros raw - Mi propia opinión es la siguiente: nunca use punteros inteligentes cuando no se trata de propiedad. Siempre use punteros inteligentes cuando se trata de propiedad. Naturalmente, con algunas excepciones.

  • Desplazamiento de bits en lugar de multiplicación por potencias de dos. Este, creo, es un ejemplo clásico de optimización prematura. x << 3; Apuesto a que al menos el 25% de sus compañeros de trabajo necesitarán algún tiempo antes de que entiendan/se den cuenta de esto significa x * 8; Código ofuscado (al menos para el 25%) ¿por qué razones exactas? Una vez más, si el perfilador le dice que este es el cuello de botella (que dudo que sea el caso para casos extremadamente raros), entonces luz verde, adelante y hazlo (dejando un comentario que de hecho esto significa x * 8)

Para resumirlo. Bueno profesional reconoce los "buenos estilos", entiende por qué y cuándo son buenos, y legítimamente hace excepciones porque sabe lo que está haciendo. Los profesionales promedio / malos se clasifican en 2 tipos: el primer tipo no reconoce el buen estilo, ni siquiera entiende qué y por qué es. despídelos. El otro tipo trata el estilo como un dogma, que no siempre es bueno.

 9
Author: Armen Tsirunyan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-11-13 13:28:51

¿Qué es una buena práctica ? Las palabras de Wikipedia son mejores que las mías:

Una buena práctica es una técnica, método, proceso, actividad, incentivo, o recompensa que sabiduría convencional más eficaz en el entrega de un resultado particular que cualquier otra técnica, método, proceso, sucesivamente. cuando se aplica a un condición o circunstancia.

[...]

A given best practice is only aplicable a condición particular o circunstancia y puede tener que ser modificado o adaptado para similares circunstancias. Además, un " mejor" la práctica puede evolucionar para ser mejor a medida que se descubren mejoras.

Creo que no hay tal cosa como la verdad universal en la programación : si usted piensa que algo es un mejor ajuste en su situación que una llamada "mejor práctica", entonces haga lo que usted cree que es correcto, pero sepa perfectamente por qué lo hace (es decir: probarlo con números).

 3
Author: icecrime,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-11-13 10:05:15
  1. Las funciones con argumentos mutables char* son malas en C++ porque es demasiado difícil manejar manualmente su memoria, ya que tenemos una alternativa. No son genéricos, no podemos cambiar fácilmente de char a wchar_t como basic_string lo permite. También lexical_cast es un reemplazo más recto para atoi, itoa.
  2. Si realmente no necesita la inteligencia de un puntero inteligente, no lo use.
  3. Para intercambiar utilice swap. Usar operaciones bitwise solo para operaciones bitwise-checking / setting / inverting flags, sucesivamente.
  4. Las excepciones son rápidas. Permiten eliminar ramas de condición de comprobación de errores, por lo que si realmente "nunca ocurren", aumentan el rendimiento.
 3
Author: Abyx,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-11-15 15:05:38

La multiplicación por bitshifting no mejora el rendimiento en C, el compilador lo hará por ti. Solo asegúrese de multiplicar o dividir por 2 valores^n para el rendimiento.

El intercambio de campos de bits también es algo que probablemente confundirá a tu compilador.

No tengo mucha experiencia con el manejo de cadenas en C++, pero por lo que sé, es difícil creer que sea más flexible que scanf e printf.

Además, estas declaraciones de" nunca debes", generalmente las considero como recomendaciones.

 2
Author: onemasse,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-11-13 10:25:42

Todas tus preguntas son a priori. Lo que quiero decir es que las estás preguntando en abstracto, no en el contexto de ningún programa específico cuyo desempeño sea de tu incumbencia. Es como intentar nadar sin estar en el agua.

Si sintoniza un programa concreto específico, encontrará problemas de rendimiento, y es probable que no tengan casi nada que ver con estas preguntas abstractas. Lo más probable es que todas sean cosas que no podrías haber pensado a priori.

Para un ejemplo específico de esto, mira aquí.

Si pudiera generalizar a partir de la experiencia, una fuente importante de problemas de rendimiento es generalidad galopante. Es decir, mientras que la abstracción de la estructura de datos generalmente se considera una buena cosa, cualquier cosa buena puede ser masivamente utilizada en exceso, y luego se convierte en una cosa mala paralizante. Esto no es raro. En mi experiencia es típico.

 2
Author: Mike Dunlavey,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 11:53:22

Creo que estás respondiendo grandes partes de tu pregunta por tu cuenta. Personalmente prefiero el código fácil de leer (incluso si entiendes el estilo C, tal vez el siguiente en leer tu código tenga más problemas con él) y el código seguro (que sugiere stringstream, excepciones, punteros inteligentes...)

Si realmente tiene algo en lo que tiene sentido considerar operaciones bitwise - ok. Pero a menudo veo que los programadores de C usan un char en lugar de un par de bools. No me gusta esto.

La velocidad es importante, pero la mayor parte del tiempo se requiere generalmente en algunos puntos de acceso en un programa. Así que a menos que midas que alguna técnica es un problema (o sabes bastante seguro de que se convertirá en uno) Prefiero usar lo que llamas estilo C++.

 1
Author: Philipp,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-11-13 09:45:15

¿Por qué el carácter costoso de las excepciones es un argumento? Las excepciones son excepciones porque son raras. Su rendimiento no influye en el rendimiento general. Los pasos que debe seguir para hacer que su excepción de código sea segura tampoco influyen en el rendimiento. Pero por otro lado las excepciones son convenientes y flexibles.

 1
Author: noxmetus,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-11-13 09:54:39

Esto no es realmente una "respuesta", pero si trabajas en un proyecto donde el rendimiento es importante (por ejemplo, embedded/games), la gente suele hacer la forma C más rápida en lugar de la forma C++ más lenta en las formas que describiste.

La excepción puede ser operaciones bitwise, donde no se gana tanto como se podría pensar. Por ejemplo ,"No utilices el desplazamiento a la izquierda para multiplicar por dos."Un compilador decente a medio camino generará el mismo código para

 1
Author: Johan Kotlinski,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-11-13 09:59:07