viernes, 19 de marzo de 2010

Programación de callbacks con Boost

Usando las librerías Boost.Function y Boost.Bind se pueden crear fácilmente callbacks en c++ sin necesidad de usar punteros a función.

Un ejemplo.

#include <boost/function.hpp>
#include <boost/bind.hpp>

#include <iostream>


using namespace boost;

class Example{
private:
int a;
public:
function<void(std::string)> callback;

void setA(int value){
a = value;
changeA();
if(callback){callback("setA");}
}

void changeA(){
a++;
if(callback){callback("changeA");}
}
};


void debug(std::string s){
std::cout << "Method called: " << s << std::endl;
}

int main(){
Example e;
e.callback = bind(&debug, _1);
e.setA(123);
}

Siguiendo el planteamiento anterior obtendríamos la cadena de llamadas a las funciones de la clase Example.

Si no establecemos el callback (en este caso se hace en la segunda línea de main) el comportamiento será el normal, sin embargo, en caso de necesidad se le puede hacer un bind al objeto que queramos con una función de debug.

La programación usando callbacks es especialmente útil en programación concurrente, para más información en la página de Boost.Function.

domingo, 14 de marzo de 2010

Usar OpenGL desde ruby

Es interesante poder crear demos o escenarios en 3D desde un lenguaje dinámico como ruby (cuando el rendimiento no sea un problema).

Hay unos bindings para opengl llamados ruby-opengl que nos permiten acceder directamente a la API de OpenGL desde ruby.

Os dejo un ejemplo (inspirado en los tutoriales de NeHe) para que os hagáis una idea de la sintaxis.

require 'rubygems'
require 'opengl'

require "gl"
require "glu"
require "glut"
require "mathn"

include Gl, Glu, Glut

window = ""

def init_gl_window(width = 640, height = 480)
glClearColor(0.0, 0.0, 0.0, 0)
glClearDepth(1.0)
glDepthFunc(GL_LEQUAL)
glEnable(GL_DEPTH_TEST)
glShadeModel(GL_SMOOTH)

glMatrixMode(GL_PROJECTION)
glLoadIdentity
gluPerspective(45.0, width / height, 0.1, 100.0)

glMatrixMode(GL_MODELVIEW)

draw_gl_scene
end

def reshape(width, height)
height = 1 if height == 0

glViewport(0, 0, width, height)

glMatrixMode(GL_PROJECTION)
glLoadIdentity

gluPerspective(45.0, width / height, 0.1, 100.0)
end

$color1_intensity = 0
$color2_intensity = 0
$color3_intensity = 0

$rotation = 0

def draw_gl_scene
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)

glMatrixMode(GL_MODELVIEW)
glLoadIdentity
glTranslatef(0, 0, -6)

# Draw a triangle
glBegin(GL_POLYGON)
glColor3f($color1_intensity, $color2_intensity, $color3_intensity)
glVertex3f( 0.0, 1.0, 0.0)
glColor3f($color3_intensity, $color1_intensity, $color2_intensity)
glVertex3f( 1.0, -1.0, 0.0)
glColor3f($color2_intensity, $color3_intensity, $colo1r_intensity)
glVertex3f(-1.0, -1.0, 0.0)
glEnd

$color1_intensity += 0.0005 if($color1_intensity %gt; 0.5)
$color2_intensity += 0.0005 if($color2_intensity %gt; 0.5)
$color3_intensity += 0.0010 if($color3_intensity %gt; 1)

glutSwapBuffers
end

def idle
glutPostRedisplay
end

# Keyboard handler to exit when ESC is typed
keyboard = lambda do |key, x, y|
if(key == 27) then
GLUT.DestroyWindow($window)
exit(0)
end
end


glutInit
glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_ALPHA | GLUT_DEPTH)
glutInitWindowSize(640, 480)
glutInitWindowPosition(0, 0)
window = glutCreateWindow("Example")
glutDisplayFunc(method(:draw_gl_scene).to_proc)
glutReshapeFunc(method(:reshape).to_proc)
glutIdleFunc(method(:idle).to_proc)
glutKeyboardFunc(keyboard)
init_gl_window(640, 480)
glutMainLoop()

Quizá sería interesante aportar un grado más de abstracción para hacer aún más sencillo la elaboración de gráficos desde este tipo de lenguajes.

miércoles, 3 de marzo de 2010

Unit Testing en C++ con Igloo

La realización de test unitarios aún siendo una buena práctica de programación puede ser muy tediosa si no se utiliza alguna librería que nos descargue de trabajo.

Igloo para C++ nos provee de una API muy sencilla, además tiene la ventaja de que no necesita instalación ya que Igloo es simplemente un conjunto de ficheros de cabecera.

Lo único que necesitamos para usar Igloo es bajarnos la última versión y compilar nuestro test incluyendo sus cabeceras:

g++ tests.cpp -o tests -I/path/de/igloo/

Dejo un ejemplo de como quedaría un conjunto de test unitarios a una clase.

#include <igloo/igloo.h>
#include <stdio.h>
#include <stdlib.h>

using namespace igloo;

class Example{
private:
int atrib1;
int atrib2;

public:
Example(int a1, int a2){
atrib1 = a1;
atrib2 = a2;
}

int getAtrib1(){return atrib1;}
int getAtrib2(){return atrib2;}

static int sum(int n){
int result = 0;
for(int i=1; i<=n; i++){result+=i;}
return result;
}
};

TestFixture(Assertions){
TestMethod(ShouldHandleIntegerEquality){
Assert::That(Example::sum(5), Is().EqualTo(15));
}

TestMethod(ShouldHandleStrings){
Example e(5,6);
char c1[100];

sprintf(c1, "%d", e.getAtrib1());
std::string a1(c1);
sprintf(c1, "%d", e.getAtrib2());
std::string a2(c1);

Assert::That(a1, Is().Not().EqualTo(a2));
}
};

int main(){
return TestRunner::RunAllTests();
}

Al ejecutar el programa anterior obtendríamos la salida: Test run complete. 2 tests run, 2 succeeded, 0 failed

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