viernes, 19 de febrero de 2010

Smart pointers de la librería Boost

Una de las ventajas que tiene la programación en Java sobre C++ es la recolección de basura.

Cuando escribimos un programa en Java no nos tenemos que preocupar de liberar la memoria que vamos usando, hay un recolector de la máquina virtual que lo hace por nosotros. De esta forma se reduce el número de memory leaks y se facilita la programación.

En C++ sin embargo la gestión de memoria debe realizarla el desarrollador, proporcionando más potencia pero también dificultando el desarrollo.

Para facilitar la programación en este apartado, la librería Boost cuenta con un concepto llamado smart pointers que liberan de complejidad a la hora de gestionar la memoria al programador.

Por ejemplo, usando el tipo scoped_ptr.

#include <boost/smart_ptr.hpp>

class Example{
public:
int a;
};

void scoped_pointer(){
boost::scoped_ptr<Example> ptr(new Example);
}

void pointer(){
Example *ptr = new Example;
}


int main(){
while(1){
pointer();
}
}


Al ejecutar este programa se irá reservando memoria constantemente hasta agotar la memoria total del sistema, puesto que en ningún momento se libera.

Sin embargo, si se modifica la llamada pointer por scoped_pointer el programa se ejecutará de forma estable, dado que al terminar el método, el scoped pointer libera la memoria utilizada de forma automática.

Hay smart pointers con otras políticas de gestión de memoria, para elegir cual nos conviene en la documentación de boost se detalla cada uno.

domingo, 24 de enero de 2010

Profiling de peticiones en ruby on rails

Ruby es un lenguaje dinámico muy potente pero por ello también muy díficil de depurar en ocasiones. En ruby on rails con los named_scope, before_filter, plugins, gems, etc puede ser todavía peor.

Sin embargo, en los sistemas operativos que soportan DTrace tenemos una ayuda para encontrar los cuellos de botella de la aplicación, por ejemplo, usando el script que hay en: http://dl.getdropbox.com/u/478290/blog/dtrace/rb_linetime.d.

Simulo un before_filter muy lento que se ejecute para todas las request como éste:

22 def takes_too_long
23 i = 0
24 100.times{
25 100000.times{i+=1}
26 100000.times{i-=1}
27 }
28 end

Después arrancamos el servidor de ruby on rails y obtenemos su PID (vía passenger-status, ps, top...)

Ejecutamos:

sudo dtrace -s rb_linetime.d -p pid_del_servidor > request.log

Hacemos una petición a la aplicación ruby on rails, después paramos DTrace con Ctrl+C y en request.log podemos ver los ficheros ruby que se van cargando y cuanto tarda cada uno, en concreto, nos fijamos en las líneas:

FILE LINE COUNT AVG(us) SUM(us)
...

/Users/javiyu/projects/rails/prueba/vendor/rails/activesupport/lib/active_support/core_ext/module/introspection.rb 86 849 188 160330
/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/gems/1.8/gems/mongrel-1.1.4/lib/mongrel/configurator 285 83 30374 2521066
/Users/javiyu/projects/rails/prueba/app/controllers/application.rb 26 600000 16 9690835
/Users/javiyu/projects/rails/prueba/app/controllers/application.rb 25 681051 16 11087871
/Users/javiyu/projects/rails/prueba/vendor/rails/railties/lib/commands/servers/base.rb 14 69 974199 67219750
....

Vemos que application.rb tarda mucho más de lo normal, e incluso detecta el número de línea en el que está el cuello de botella, en este caso la 25 y la 26.

lunes, 11 de enero de 2010

Obtención de información relativa al procesador

Para la generación de código optimizado puede ser necesario conocer sobre que tipo de procesador se está ejecutando nuestro programa y que instrucciones soporta.

La instrucción cpuid proporciona información relativa al procesador.
Por ejemplo.

#include <stdio.h>

typedef unsigned int uint;

void print_register_str(reg){
int i;

for(i=0; i<4; i++){
printf("%c", reg >> (i*8) );
}
}

void cpuid(uint *eax, uint *ebx, uint *ecx, uint *edx, uint service){
__asm__ (
"pushl %%ebx \n\t"
"cpuid \n\t"
"movl %%ebx, %1 \n\t"
"popl %%ebx \n\t"
: "=a" (*eax),
"=S" (*ebx),
"=c" (*ecx),
"=d" (*edx)
:"a" (service)
);
}

