diego sevilla’s weblog
it is better to remain silent and be thought a fool,
than to open your mouth and remove all doubt -- groucho marx

19/2/2009

C++: Optimizaciones del compilador

Filed under: español, código/code — Diego Sevilla @ 23:58 — In English

Hoy, durante la primera reunión de la Cátedra que hemos tenido en SAES, se me ha ocurrido una idea para hacer logging de una aplicación en C++. Hace un tiempo leí en el blog de mi amigo Boris Kolpackov cómo el compilador de C++ es lo suficientemente inteligente como para darse cuenta de que cierto código no lo debe generar.

El caso del log es tal que a veces se quiere activar y otras veces desactivar de una aplicación. Cuando se desactiva para la aplicación que se entregará al usuario, cierto código de logging no tendría que aparecer (ni que ocupar tiempo) de la aplicación entregada. Sin embargo, durante el desarrollo, es interesante que el log esté activo.

Así pues, ¿cómo conseguir que el mismo código aparezca o no? La opción más común es utilizar el preprocesador para definir algún macro de depuración. Estos macros al final son algo extraños, porque utilizan el preprocesador y no objetos normales de C++, lo cual queda algo extraño y poco elegante. Viendo su artículo, se me ocurrió que se podría especializar un patrón (template) en el que la versión por defecto no hiciera nada (no hay log), y la versión activada enviaba la salida al log. He aquí una posible implementación:

#include <iostream>

class logger_base
{
public:
    logger_base (std::ostream& s)
        : s_ (s)
    {
    }

protected:
    std::ostream& s_;
};

template <bool b>
class logger: public logger_base
{
public:
    logger (std::ostream& s)
        : logger_base (s)
    {
    }

    template <typename T>
    friend logger<b>&
    operator<< (logger<b> const& l, T const& t)
    {
        // Default: do nothing
        return const_cast<logger<b>&>(l);
    }

    template <typename T>
    friend logger<b>&
    operator<< (logger<b> const& l, char const* t)
    {
        // Default: do nothing
        return const_cast<logger<b>&>(l);
    }
};

template<>
class logger<true> : public logger_base
{
public:
    logger (std::ostream& s)
        : logger_base (s)
    {
    }

    template <typename T>
    friend logger<true>&
    operator<< (logger<true> const& l, T const& t)
    {
        // Do now: log
        logger<true>& _l = const_cast<logger<true>&>(l);
        _l.s_ << t;
        return _l;
    }

    template <typename T>
    friend logger<true>&
    operator<< (logger<true> const& l, char*const t)
    {
        // Do now: log
        logger<true>& _l = const_cast<logger<true>&>(l);
        _l.s_ << t;
        return _l;
    }
};

// DO LOG
typedef logger<true> logger_t;

int
main (int argc, char**argv)
{
    logger_t log (std::cout);

    log << "abc" << "cde";

    return 0;

}

El código, primero, define un patrón que se especializa parcialmente a posteriori. Como se ha dicho, la implementación por defecto es no hacer nada. La clase logger<T> implementa el operator<< de tal forma que no hace absolutamete nada. La clase acepta cualquier std::ostream, con lo que se puede mostrar por pantalla o por cualquier fichero, socket, etc.

El código del patrón se especialza para el caso “true”. Si el patrón se crea con este valor, ahora los operadores realizan la función de log de forma normal.

El usuario puede, entonces, definir el tipo logger_t como un typedef del logger adecuado, y utilizar a partir de ese momento logger_t durante todo el código.

Nótese cómo ahora no hay ningún elemento extraño ni macro más o menos afortunado. El logging se hace con streams de C++.

Ahora bien. La preunta que surge es: cuando se selecciona el patrón “false” se genera código o se elimina? Primero veamos qué genera el compilador para el caso “true” (con optimización -O2):

main:
.LFB1037:
        subq    $8, %rsp
.LCFI1:
        movl    $3, %edx
        movl    $.LC0, %esi
        movl    $_ZSt4cout, %edi
        call    _ZSt16__ostream_insertIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_PKS3_l
        movl    $3, %edx
        movl    $.LC1, %esi
        movl    $_ZSt4cout, %edi
        call    _ZSt16__ostream_insertIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_PKS3_l
        xorl    %eax, %eax
        addq    $8, %rsp
        ret

Véase cómo el compilador reduce las llamadas inline a una llamada a ostream::insert dos veces (con las dos cadenas), y el log se produce. Ahora para el caso “false” (también con -O2):

main:
.LFB1037:
        xorl    %eax, %eax
        ret

Esto es, el compilador no produce ningún código (el valor 0 se retorna en eax), y optimiza automáticamente todas las llamadas al log. Estos mecanismos de metaprogramación hacen a C++ todavía más potente de lo que parece a simple vista.

2 Comments »

  1. Joder, eres un cabrito (en diminutivo por quitarle hierro al asunto ;-) ) Precisamente y como consecuencia a ciertos desarrollos que estamos haciendo donde trabajo y en los que se usa código para hacer log basado en macros llegué a lo conclusión que lo suyo era intentar actualizar esto y desarrollar un sistema un poco más moderno. Como no, ipso facto quedó apuntado en la zona de “en cuanto pueda” del TODO-list. Y se me ocurre venir aqui y me encuentro con esto. Y encima se te ocurre en la primera reunion de la cátedra de SAES. Igual son ideas universales que “flotan” en el ambiente. ;-)

    En fin, si necesitais voluntarios para esto, cogedme de un pie y os cederé mi mano manca de programar. Hareis un bien muy grande en cierta empresa que yo me sé.

    Un saludo.

    Comment by Juan Pedro — 2/3/2009 @ 19:02

  2. Jaja, Juan Pedro: tu ayuda siempre será bienvenida. Hemos empezado varias líneas a la vez en la cátedra y vamos poco a poco. En realidad esto debería haber ido al hipotético sitio web de la cátedra, pero también lo estamos montando…

    Esta idea funciona bien. Habría que perfilarla, para incluir por ejemplo logs por niveles, la inclusión automática de la fecha y hora, por ejemplo, etc. Lo bueno es que estas técnicas de meta-programación o templates se prestan muy bien para este tipo de cosas… Y ve pensando, por ejemplo, también en tests unitarios, que también estamos trabajando en ello. Por ahora estamos con macros, pero también nos gustaría hacer un interfaz parecido a este sin macros.

    Saludos!
    diego.

    PD. No te preocupes, que esto al final termina (si nos lo aceptan) en la empresa que tú y yo sabemos… :)

    Comment by Diego Sevilla — 2/3/2009 @ 19:46

RSS feed for comments on this post. TrackBack URI

Leave a comment

Line and paragraph breaks automatic, e-mail address never displayed, HTML allowed: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <code> <em> <i> <strike> <strong>


Anti-Spam by WP-Morph 

Creative Commons License
This work is licensed under a Creative Commons License.
EWWV  AWStats  Site Meter 24 queries. 0.074 seconds. Powered by WordPress
406021 email messages processed in this box. 10858 were spam

0