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)

sábado, 11 de julio de 2009

Diferencias de rendimiento entre eval y send

La metaprogramación en ruby permite mucha expresividad y flexibilidad, aún así hace caer bastante el rendimiento.

A pesar de eso, lo más importante es evitar en la medida de lo posible eval ya que es bastante más lento.

Al hacer una llamada a send la máquina virtual de ruby tiene que buscar el nombre del método que se le pasa como parámetro y ejecutarlo.

En cambio, al hacer un eval además de lo anterior la máquina virtual necesita parsear la cadena y después ejecutar el código.


Benchmark.bm{|x| x.report{100000.times{eval('Post.scopes')}}}
user system total real
0.410000 0.000000 0.410000 ( 0.410306)

Benchmark.bm{|x| x.report{100000.times{Post.send('scopes')}}}
user system total real
0.140000 0.000000 0.140000 ( 0.144820)

viernes, 26 de junio de 2009

Optimización de consultas en rails

Cuando en rails se hace una consulta a la base de datos sobre una tabla que contiene mucha información, no todo el tiempo se pierde en la consulta. Una parte importante del tiempo se pierde en la creación de cada objeto ActiveRecord.

Usando directamente el método select_all de la conexión ActiveRecord devolverá un array de hash, ahorrando el tiempo de construcción de objetos.

Benchmark.bm{|x| x.report{100.times{BigTable.find(:all)}}}
user system total real
10.090000 2.090000 12.180000 ( 19.199150)

Benchmark.bmbm{|x| x.report{100.times{BigTable.connection.select_all('select * from big_table')}}}
user system total real
7.350000 2.080000 9.430000 ( 16.646406)

En general, no es buena idea guardar todos los elementos de una tabla grande en un array, pero aún seleccionando tan solo el índice se obtiene una mejora sustancial.

Benchmark.bm{|x| x.report{100.times{BigTable.find(:all, :select => :id)}}}
user system total real
0.610000 0.010000 0.620000 ( 0.707702)

Benchmark.bmbm{|x| x.report{100.times{BigTable.connection.select_all('select id from big_table')}}}
user system total real
0.080000 0.000000 0.080000 ( 0.161878)

sábado, 20 de junio de 2009

Crear extensiones de ruby en c

Crear extensiones en C del lenguaje ruby es bastante sencillo.

En primer lugar hay que crear un fichero para extconf, este nos generará el fichero makefile de forma automática.

En el fichero extconf.rb debería haber algo como:

#!/usr/bin/env ruby

require 'mkmf'
create_makefile("ruby1")

Donde ruby1 es el nombre de la extensión.

En segundo lugar hay que crear la extensión en código C, simplemente hay que incluir la cabecera ruby.h.

La función Init_ruby1 es la que se ejecutará al importar la extensión desde código ruby.

#include "ruby.h"

static VALUE c_printf(VALUE self, VALUE anObject){
printf("%s\n", STR2CSTR(anObject));
return Qnil;
}

static VALUE c_init(VALUE self){
return self;
}


VALUE FromC;

void Init_ruby1() {
rb_define_global_function("printfc", c_printf, 1);
FromC = rb_define_class("FromC", rb_cObject);
rb_define_method(FromC, "initialize", c_init, 0);
rb_define_method(FromC, "printfc", c_printf, 1);
}

En este ejemplo se define una función global printfc, se define la clase FromC, el método de instancia printfc de esa clase.

Para compilar e instalar este código hay que ejecutar ruby extconf.rb, make y make install.

Ya deberíamos tener disponible la librería ruby1, al ejecutar require 'ruby1' podremos ejecutar las funciones de nuestro código en C.

Hay una gran cantidad de funciones disponibles para crear extensiones en la siguiente dirección: http://www.rubycentral.com/book/ext_ruby.html