int main(){
uint eax, ebx, ecx, edx;

printf("VendorString: ");
cpuid(&eax, &ebx, &ecx, &edx, 0);
print_register_str(ebx);
print_register_str(edx);
print_register_str(ecx);
printf("\n");


cpuid(&eax, &ebx, &ecx, &edx, 1);
printf("MMX support: %d\n", (edx >> 23) & 1);
printf("SSE support: %d\n", (edx >> 25) & 1);
printf("SSE2 support: %d\n", (edx >> 26) & 1);
printf("HyperTransport: %d\n", (edx >> 28) & 1);
printf("SSE3 support: %d\n", (ecx) & 1);

return 0;
}

Este programa tiene como resultado para mi cpu.

VendorString: GenuineIntel
MMX support: 1
SSE support: 1
SSE2 support: 1
HyperTransport: 1
SSE3 support: 1


Se puede obtener más información específica, aquí vienen algunos ejemplos más: http://softpixel.com/~cwright/programming/simd/cpuid.php.

lunes, 28 de diciembre de 2009

Implementación de malloc en glibc

malloc es una función de C que sirve para reservar n bytes consecutivos en memoria y devuelve un puntero a ese array.

¿Qué pasa cuando se agota la memoria? En teoría la función malloc debería devolver un puntero nulo y establecer la variable errno a ENOMEM.

¿Qué ocurre en realidad? En la librería de C de Linux (glibc) se usa un algoritmo de reserva de memoria optimista, no contempla el caso de haber agotado la memoria y siempre se devuelve un puntero válido. Según la manpage "uno o varios procesos pueden morir en estas situaciones llegándoles el mensaje 'out of memory'".

Hay una forma de solucionar esto, cambiar el comportamiento del algoritmo estableciendo a 2 la variable overcommit_memory con el comando:

echo 2 > /proc/sys/vm/overcommit_memory

Fuentes:
manpagez.com
linux.die.net

domingo, 1 de noviembre de 2009

Ejecución asíncrona de javascript con web workers

Tal y como anunciaba Google en la Google IO de este año han estado trabajando junto a la fundación Mozilla en la definición e implementación de los llamados web workers.

Los web workers permiten carga y ejecución asíncrona de javascript, tienen algunas limitaciones ya que no se puede acceder al DOM entre otras funciones, usa threads a nivel de sistema operativo y usa como método de sincronización paso de mensajes.

Un ejemplo de sincronización sencillo.

long_work.js

var j=1;
for(var i=0; i<999999999; i++){j=j*7+2;j=1}
this.postMessage('Done');

index.html

<script type="text/javascript">
var worker = new Worker("/javascripts/long_work.js");
worker.onmessage = function(event){
alert(event.data);
};
</script>

Ejemplo de uso en la web de la fundación Mozilla.

sábado, 15 de agosto de 2009

Como hacer copias de seguridad de tu agenda en symbian

Ya que tenemos acceso al API de symbian desde python (gracias al proyecto pys60) podemos aprovecharnos para hacer algunas cosas útiles con unos scripts simples en python.

Por ejemplo, para hacer una copia de seguridad de la agenda serviría el siguiente script.

from contacts import ContactsDb

contacts = ContactsDb().values()

filename = 'e:\\agenda.txt'
f = open(filename, "w")

for c in contacts:
f.write(c.as_vcard())

f.close()


Este programa exporta a un fichero toda la agenda en formato VCard para posteriormente transferirlo a otro dispositivo.

sábado, 18 de julio de 2009

Conectar una aplicación rails a varias bases de datos simultáneamente

Se puede conectar una aplicación rails a varias bases de datos a la vez, eso sí, hay que tener un especial cuidado con el rendimiento de la aplicación, aunque ActiveRecord nos provee una interfaz transparente a la hora de acceder a los modelos.

Suponiendo como ejemplo que tenemos dos modelos (Post y Comment) y este último está localizado en una base de datos externa, se puede configurar rails de la siguiente forma:

1) Primero, creamos un fichero de configuración para la conexión a la nueva base de datos (config/external_database.yml).

development:
adapter: mysql
database: test
username: root
password:

production:
adapter: mysql
database: test
username: root
password:

2) Después, creamos un módulo para encapsular la lectura del fichero y la nueva conexión.

module ExternalDatabase
def self.included(base)
config = YAML.load(File.open('config/external_database.yml'))

if config and ENV['RAILS_ENV']
base.establish_connection(config[ENV['RAILS_ENV']])
else
raise 'External Database not configured'
end
end
end

3) Por último, incluimos en los modelos que van a la base de datos externa el módulo que hemos creado.

class Comment < ActiveRecord::Base
include ExternalDatabase

belongs_to :post
end

Con esto ya podemos utilizar los modelos aunque cada uno esté en una base de datos diferente.

Comment.find(:all, :include => :post)