¿Debo pensar en el código de máquina compilado cuando escribo mi código?

20

Por ejemplo, tengo el siguiente código:

auto z = [](int x) -> int {
    if (x > 0) {
        switch (x) {
            case 2: return 5;
            case 3: return 6;
            default: return 1;
            }
        }
    return 0;
    };

Y luego llamo a esto varias veces. En el código asm veo llamadas externas con lambda ... algo ... No se está volviendo fácil de leer y creo que también puede causar rendimiento. Entonces, tal vez gano en metaprogramación, pero ¿pierdo en depuración y rendimiento de asm? ¿Debo evitar las características del lenguaje moderno, las macros y otros aspectos de meta programación para asegurarme de la simplicidad de depuración y rendimiento?

cnd
fuente
1
Dependiendo de la versión del compilador y su biblioteca estándar incluida, lambda puede implementarse de manera ineficiente. Ver esta pregunta en Stackoverflow. Sin embargo, la responsabilidad de la mejora debe recaer en el proveedor del compilador.
rwong
15
No debería necesitar depurar el código de ensamblaje, a menos que esté en una ruta crítica de rendimiento. Además, "código limpio"! = "Buenas actuaciones".
B 8овић
Arregla tu sangría por favor. Intenté hacerlo, pero parece que no puedes editar solo los espacios en blanco.
Christoffer Hammarström
3
@Heather: Parece que estás usando el estilo Ratliff , que nunca antes había visto y me resulta difícil de leer. Es sin duda uno de los menos conocidos. Mi impresión fue que no habías sangrado correctamente. No importa, entonces, si lo encuentra legible, no estoy de acuerdo.
Christoffer Hammarström
1
El ifes completamente redundante en el código de ejemplo, y aunque el compilador probablemente se dará cuenta de que no hay razón para tentar una predicción de rama incorrecta.
dmckee

Respuestas:

59

¿Debo pensar en el código de máquina compilado cuando escribo mi código?

No , no cuando escribe su código la primera vez y no sufre ningún problema de rendimiento real y medible. Para la mayoría de las tareas, este es el caso estándar. Pensar demasiado pronto sobre la optimización se llama "optimización prematura", y hay buenas razones por las cuales D. Knuth lo llamó "la raíz de todo mal" .

, cuando mide un cuello de botella de rendimiento real y demostrable, e identifica esa construcción lambda específica como la causa raíz. En este caso, puede ser una buena idea recordar la "ley de las abstracciones con fugas" de Joel Spolsky y pensar en lo que podría suceder a nivel asm. Pero tenga cuidado, es posible que se sorprenda de lo pequeño que será el aumento del rendimiento cuando reemplace una construcción lambda por una construcción de lenguaje "no tan moderna" (al menos, cuando se utiliza un compilador decente C ++).

Doc Brown
fuente
2
+1 Conciso, preciso y fácil de seguir por Doc habitual, me alegro de tenerte por aquí
Jimmy Hoffa
De acuerdo, respuesta muy clara.
cnd
8

La elección entre lambda y functor-class es una compensación.

La ganancia de lambda es principalmente sintáctica, al minimizar la cantidad de repeticiones y permitir que el código relacionado conceptualmente se escriba en línea, dentro de la función que lo va a utilizar (inmediatamente o más tarde).

En cuanto al rendimiento, esto no es peor que una clase functor , que es una estructura o clase C ++ que contiene un solo "método". De hecho, los compiladores tratan la lambda de manera diferente a una clase de functor generada por el compilador detrás de escena.

// define the functor method somewhere
struct some_computer_generated_gibberish_0123456789
{
    int operator() (int x) const
    {
        if (x == 2) return 5;
        if (x == 3) return 6;
        return 0;
    }
};

// make a call
some_computer_generated_gibberish_0123456789 an_instance_of_0123456789;
int outputValue = an_instance_of_0123456789(inputValue);

En su ejemplo de código, en cuanto al rendimiento, no es diferente de una llamada de función, porque esa clase de functor no tiene estado (porque tiene una cláusula de captura vacía), por lo que no requiere asignación, constructor ni destrucción.

int some_computer_generated_gibberish_0123456789_method_more_gibberish(int x)
{
    if (...) return ...;
    return ...;
}

La depuración de cualquier código C ++ no trivial utilizando un desensamblador siempre ha sido una tarea difícil. Esto es cierto con o sin usar lambda. Esto es causado por la sofisticada optimización de código del compilador de C ++ que resultó en la reordenación, intercalación y eliminación de código muerto.

El aspecto de cambio de nombre es algo desagradable, y el soporte del depurador para lambda todavía está en pañales . Solo se puede esperar que el soporte del depurador mejore con el tiempo.

Actualmente, la mejor manera de depurar el código lambda es utilizar un depurador que admita establecer puntos de interrupción en el nivel del código fuente, es decir, especificando el nombre del archivo fuente y el número de línea.

rwong
fuente
3

Para agregar a la respuesta de @DocBrown, recuerde que actualmente las CPU son baratas pero la mano de obra es costosa.

En el costo general de un programa, el hardware generalmente es trivial en comparación con el costo de mantenimiento, que es, con mucho, la parte más costosa de un proyecto típico (incluso más que su desarrollo).

Por lo tanto, su código necesita optimizar el mantenimiento por encima de todo lo demás, excepto cuando el rendimiento es crítico (e incluso entonces el mantenimiento debe considerarse).

Paddy Landau
fuente
Solo parcialmente cierto. Si su código ejecuta O (n ^ 2) (cuadrático) y puede hacer que sea algo mejor que decir O (log (n)) (logarítmico), entonces el hardware nunca tendrá un aumento de rendimiento tan grande como cambiar el código. En el caso especificado por el póster original, esto es muy poco probable.
gnash117
@ gnash117: sí, tiene razón si el código se ejecutará muchas veces; Gracias por señalar esto. En tales casos, documentar el código claramente lo mantendrá mantenible mientras permite la mejora del rendimiento.
Paddy Landau
"el trabajo es caro" - Correcto. El tiempo de su cliente es muy importante y a menudo costoso.
Cerad