May 01, 2006

Notas de desempeño, 2

Después de haber hecho lo antes comentado y cambiar las clases por estructuras he notado que (sin la menor duda) se acelara el desempeño, he bajado la respuesta de 6~8 segundos a menos de un segundo, lo cual es algo grandioso, procesar más de 21000 estructuras de aproxidamente 1500 bytes cada una es fantástico, el consumo de CPU es de 1%-3% (hablando de un P4 2.4Ghz), claro que este caso de prueba es "el peor de los casos" y también, aún, hay detalles que podrían ser mejorados, los resultados son excelentes. Durante la recepción utilizo la solución del marshalling+cast, de modo que se hace un cast explícito al tipo de estructura, por ejemplo, suponiendo que tenemos una estructura definida como:

[StructLayout (LayoutKind.Explicit)]
public struct MyStruct
{
[FieldOffset (0)]
public int Integer;

[FieldOffset (4)]
public short Short;
}

Podríamos dentro de alguna parte de nuestra lógica de procesamiento del flujo binario, hacer el cast:

fixed (byte *data = reader.ReadBytes (sizeof (MyStruct))) {
header = *(MyStruct *) data;
}

Donde el flujo lo leemos a través de la variable reader (BinaryReader). Lo más probable es que este proceso se encuentre dentro de un ciclo infinito que se encuentra recibiendo la información del flujo de red. Después de unas pruebas, que hice por simple curiosidad, note algo muy interesante de las formas de obtener el tamaño de una definición de estructura y clase, lo más sorprendente fue que el método propuesto por "Marshal.SizeOf (typeof (MyStruct))", es más lento, y sin duda es cierto, pues cada ocasión que es llamado se realiza un asignación por la CLR para calcular su tamaño.

repetitions: 1000
iterations: 1.000000e+006

TestSizeOfStatic :      7.773 seconds
TestSizeOf:     2.781 seconds
TestSizeOfMarshal :     50.093 seconds

Debido a que sizeof únicamente sirve para tipos por valor, no es posible aplicar este método a una clase y tendríamos que utilizar Marshal.SizeOf para obtener este tamaño, una solución para no afectar el desempeño y no utilizar siempre la opción no recomendada es agregando una propiedad estática a nuestra clase además de una variable privada estática que mantenga este tamaño, algo como:

[StructLayout (LayoutKind.Sequential)]
public class MyClass
{
public int Integer;
public short Short;

static unsafe MyClass ()
{
_size = Marshal.SizeOf (typeof (MyClass));
}

public static int Size
{
get { return _size; }
}

private static int _size;
}

Además hay que recordar que el overhead de utilizar una clase en comparación a una estructura es mayor. Hay detalles en las clases, al igual que en las estructuras, que deben ser consideradas, el sitio de mono tiene algunos buenos consejos para esto.