jueves, 14 de julio de 2011

Utilizar la memoria RAM como cache automatico en Linux

En los sistemas linux, el uso de memoria RAM como cache, es gestionado por el kernel de manera automatica, sin necesidad de configurar o ejecutar los programas de manera distinta a la usual.

Veamos el siguiente ejemplo, en el cual, se tiene un archivo de 25.85M aproximadamente.

   $ ls -l archivo.bin
   -rwxr-xr-x. 1 cmartine cmartine 27109840 Jul  1 10:52 archivo.bin

Utilizando el comando time, para determinar el tiempo de ejecucion y el comando cat para realizar la lectura del archivo, obtenemos lo siguiente en la primer ejecucion. Nota: se envia la salida al dispositivo /dev/null, para descartarla.

   $ time cat archivo.bin > /dev/null
   real   0m0.466s
   user   0m0.002s
   sys   0m0.123s

El tiempo que se tardo en ejecutar fue de 0.466 segundo. Posteriormente, ejecutando el mismo comando inmediatamente 3 veces seguidas, se obtiene el siguiente resultado:

   $ time cat archivo.bin > /dev/null
   real    0m0.040s
   user    0m0.002s
   sys     0m0.037s

   $ time cat archivo.bin > /dev/null
   real    0m0.026s
   user    0m0.001s
   sys     0m0.025s

   $ time cat archivo.bin > /dev/null
   real    0m0.029s
   user    0m0.000s
   sys     0m0.025s

Como se puede observar, el tiempo real, disminuyo drasticamente a menos de la decima parte del tiempo original. Esto es debido a que el kernel de linux maneja de manera automatica la memoria RAM que no se emplea en crear un cache de los archivos utilizados, sin necesidad de realizar configuracion alguna. La cantidad de memoria que esta siendoutilizada en este cache, se puede consultar utilizando el comando top.

¿Por que linux hace esto? Sencillamente por que si no se emplea la memoria, estaria desocupada (desperdiciandose cuando el sistema no la esta utilizando para realizar algun trabajo). Es importante aclara, que cuando se ejecuta un programa, el cual requiere utilizar mucha memoria, se libera parte de la memoria utilizada para el cache y se asigna al programa, pudiendo llegar a la situacion en que el cache ya no tenga memoria asignada, por que un programa requiere utilizar toda la memoria del sistema

jueves, 23 de junio de 2011

Comparacion entre 2 fechas

Comparar 2 fechas, para determinar cual es mayor, puede ser sencillo, sobre todo, si con lo que trabajas soporta el concepto de fecha y/o tiempo, por ejemplo: java.util.Date en java o System.DateTime en C#, que son lenguajes de programacion o incluso en hojas de datos, tipo Microsoft Excel o LibreOffice Calc.

El problema, se presenta, cuando es necesario trabajar con lenguajes que no conocen el concepto, como los shells de unix o cuando se almacenan archivos que contienen la fecha en el nombre del mismo.

En este punto la abstraccion de la fecha y la forma en como se representa, suele tomar un papel importante, dado que puede resolver completamente el problema, si se emplea el formato correcto.

Formas comunes de representar una fecha son: dia/mes/año en mexico o mes/dia/año en EEUU. Para nombres de archivos se suelen substituir las diagonales, por guiones, asi como el uso del nombre del mes y no el numero de mes. Por ejemplo:
  • Jun/27/2011
  • Oct/21/2011
  • Jul/1/2011
  • Oct/24/2005
  • Jul/4/2011
Si se ordena alfabeticamente, se obtiene lo siguiente:
  • Jul/1/2011
  • Jul/4/2011
  • Jun/27/2011
  • Oct/21/2011
  • Oct/24/2005
Lo cual no es lo que se requiere, sin embargo, si se reacomoda la fecha utilizando el orden año, mes, dia:
  • 2011/Jun/27
  • 2011/Oct/21
  • 2011/Jul/1
  • 2005/Oct/24
  • 2011/Jul/4
Al ordenar se obtiene lo siguiente:
  • 2005/Oct/24
  • 2011/Jul/1
  • 2011/Jul/4
  • 2011/Jun/27
  • 2011/Oct/21
El problema con el que nos encontramos es que la l esta antes que la n, en orden alfabetico, para sortear este inconveniente, en vez del nombre del mes, utilizamos el numero del mes:

  • 2011/6/27
  • 2011/10/21
  • 2011/7/1
  • 2005/10/24
  • 2011/7/4 
Al ordenar, obtenemos lo siguiente:

  • 2005/10/24
  • 2011/6/27
  • 2011/10/1
  • 2011/7/1
  • 2011/7/4
 Este resultado tampoco es correcto, esto es debido a que la longitud de los meses, es variable, por lo que, agregamos un 0 a la izquierda, cuando el mes esta formado por un solo digito (es recomendable tambien hacerlo para los dias)

  • 2011/06/27
  • 2011/10/21
  • 2011/07/01
  • 2005/10/24
  • 2011/07/04 
Al ordenar, obtenemos lo siguiente:

  • 2005/10/24
  • 2011/06/27
  • 2011/07/01
  • 2011/07/04
  • 2011/10/01
Que es finalmente lo que se requiere.

Esto, se puede aplicar a la creacion de nombres de archivos, por ejemplo, para poder ordenarlos por una fecha en particular, sobre todo cuando la fecha de creacion, es incorrecta (cuando son enviados por correo electronico y se pierde la fecha de creacion del archivo), por ejemplo los siguientes nombres de archivos seran ordenados correctamente por el sistema operativo al momento de listarlos (esto tanto en windows, linux o mac os)
  • reporte_2011-06-27.txt
  • reporte_2011-10-21.txt
  • reporte_2011-07-01.txt
  • reporte_2005-10-24.txt
  • reporte_2011-07-04.txt
 Al ser listados, se obtiene lo siguiente:
  • reporte_2005-10-24.txt
  • reporte_2011-06-27.txt
  • reporte_2011-07-01.txt
  • reporte_2011-07-04.txt
  • reporte_2011-10-21.txt
En unix, incluso, es posible emplear las fechas en este formato, con el comando expr, el cual puede comparar dos cadenas y determinar cual es mayor o menor.

jueves, 5 de mayo de 2011

Implementación la funcion compareTo de IComparable en C#






La interfaces IComparable e IComparable<t> de C#, se utilizan para comparar objetos, normalmente del mismo tipo y determinar, si alguno es mayor, menor o la equivalencia. A continuacion, se presentan 4 ejemplos de como implementar esta funcionalidad.

Nota1: Para el primer ejemplo, se utiliza el concepto de National Identification Number (CURP en México, RUN en Chile, DNI en España y Argentina o Tarjeta de Identidad en colombia, por ejemplo)
Nota2: Por razones de compatibilidad, se implementan 2 interfaces distintas, con funcionalidades similares, en la implementacion de estas clases.

class Person1 : System.IComparable, System.IComparable<Person1>
    {
        private string nin; // National Identification Number
        private string name;
        private string surname;

        //...

        public int CompareTo(object obj)
        {
            if (obj is Person1)
            {
                return obj == null ? 1 : nin.CompareTo(((Person1)obj).nin);
            }
            throw new System.ArgumentException();
        }

        public int CompareTo(Person1 p)
        {
            return p == null ? 1 : nin.CompareTo(p.nin);
        }

    }

    class Person2 : System.IComparable, System.IComparable<Person2>
    {
        private int id;
        private string name;
        private string surname;

        //...

        public int CompareTo(object obj)
        {
            if (obj is Person2)
            {
                return obj == null ? 1 : id.CompareTo(((Person2)obj).id);
            }
            throw new System.ArgumentException();
        }

        public int CompareTo(Person2 p)
        {
            return p == null ? 1 : id.CompareTo(p.id);
        }


    }

    class Person3 : System.IComparable, System.IComparable<Person3>
    {
        private int id;
        private string name;
        private string surname;

        //...

        public int CompareTo(object obj)
        {
            if (obj == null)
            {
                return 1;
            }
            else
            {
                if (obj is Person3)
                {
                    Person3 o = (Person3)obj;
                    return id - o.id;
                }
                throw new System.ArgumentException();
            }
        }

        public int CompareTo(Person3 p)
        {
            return p == null ? 1 : id - p.id;
        }

    }

    class Person4 : System.IComparable, System.IComparable<Person4>
    {
        private int idPart1;
        private string idPart2;
        private string name;
        private string surname;

        //...

        private int CompareTo0(Person4 p)
        {
            int v = idPart1 - p.idPart1;
            if (v == 0)
            {
                v = idPart2.CompareTo(p.idPart2);
            }
            return v;
        }

        public int CompareTo(object obj)
        {
            if (obj == null)
            {
                return 1;
            }
            else
            {
                if (obj is Person4)
                {
                    return CompareTo0((Person4)obj);
                }
                throw new System.ArgumentException();
            }
        }

        public int CompareTo(Person4 p)
        {
            if (p == null)
            {
                return 1;
            }
            else
            {
                return CompareTo0(p);
            }
        }

    }


En el primer y segundo ejemplo, se implementa la funcionalidad, utilizando la funcion CompareTo implementada por la clase string y por la estructura int32. Regresando el resultado de ejecutar dicha funcion. En el tercer ejemplo, se muestra, como se puede implementar utilizando la diferencia entre 2 enteros (es posible utilizar cualquier tipo entero, como un byte, short, int). Finalmente, en el cuarto ejemplo, se implementa la funcionalidad utilizando 2 propiedades, donde se compara la primer propiedad y en caso de igualdad, se regresa el resultado de comparar la segunda propiedad. Tambiem, en este ultimo ejemplo, define una funcion intermedia (CompareTo0), la cual hace la comparacion real y es llamada por las 2 implementaciones del CompareTo.

Como se observa. por razones de compatibilidad, es necesario implementar la funcionalidad de 2 interfaces, una utlizando Generics y otra sin pre-generics. En la version Generics, es necesario validar que el tipo de corresponde con la clase que implementa, en caso de no coincidir, hay que generar una excepcion del tipo ArgumentException. En el caso del null, la documentacion tambien especifica regresar 1 explicitamente. Final mente, en C#, como en C, se especifican valores validos cualquier entero positivo, negativo o cero (dentro de los rangos de int32)

Es recomendable, por cuestiones de simplicidad en la implementacion, incluir en la comparacion solo los valores que corresponden a la llave primaria, comparandolos, en orden de importancia, regresando el valor, del primero que resulte distinto (como se muestra en el ejemplo 4).

jueves, 28 de abril de 2011

Implementación la funcion compareTo de Comparable en java





La interface Comparable se emplea, para indicar si el objeto representado por this es menor, igual o mayor que el objeto que se pasa como parametro. A continuacion se presentan 3 ejemplos de clases que definen la funcion compareTo en base a una propiedad existente. Nota, en algunos paises existe el concepto de National Identification Number (CURP en México, RUN en Chile, DNI en España y Argentina o Tarjeta de Identidad en colombia, por ejemplo)


public class ExamplePerson1 implements Comparable<ExamplePerson1> {

 private String nin; // National Identification Number
 private String name;
 private String surname;

 ...

 @Override
 public int compareTo(ExamplePerson1 o) {
  return this.nin.compareTo(o.nin);
 }

}

public class ExamplePerson2 implements Comparable<ExamplePerson2> {

 private Integer id;
 private String name;
 private String surname;

 ...

 @Override
 public int compareTo(ExamplePerson2 o) {
  return this.id.compareTo(o.id);
 }

}

public class ExamplePerson3 implements Comparable<ExamplePerson3> {

 private int id;
 private String name;
 private String surname;

 ...

 @Override
 public int compareTo(ExamplePerson3 o) {
  return this.id < o.id ? -1 : (this.id == o.id ? 0 : 1);
 }

}

En el ejemplo 1 y 2, se emplean propiedades de tipo String e Integer las cuales implementan la interface Comparable, por lo que solo se regresa el resultado de llamar la funcion compareTo de la propiedad respectiva. En el ejemplo 3 se utiliza una propiedad de tipo nativo y se muestra un ejemplo de como puede implementarse la funcion, utilizando las clases que envuelven a los tipos nativos (wrappers), evitando el uso de autoboxing y autounboxing.

En estos ejemplos, solo se emplea una propiedad, para implementar la funcionalidad. La recomendacion, es utilizar solo los atributos que forman la llave primaria (el concepto de base de datos). En caso de requerir utilizar mas de una propiedad, se incremanta el grado de complejidad. Como se muestra en el siguiente ejemplo.

public class ExamplePerson4 implements Comparable<ExamplePerson4> {

 private Integer idPart1;
 private Integer idPart2;
 private String name;
 private String surname;

 ...

 @Override
 public int compareTo(ExamplePerson4 o) {
  int result = this.idPart1.compareTo(o.idPart1);
  if(result == 0) {
   result = this.idPart2.compareTo(o.idPart2);
  }
  return result;
 }

}

Como se observa, si la comparacion de la primer propiedad es cero, hay que comparar la segunda propiedad y asi sucesivamente, hasta encontrar una distinta o ya no tener mas propiedades.

--- Actualizacion ---

En ocasiones, es necesario evitar que la comparacion genere una excepcion del tipo NullPointerException, cuando el parametro que recibe la funcion compareTo, es null. Para procesar el null, es necesario insertar un if para validar si es null, en caso de ser null, se regresa el valor necesario (normalmente 0), en caso contrario, se regresa el valor calculado, utilizando el algoritmo descrito anteriormente.

public class ExamplePerson5 implements Comparable<ExamplePerson5> {

 private Integer id;
 private String name;
 private String surname;

 ...

 @Override
 public int compareTo(ExamplePerson5 o) {
  return o == null ? 0 : this.id.compareTo(o.id);
 }

}

Existe la posibilidad de implmentarlo utilizando un try-catch, en vez de utilizar un if. Si bien en apariencia no se ejecutan 2 operaciones, cuando se emplea el if (primero la comparacion contra null y posteriormente la evaluacion del if en si). En la practica no es optimo, dado que cuando se recibe un null, se genera una excepcion, que implicitamente es, la creacion del objeto excepcion, el llenado del stack (que en el caso de un programa muy sencillo son 3 o 4 elementos en el stack, pero en el caso de sistemas complejos, pueden ser mas de 50 elementos).

public class ExamplePerson6 implements Comparable<ExamplePerson6> {

 private Integer id;
 private String name;
 private String surname;

 ...

 @Override
 public int compareTo(ExamplePerson6 o) {
  try {
   return this.id.compareTo(o.id);
  } catch (NullPointerException e) {
   return 0;
  }
 }

}

En conclusion, siempre es recomendable utilizar un if, si se necesita regresar un valor en caso de que el parametro sea null y evitar el uso de un try-catch.

miércoles, 27 de abril de 2011

Diferencia entre 2 fechas (años cumplidos) en java

Obtener los años cumplidos, a partir de una fecha en particular, tiene sus aplicaciones (validar mayoria de edad en las personas, verificar si aun aplica una garantia, a partir de la fecha de compra, etc)

La aproximacion, es la siguiente:


 1 import java.util.Calendar;
 2 import java.util.Date;
 3 
 4 public class DatesDiff {
 5 
 6  public static int calculateAge(Date bornDate) {
 7   Calendar cal = Calendar.getInstance(); // current date
 8   int currYear = cal.get(Calendar.YEAR);
 9   int currMonth = cal.get(Calendar.MONTH);
10   int currDay = cal.get(Calendar.DAY_OF_MONTH);
11   cal.setTime(bornDate); // now born date
12   int years = currYear - cal.get(Calendar.YEAR);
13   int bornMonth = cal.get(Calendar.MONTH);
14   if (bornMonth == currMonth) { // same month
15    return cal.get(Calendar.DAY_OF_MONTH) <= currDay ? years
16      : years - 1;
17   } else {
18    return bornMonth < currMonth ? years - 1 : years;
19   }
20  }
21 
22 }


Explicación:
07-07: Se obtiene la fecha actual, al obtener una instancia del objeto Calendar
08-10: Se obtienen lo valores del año, mes y dia actual (cache)
11-11: Se actualiza el objeto cal, con la fecha de nacimiento
12-12: Se calcula la diferencia de años
13-13: Se obtiene el año de nacimiento (cache)
14-14: Se verifica si el mes actual coincide con el mes de nacimiento
15-15:  Si los meses coinciden, se valida si el los dias coinciden, de ser este el caso, se regresan la diferencia de años, en caso contrario (aun no se cumple otro año) se regresa la diferencia menos uno.
18-18: Si los meses no coinciden, se regresala diferencia de los años menos 1 si el mes de nacimiento es menor que el mes actual y la diferencia de años, en caso contrario

En este codigo, se utiliza un solo objeto Calendar en vez de 2 objetos distintos. En vez de utilizar un objeto Calendar nuevo, obtenemos los valores que se emplearan a posteriormente en variables de tipo nativo, las cuales radican en el pila (stack), en vez de el  (heap).

Una optimizacion posible, es parametrizar la fecha actual, para el tema de internacionalizacion, con respecto a las zonas horarias, ya que se da la posibilidad que tomando en cuenta la fecha de la zona horaria de un pais distnto puede ser que se cumpla o no un año mas.

jueves, 1 de julio de 2010

tablas de hash y su relacion con las funciones GetHashCode y Equals de C# y hashCode y equals de Java

La mayoria de los lenguajes de programacion implementan tablas de hash en alguna de sus variantes (Hashtable, HashMap en Java o Hastable en C#). Un problema asociado a esta funcionalidad, es la correcta implementacion de las funciones para generar el codigo el codigo de hash de la llave, que sirve para obtener el valor asociado a la misma dentro de la tabla de hash.

Como se vio en el post hashCode y equals funcion en Java o GetHashCode y Equals en C#, una tabla de hash, se puede comparar con un diccionario. Dado que la funcion que obtiene el codigo hash de un objeto (GetHashCode para C# y hashCode para Java) puede obtener el valor entre -231 y +231 y si el codigo de hash, es unico, entonces, este codigo puede fungir como una "llave" adicional, sobre todo, si se define correctamente y se implementa una funcion que genere este codigo lo mas rapido posible.

Los tiempos de respuesta y uso de memoria de una tabla de hash, dependen de como se implemente, asi como de una correcta definicion de la funcion que genere el codigo de hash. El secreto del desempeño de la tabla de hash, esta el uso de la aritmetica modular, utilizada para determinar que codigos de hash son similares.

Tanto en Java, como en C# el codigo de hash, esta representado por un numero entero de 32 bits (4 bytes), esto da la posibilidad de tener 232 (poco mas de 4 mil millones o millardos) posibles codigos de hash distintos. Como se vio en otro post, el codigo de hash, se emplea para "indexar" la informacion y realizar una busqueda ejecutando el menor numero de comparaciones posibles.


Para ver como funciona comparemos ahora la tabla de hash con un hospital, el cual tiene un archivo, donde por cuestiones de orden, se colocan los expedientes en cajones. Existen 27 cajones (utilizaremos el alfabeto ingles, el cual no tiene ñ, ch, ni acentos, dieresis u otras letras, simbolos, acentos, etc) cada cajon almacena los expedientes correspondientes a la letra inicial del nombre de la persona.

Suponiendo que el hospital es nuevo (new Hospital()), los 27 cajones estan vacios. cuando llega el primer paciente, se le genera un nuevo expediente y cuando se da de alta, se almacena su expediente en el archivo, si el paciente se llama Ricardo, el cajon que tiene una letra R al frente tendra un unico expediente y los otros 26 expedientes, seguiran vacios.

Si llega un nuevo paciente, llamado Juan, lo ideal, es que se busque si no tiene un expediente, entonces, buscando en el cajon donde se almacenan los expedientes que inician con J, nos damos cuenta de que no tiene expediente y creamos uno nuevo, para posteriormente almacenarlo.

Pero, este hospital es un poco desordenado al momento de almacenar los archivos, pues cuando llega otra persona con cuyo nombre es Ramon, el nuevo expediente, simplemente se pone encima del anterior y cuando llega Rosa, ocurre lo mismo, es decir, dentro del cada cajon, los expedientes, no se ordenan. Esto provoca que cuando llega una persona llamada Romario, se deba buscar el nombre en 3 expedientes distintos, puesto que al no estar ordenados alfabeticamente, hay que comparar el nombre para verificar que no es el expediente buscado. Por lo que es ligeramente mas tardado el proceso de busqueda del expediente de una persona cuyo nombre inicia con R que si inicia con J o si inicia con cualquier otra letra del abecedario. Ya se atendio Romario y se le genero un expediente. Extrañamente, viene una persona que se llama Ruperto (si, ya se, muchas R), esto provoca que el cajon donde estan estos expedientes, comienze a desbordarse y la busqueda cada vez tarde mas.

Esto que estoy describiendo, ocurre, cuando la funcion que genera el codigo de hash (la letra inicial del nombre, en este caso) esta incorrectamente definida y todos o la mayoria de los elementos que estamos almacenando tengan el mismo codigo de hash.

Para evitar esta situacion, se suelen definir funciones, en ocasiones extrañamente complejas, imaginemos por un momento, que cambiase las reglas para almacenar la informacion, ahora tendre las mismas 27 cajas, pero para determinar el cajon donde se almacenara el expediente, se sumara el valor ASCII de cada letra del nombre de la persona, el resultado se multiplicara por 13 (un numero primo por cierto) y se realizara la operacion modulo 27, al resultado de este, se le sumara 1, el resultado de esto, se convertira a una letra, 1 por A, 2 por B  ... y 27 por Z ... Despues de realizar todas estas operaciones , no duden que probablemente haya uno y solo un expediente por cajon,  pero ahora la generacion del codigo de hash se volvio costosa en tiempo.

Ahora, que ocurre cuando se agregan mas letras al abecedario, por ejemplo acentos y dieresis, asi como la letra ñ, pero sin agregar cajones a nuestro sistema de almacenamiento. La solucion es buscar similitudes o equivalencias, por ejemplo n es parecida a la letra ñ y las letras acentuadas o con dieresis a las mismas sin el acento o la dieresis. Esto es buscar similitudes o equivalencias. Para realizar estas operaciones de "equivalencias" las tablas de hash, utilizan aritmetica modular, es decir, dado que el universo para la generacion del codigo de hash, es basto, se emplea este tipo de aritmetica, para determinar que codigos de hash son similares y almacenarlos conjuntamente.

jueves, 3 de junio de 2010

operador +, cadenas, cadenas constante y el compilador en C#

Al igual que en java, el compilador de C# es capaz de realizar optimizaciones al momento de compilar el codigo y convertir una instruccion en otra, utilizando las funciones definidas en la clase String.

Como se vio en el post dedicado a java, es posible utilizar el compilador a nuestro favor, cuando se conoce como funciona.
En el siguiente ejemplo, se muestra una concatenacion, utilizando el operador +.

01: using System;
02: using System.Collections.Generic;
03: using System.Linq;
04: using System.Text;
05: 
06: namespace ClassLibrary1
07: {
08:     public class Class1
09:     {
10:         void Test()
11:         {
12:             string a = "cadena1";
13:             string b = "cadena2";
14:             string x = a + b;
15:             Console.WriteLine(x);
16:         }
17:     }
18: }
19: 

Al decompilar el codigo, empleando el comando ildasm, se obtiene el siguiente codigo:

.method private hidebysig instance void  Test() cil managed
   {
     // Code size       29 (0x1d)
     .maxstack  2
     .locals init ([0] string a,
              [1] string b,
              [2] string x)
     IL_0000:  nop
     IL_0001:  ldstr      "cadena1"
     IL_0006:  stloc.0
     IL_0007:  ldstr      "cadena2"
     IL_000c:  stloc.1
     IL_000d:  ldloc.0
     IL_000e:  ldloc.1
     IL_000f:  call       string [mscorlib]System.String::Concat(string,
                                                              string)
     IL_0014:  stloc.2
     IL_0015:  ldloc.2
     IL_0016:  call       void [mscorlib]System.Console::WriteLine(string)
     IL_001b:  nop
     IL_001c:  ret
   } // end of method Class1::Test

El codigo que genera codigo similar es el siguiente:


01: using System;
02: using System.Collections.Generic;
03: using System.Linq;
04: using System.Text;
05: 
06: namespace ClassLibrary1
07: {
08:     public class Class2
09:     {
10:         void Test()
11:         {
12:             string x = String.Concat("cadena1","cadena2");
13:             Console.WriteLine(x);
14:         }
15:     }
16: }
17: 

Como es posible observar, el codigo generado ejecuta una funcion propia de la clase String, la cual sabe concatenar cadenas, generando una nueva cadena del tamaño necesario. Sin embargo, este comportamiento solo se presenta cuando se emplean 2, 3 y hasta 4 cadenas, ya cuando se trata de 5 o mas cadenas, primero construye un arreglo de cadenas, con las cadenas a concatenar y posteriormente emplea el metodo Concat(String[]). Por ejemplo:


01: using System;
02: using System.Collections.Generic;
03: using System.Linq;
04: using System.Text;
05: 
06: namespace ClassLibrary1
07: {
08:     class Class3
09:     {
10:         void Test()
11:         {
12:             string a = "cadena1";
13:             string b = "cadena2";
14:             string c = "cadena3";
15:             string d = "cadena4";
16:             string e = "cadena5";
17:             string x = a + b + c + d + e;
18:             Console.WriteLine(x);
19:         }
20:     }
21: }
22: 

Produciendo el siguiente codigo:

.method private hidebysig instance void  Test() cil managed
  {
    // Code size       84 (0x54)
    .maxstack  3
    .locals init ([0] string a,
             [1] string b,
             [2] string c,
             [3] string d,
             [4] string e,
             [5] string x,
             [6] string[] CS$0$0000)
    IL_0000:  nop
    IL_0001:  ldstr      "cadena1"
    IL_0006:  stloc.0
    IL_0007:  ldstr      "cadena2"
    IL_000c:  stloc.1
    IL_000d:  ldstr      "cadena3"
    IL_0012:  stloc.2
    IL_0013:  ldstr      "cadena4"
    IL_0018:  stloc.3
    IL_0019:  ldstr      "cadena5"
    IL_001e:  stloc.s    e
    IL_0020:  ldc.i4.5
    IL_0021:  newarr     [mscorlib]System.String
    IL_0026:  stloc.s    CS$0$0000
    IL_0028:  ldloc.s    CS$0$0000
    IL_002a:  ldc.i4.0
    IL_002b:  ldloc.0
    IL_002c:  stelem.ref
    IL_002d:  ldloc.s    CS$0$0000
    IL_002f:  ldc.i4.1
    IL_0030:  ldloc.1
    IL_0031:  stelem.ref
    IL_0032:  ldloc.s    CS$0$0000
    IL_0034:  ldc.i4.2
    IL_0035:  ldloc.2
    IL_0036:  stelem.ref
    IL_0037:  ldloc.s    CS$0$0000
    IL_0039:  ldc.i4.3
    IL_003a:  ldloc.3
    IL_003b:  stelem.ref
    IL_003c:  ldloc.s    CS$0$0000
    IL_003e:  ldc.i4.4
    IL_003f:  ldloc.s    e
    IL_0041:  stelem.ref
    IL_0042:  ldloc.s    CS$0$0000
    IL_0044:  call       string [mscorlib]System.String::Concat(string[])
    IL_0049:  stloc.s    x
    IL_004b:  ldloc.s    x
    IL_004d:  call       void [mscorlib]System.Console::WriteLine(string)
    IL_0052:  nop
    IL_0053:  ret
  } // end of method Class3::Test


En cambio, el compilador de C# al igua que el de java, es capaz de detectar cadenas constantes "inline" y generar una cadena con el resultado en tiempo de compilacion, evitando el procesamiento en tiempo de ejecucion


01: using System;
02: using System.Collections.Generic;
03: using System.Linq;
04: using System.Text;
05: 
06: namespace ClassLibrary1
07: {
08:     public class Class4
09:     {
10:         void Test()
11:         {
12:             string x = "cadena1" + "cadena2";
13:             Console.WriteLine(x);
14:         }
15:     }
16: }
17: 

Generando el siguiente codigo:

.method private hidebysig instance void  Test() cil managed
{
  // Code size       15 (0xf)
  .maxstack  1
  .locals init ([0] string x)
  IL_0000:  nop
  IL_0001:  ldstr      "cadena1cadena2"
  IL_0006:  stloc.0
  IL_0007:  ldloc.0
  IL_0008:  call       void [mscorlib]System.Console::WriteLine(string)
  IL_000d:  nop
  IL_000e:  ret
} // end of method Class4::Test

El compilador puede ayudar en el desempeño del sistema, al generar algunas de estas operaciones previamente.

miércoles, 2 de junio de 2010

cadenas en java (strings)

En java, como en muchos lenguajes de programacion modernos, existe una clase que define la representacion interna de una cadena durante la ejecucion del programa. En java esta clase es String. Internamente, la clase String, esta representada por un arreglo del tipo nativo char. Derivado de esto, la maquina virtual de java, maneja las cadenas como un conjunto de caracteres codificados en Unicode en formato UTF-16 (No UTF-8, no ASCII). Una consecuencia es que cada caracter que conforma la cadena, utiliza 2 bytes.

Una situacion que se da debido a la restriccion de inmutabilidad, es que los constructores que reciben un arreglo de caracteres (char[]) deben generar un arreglo interno nuevo y copiar el original (total o parcialmente, segun sea el caso), dependiendo del constructor que se emplee.

En cambio, cuando se genera una cadena utilizando otra cadena como base, es posible compartir el arreglo internamente, pues la fuente inmutable por definicion. En algunas situaciones, se puede generar un nuevo arreglo interno, para evitar el uso de memoria.

Cuando se necesita extraer un caracter de una cadena, es preferible utilizar la funcion charAt, en vez de un substring. Dado que esta funcion regresa un valor directamente (el iesimo caracter en el arreglo) sin necesidad de crear objetos y utilizando exclusivamente el stack, en cambio substring crea un nuevo objeto String, que puede compartir la memoria empleada por la cadena original. Una prueba de esto, es ejecutar el siguiente codigo:

        String s = "test";
        long start = System.currentTimeMillis();
        for (int i = 0; i < Integer.MAX_VALUE; i++) {
            s.substring(0,1);
        }
        System.out.println(System.currentTimeMillis() - start);

Contra este codigo

        String s = "test";
        long start = System.currentTimeMillis();
        for (int i = 0; i < Integer.MAX_VALUE; i++) {
            s.charAt(0);
        }
        System.out.println(System.currentTimeMillis() - start);

El primer codigo, puede llegar a ejecutarse en unos cuantos milisegundos (30 aprox en un Centrino Duo a 1.83GHz), mientras que el segundo codigo se tarda mas de 200 veces mas tiempo (casi 1 minuto en el mismo procesador), particularmente, cuando se ejecuta la maquina virtual con la opcion -server, cuando se ejecuta con la opcion -client, los tiempos de ejecucion del primer ejemplo es de 1/3 con respecto al segundo ejemplo. Este codigo optimizado esta empleado en el post "Extraccion del nombre de una propiedad del getter respectivo en java".

La funcion concat, es optima cuando se emplea exclusivamene con 2 cadenas, pues se aloja exactamente la memoria necesaria para construir la nueva cadena, cuya longitud es la suma de ambas longitudes.


viernes, 28 de mayo de 2010

numeros constantes y el operador + en java

Al igual que ocurre con las cadenas, el compilador, puede realizar calculos y utilizar el resultado, reemplazando una complicada operacion y minimizando el trabajo en tiempo de ejecucion. Esto resultando en menor uso de memoria y procesador. El ejemplo acontinuacion:

01: package test;
02: 
03: public class NumberConstantTest {
04:         public static void main(String[] args) {
05:                 int a = 1 // number or hours
06:                 * 60 // mins per hour
07:                 * 1000; // milliseconds per second
08:                 float b = (3.5f + 1.2f) / 2f;
09:                 double c = a * (1d / 3d);
10:                 System.out.println(a);
11:                 System.out.println(b);
12:                 System.out.println(c);
13:         }
14: }
15: 

Explicacion
05-07: Un ejemplo de como definir el numero de segundos en una hora (60000), teniendo la posibilidad de definir el numero de horas, cuando sea necesario.
08-08: Realizando una operacion con punto flotante.
09-09: Ejemplo de una combinacion de constantes, con variables.

Compiled from "NumberConstantTest.java"
   public class test.NumberConstantTest extends java.lang.Object{
   public test.NumberConstantTest();
     Code:
      0: aload_0
      1: invokespecial #8; //Method java/lang/Object."<init>":()V
      4: return

   public static void main(java.lang.String[]);
     Code:
      0: ldc #16; //int 60000
      2: istore_1
      3: ldc #17; //float 2.35f
      5: fstore_2
      6: iload_1
      7: i2d
      8: ldc2_w #18; //double 0.3333333333333333d
      11: dmul
      12: dstore_3
      13: getstatic #20; //Field java/lang/System.out:Ljava/io/PrintStream;
      16: iload_1
      17: invokevirtual #26; //Method java/io/PrintStream.println:(I)V
      20: getstatic #20; //Field java/lang/System.out:Ljava/io/PrintStream;
      23: fload_2
      24: invokevirtual #32; //Method java/io/PrintStream.println:(F)V
      27: getstatic #20; //Field java/lang/System.out:Ljava/io/PrintStream;
      30: dload_3
      31: invokevirtual #35; //Method java/io/PrintStream.println:(D)V
      34: return
   }

Explicacion
  • En codigo resultante, despues de ejecutar la decompilacion, utilizando el comando "javap -c" se puede apreciar que el compilador calculo los valores y coloco los resultados donde aplica, sin embargo, cuando se trata de combinaciones de constantes, con variables (incluso siendo constantes), no se optimizas y se realiza la operacion.

miércoles, 26 de mayo de 2010

generics en C#, implementando GetHashCode, Equals y Comparable

A continuacion, la manera de utilizar generics en C#, para implementar las funciones GetHashCode y Equals, asi como la implementacion de IComparable. Este ejemplo es equivalente al ejemplo de java


01: using System;
02: using System.Collections.Generic;
03: 
04: namespace Test
05: {
06:     public abstract class GenericClass<T>
07:     {
08:         internal T id;
09: 
10:         public T Id
11:         {
12:             get
13:             {
14:                 return id;
15:             }
16:             set
17:             {
18:                 id = value;
19:             }
20:         }
21:         public override int GetHashCode()
22:         {
23:             return id.GetHashCode();
24:         }
25:         public override bool Equals(object obj)
26:         {
27:             return this == obj
28:                 || (((object)(id as GenericClass<T>) == null)
29:                     && Equals0((GenericClass<T>)obj));
30:         }
31:         public bool Equals(GenericClass<T> obj)
32:         {
33:             return this == obj || Equals0(obj);
34:         }
35:         private bool Equals0(GenericClass<T> obj)
36:         {
37:             return id.Equals(obj.id);
38:         }
39:     }
40: 
41:     public abstract class GenericClassComparable<T> : GenericClass<T>,
42:         IComparable<GenericClassComparable<T>> where T : IComparable<T>
43:     {
44:         public int CompareTo(GenericClassComparable<T> o)
45:         {
46:             return id.CompareTo(o.id);
47:         }
48:     }
49: }
50: 

Explicacion:
06-06: Declaracion de utilizacion del uso de generics
08-08: Declaracion del id.
10-20: Declaracion de los accesors para el id.
21-24: Definicion de la funcion GetHashCode, empleando las funciones propias del atributo id.
25-38: Definicion de multiples funciones Equals.
41-42: Declaracion del uso de IComparable
44-47: Definicion de la funcion CompareTo

Optimizacion: