Alba on Tech

Talking about technology

Kata: Conoce tu lenguaje: Excepciones

Voy a proponeros una Kata, pero antes tengo que poner el contexto.

Una de las cosas más importantes para escribir código eficiente, es conocer bien el lenguaje que estamos utilizando. Esto parece muy obvio, pero no lo es tanto. Hay normativas de codificación que se dan por supuestas, y no necesariamente son iguales en todos los lenguajes.

Un ejemplo de esto es el tema de las excepciones. No voy a hablar de la horrible práctica de los catch vacíos. Voy a hablar de eficiencia.

En muchas ocasiones, nos encontramos con una operación que tiene varios condicionantes, que pueden hacer que falle. Antes esto, hay dos aproximaciones. La primera es utilizar sentencias condicionales que tengan en cuenta todas las condiciones. Vamos a verlo con un ejemplo muy simple. Al acceder a un elemento de un array, tenemos que asegurarnos que el índice está dentro del array. Por ejemplo:


        int[] arr={0,1,2,3,4,5,6,7,8,9};
        private int access1(int pos){
            if ((pos<arr.length)&&(pos>0)){
                return arr[pos];
            }else{
                return 0;
            }
        }

Pero existe otra aproximación. No comprobar nada, lanzarse con alegría a la tarea, y actuar en caso de fallo. El mismo ejemplo quedaría así:


        private int access2(int pos){
            try{
                return arr[pos];
            }catch (ArrayIndexOutOfBoundsException e){
                return 0;
            }
        }

Ambas funciones consiguen exáctamente el mismo resultado. Pero, ¿cual es mejor, desde el punto de vista de la eficiencia? Siguiendo con el ejemplo en java, vamos a lanzar cada una de las funciones 10 millones de veces, tratando de acceder a un valor fuera de rango:


        long start = System.currentTimeMillis();
        for (long i=0;i<10000000l;i++){
            int a = access1(20);
        }
        long end = System.currentTimeMillis();
        System.out.println ("Time elapsed ifs value 20: "+ ( end - start ) + " mseconds");
        
        start = System.currentTimeMillis();
        for (long i=0;i<10000000l;i++){
            int a = access2(20);
        }
        end = System.currentTimeMillis();
        System.out.println ("Time elapsed exception value 20: "+ ( end - start ) + " mseconds");

Los resultados lo dejan muy claro. El acceso controlado con sentencias condicionales tarda 8 milisegundos, mientras que el acceso controlado con excepciones tarda 45 milisegundos. ¡Las excepciones son 5 veces más lentas! ¡Siempre hay que trabajar con condicionales! Pero, un momento… Sólo hemos probado con el caso “malo”, cuando el índice está fuera de rango. Vamos a probar con un índice válido:


        start = System.currentTimeMillis();
        for (long i=0;i<10000000l;i++){
            int a = access1(2);
        }
        end = System.currentTimeMillis();
        System.out.println ("Time elapsed ifs value 2: "+ ( end - start ) + " mseconds");
        
        start = System.currentTimeMillis();
        for (long i=0;i<10000000l;i++){
            int a = access2(2);
        }
        end = System.currentTimeMillis();
        System.out.println ("Time elapsed exception value 2: "+ ( end - start ) + " mseconds");

Los resultados lo dejan muy claro. El acceso controlado con sentencias condicionales tarda 42 milisegundos, mientras que el acceso controlado con excepciones tarda 26 milisegundos. ¡Las excepciones son 2 veces más rápidas!

Las conclusiones que obtenemos es que, en java, el coste de capturar una excepción es muy alto, pero en cambio cuando todo va bien y no se lanza ninguna excepción, no se añade ningún sobrecoste. Por lo tanto, en código crítico, donde la eficiencia sea vital, y haya pocas probabilidades de que ocurra una excepción, es más eficiente olvidarse de las comprobaciones, y capturar excepciones cuando sea necesario. Puede ser contraintuitivo, pero los números no mienten.

Pero, como decía en el título, hay que conocer tu lenguaje. Lo que puede ser cierto para java, no tiene que ser igual para otros lenguajes. A modo de ejemplo, algunos valores:


    PYTHON:
        Time elapsed ifs value 20:  2330.0 mseconds
        Time elapsed exception value 20:  10650.0 mseconds
        Time elapsed ifs value 2:  2750.0 mseconds
        Time elapsed exception value 2:  1890.0 mseconds


    GROOVY:
        Time elapsed ifs value 20: 638 mseconds
        Time elapsed exception value 20: 739 mseconds
        Time elapsed ifs value 2: 932 mseconds
        Time elapsed exception value 2: 682 mseconds

En python, las reflexiones de java se mantienen. Sin embargo en groovy las diferencias son muchísimo menores. Las mismas reglas no se aplican a todos los lenguajes.

Y ahora, os toca trabajar a vosotros. ¿Qué es más eficiente en vuestro lenguaje preferido? ¿Control por condicionales, o control por excepciones?

Y recuerda, no se trata de un concurso. Se trata de una kata. Practica y mejora. Siempre

Advertisements

20 January, 2012 - Posted by | Kata, Programación

1 Comment »

  1. Exacto, penalizar siempre por algo improbable es erróneo… y en este caso simple ni siquiera hay motivos de “claridad o limpieza de código”.

    Comment by Luiyo | 21 January, 2012 | Reply


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: