[OPINION] Terminando 2011 y preparado para 2012

Hola a todos!

2012-happy-new-year-wallpaper-2

Se acaba 2011, este será el último post de este año, porque los dos días que quedan será complicado que pueda sentarme a escribir, entre trabajo en la oficina y trabajo en casa no voy a parar!.

wpmango

2011 ha sido un gran año. He podido participar en muchos eventos de la comunidad:

En total, he estado rodando por Madrid, Barcelona, Santander, Málaga, Bilbao, Tenerife… espero que el año 2012 me permita conocer sitios nuevos como Andorra, A Coruña, La Rioja y muchos más. Ha sido un placer poder participar en todos estos eventos y conocer a mucha gente con la paciencia suficiente como para sentarse a aguantar mi chapa sobre lo que mola Windows Phone y Silverlight. Muchas gracias a todos por escucharme y por preguntarme y colaborar.

También ha sido un placer poder escribir mi blog en Geeks.ms, 20 artículos este año, con unas 24000 visitas, para mi es todo un logro y me siento honrado por vuestras visitas. Espero que el 2012 me permita escribir más y mejor sobre Windows Phone Mango, Tango o Apollo.

portada_grande

El otro gran evento de este año ha sido mi libro sobre Windows Phone 7.5. Algo que empezó como la idea de escribir un pequeño libro, de no más de 100 o 150 páginas, cuyos 3 primeros capítulos podéis encontrar aquí, aquí y aquí creció hasta las más de 400 páginas, con la gran ayuda de mi compi en Plain Concepts y MVP de Azure Ibon Landa que colaboró con la mejor parte de libro, un capitulo increíble sobre Windows Azure en Windows Phone. Tengo que agradecer mucho a José Manuel Alarcón su apoyo y que se encargase de publicar el libro a sabiendas de que era más una cuestión de fe que de beneficios. También a la gente de DPE de Microsoft que han comprado un montón de libros y han realizado mucha publicidad en los roadshows. Por último y no menos importante, a todos aquellos que habéis comprado el libro, muchas gracias por confiar en mi para echaros una mano en el desarrollo para Windows Phone. En cuanto a todos los que lo habéis pirateado… gracias también, total, solo le estáis robando el pan a un humilde padre de 8 hijos… jejejeje. Si te apetece regalarlo a alguien en reyes, mira aquí

PlainConceptsTransparentLogo

Otra de las grandes suertes de este año ha sido compartirlo con la gente de Plain Concepts donde todos los días puedes aprender algo nuevo, (levantamiento de vidrio sincronizado, testeo unitario de tortilla, patrón devorador de champis…) No hay nada comparable a trabajar con buenos compañeros, buenas personas y buenos profesionales. A todos y cada uno, muchas gracias por enseñarme tanto y dejarme compartir tantos buenos momentos con vosotros!!

And… what’s next??

Espero que 2012 sea al menos tan buen año como este. Mi resumen de deseos para 2012:

  • En cuanto pueda poner las zarpas en los nuevos SDKs que salgan de Windows Phone amenazo con escribir un nuevo libro sobre Tango o Apollo, depende de las novedades de cada uno.
  • Participar en más eventos de la comunidad, seguir ayudando en los foros de MSDN y seguir visitando nuevas tierras llevando la palabra del mango a los infieles y a los herejes
  • Escribir más y mejor, sobre Windows Phone, Expression Blend, Silverlight y todas estas cosillas que me gustan
  • Seguir aprendiendo muchas cosas de todos mis compis en Plain Concepts, de los que tengo muuuuucho que aprender todavía!!

 

Y esto es todo! Se acabo el año en este blog. Feliz Año 2012 a todos, nos vemos a partir del 1 de Enero!!

Happy Coding!

[Windows Phone 7.5] Dale calidad a tus aplicaciones con Expression Blend (I)

Hola a todos!

Hoy vamos a conocer un poco mejor a un gran desconocido del desarrollo en Windows Phone 7.5: Expression Blend.

Microsoft-Expression-Blend-41

No es necesario ser un experto diseñador para, usando Expression Blend, dar a nuestras aplicaciones un toque de calidad extra, una apariencia única y mejorar globalmente el acabado de nuestro producto.

Una de las grandes ventajas de Expression Blend es que es capaz de trabajar con el mismo proyecto que usamos en Visual Studio, por lo que podremos crear nuestro proyecto en Expression Blend y luego programar en Visual Studio o crearlo en Visual Studio y luego “estilizarlo” en Expression Blend. También tenemos que tener en cuenta que con el SDK de Windows Phone obtenemos Expression Blend 4 totalmente gratis, no desperdiciemos este regalo.

Una vez que tenemos nuestro proyecto creado, ya sea desde Blend o Visual Studio, se nos presentará la vista por defecto:

image

La pantalla principal de Blend se divide en varias secciones. En la parte izquierda superior encontraremos las pestañas de proyecto, activos, estados y partes. En la izquierda inferior los objetos y linea de tiempo. En la parte derecha encontraremos las propiedades del objeto seleccionado, los recursos de nuestra aplicación y una pestaña de datos. Por último, en la parte central tenemos la vista de diseño de nuestra página.

Si seleccionamos la pestaña de Assets (Activos) en la parte superior izquierda encontraremos todo lo necesario para diseñar nuestra aplicación:

image

Además de tener una lista completa de controles, tenemos otras opciones como formas, estilos, comportamientos o proyecto. Una parte muy interesante de esta pestaña es la caja de búsqueda en la parte superior. Simplemente escribiendo el nombre de lo que deseamos buscar nos aparecerá en los resultados, por ejemplo escribiendo but:

image

Obtenemos los resultados de todos los elementos que coincidan con lo escrito, solo tenemos que seleccionar uno y arrastrarlo a nuestra página para añadirlo. Vamos a añadir 3 botones a nuestra página y ha colocarlos para que ocupen todo el ancho:

image

En muchas aplicaciones, aquí acabaría el diseño de la página principal. En nuestro caso vamos a darle algo más de estilo a los botones, para hacerlos totalmente diferentes a lo que se puede ver en otras aplicaciones.

Para empezar, vamos a presionar con el botón derecho sobre uno de los botones, da igual cual, y seleccionar del menú la opción “Edit Template” (Editar Plantilla) y “Edit a Copy” (Editar una copia). Al seleccionar la opción “Edit a Copy” Blend nos preguntará donde queremos crear la nueva plantilla y que nombre queremos asignarle:

image

En nuestro caso vamos a llamar a la plantilla “BrandNewButton” y la vamos a definir en la aplicación, pues deseamos que este nuevo estilo esté disponible para cualquier página de nuestra aplicación. Al presionar OK la pantalla cambiará para mostrarnos solo el botón y en la parte izquierda inferior, la pestaña de objetos mostrará los objetos que componen un botón standard:

image

Lo primero que vamos a hacer es seleccionar la Grid y las propiedades, pinchando sobre la pestaña “Properties” (Propiedades) que tenemos a la derecha de la pantalla, lo que nos mostrará todas las propiedades de la Grid:

image

Lo primero que encontramos en las propiedades es el editor de colores. Por defecto la Grid tiene un Background transparente. Tenemos 4 opciones posibles:

image

De izquierda a derecha: Sin background, color plano, degradado de color, imagen y por último usando un recurso. En nuestro caso vamos a seleccionar la tercera opción: degradado de color y vamos a formar el siguiente degradado:

image

Vaya, pero queremos que el degradado sea de izquierda a derecha, no de arriba a abajo. Para ello, en los iconos que tenemos a la izquierda de la pantalla seleccionamos uno que parece una flecha, la herramienta de degradado, lo cual mostrará una flecha en nuestro elemento, indicando la dirección que tiene el degradado:

image

Podemos modificar esta flecha para que el inicio esté en la izquierda y el final en la derecha, simplemente arrastrando el final y la punta de la flecha:

image

Ahora, vamos a darle una ligera inclinación al botón para que no sea totalmente recto. Para esto extendemos las propiedades “Transform” (Transformación) de la grid:

image

En este menú tenemos dos opciones: RenderTransform y Projection. RenderTransform aplica transformaciones al objeto seleccionado. Projection realiza proyecciones (Rotación, traslación) sobre el objeto. En nuestro caso usaremos la opción RenderTransform. Esta nos permite una variedad de transformaciones, de izquierda a derecha: Traslación, Rotación, Escalado, Sesgado, Centrado y Volteado. Vamos a usar dos: Rotación, donde le aplicaremos una rotación de –14º y sesgado, donde le aplicaremos un sesgado en el eje X de –15.

Con estos cambios, nuestro botón ahora debería tener el siguiente aspecto:

image

Si guardamos y volvemos a la página principal (Seleccionando MainPage.xaml en las pestañas encima de la vista de diseño) podremos ver el aspecto de nuestros botones:

image

Para aplicar este diseño al resto de botones, pinchamos sobre cada uno de ellos con el botón derecho, seleccionamos “Edit Template” (Editar Plantilla) y en el submenú escogemos “Apply Resource” donde nos aparecerá el nombre de nuestro estilo. Al seleccionarlo se aplicará automáticamente:

image

Ahora, esas líneas blancas y el texto centrado no ayudan mucho a la apariencia del botón, vamos a arreglarlo, simplemente vamos a la pestaña de recursos que tenemos en la parte derecha superior y al extender App.xaml veremos nuestro botón, haciendo doble click lo abriremos para editarlo. Si miramos la pestaña de objetos, veremos la siguiente jerarquía:

image

Hemos trabajado con la Grid. Dentro de ella encontramos el ButtonBackground, un elemento de tipo Border que es el que muestra el borde blanco, así mismo es el que se establece totalmente en blanco cuando presionamos el botón. Dentro de este encontramos un ContentControl llamado ContentContainer que es el encargado de mostrar el texto del botón. Vamos a seleccionar el objeto ContentContainer y lo vamos a arrastrar hasta la Grid, de forma que lo saquemos del Border y a continuación vamos a eliminar el Border ButtonBackground.

Ahora vamos a ver las propiedades del ContentContainer, para ello lo seleccionamos y lo primero que veremos es que la propiedad foreground tiene un punto amarillo a su derecha:

image

Este recuadro amarillo nos indica que esa propiedad está recibiendo su valor desde el objeto que use la plantilla. Por ejemplo, si en nuestro botón establecemos el foreground a Rojo, esta propiedad sería roja. Como queremos hacer un estilo que muestre lo que nosotros queremos y no tener que ir modificando cada botón por separado, podemos establecerlo pinchando sobre el punto amarillo y en el menú que aparece seleccionando «Reset” (Resetear) lo cual eliminará el enlace a datos y ya podremos cambiar el color a nuestro gusto. En este ejemplo he puesto como color el negro.

Lo siguiente que vamos a modificar es la alineación de este ContentContainer. Vamos a dejar el texto centrado verticalmente, pero lo vamos a posicionar a la derecha:

image

Con esto, si guardamos los cambios y ejecutamos la aplicación, el aspecto será el siguiente:

image

De esta forma, en muy pocos pasos y con muy poco trabajo hemos cambiado sustancialmente el aspecto de nuestra aplicación, veamos un antes y después, que en la TV siempre quedan bien:

image

En el próximo artículo veremos como poder añadir animaciones a nuestros estilos y plantillas para que reaccionen a las acciones del usuario. Pero eso será el año que viene jeje. Por ahora para practicar os dejo el proyecto de ejemplo con el estilo que hemos creado, podéis descargarlo aquí.

Un saludo y Happy Coding!

[MATERIALES] Windows Phone 7.5 en AppCircus Academy Madrid

Hola a todos!

AppCircus Academy en Madrid Nokia Microsoft

Este martes 13 de diciembre impartí un workshop de Windows Phone 7.5 en el AppCircus Academy de Madrid.

image

Pudimos ver las principales novedades de la plataforma, el funcionamiento del FAS, lanzadores y selectores, sensores y más cosas interesantes.

Aquí os dejo todos los materiales del mismo (pptx y demos) para quien quiera repasar lo que vimos y empezar a desarrollar para Windows Phone 7.5

Disfrutadlo. Un saludo y Happy Coding!

[Windows Phone 7.5] Inserciones masivas en SQL Server CE vs SQL Server vs MongoDb (y II)

Vuelvo a la carga! Después de una retirada para curar nuestras heridas, vamos a continuar la batalla, tengo sorpresas buenas y no tan buenas que iremos viendo a lo largo del artículo. Si no has visto la primera parte y no sabes de que estoy hablando… no me he enrolado ni he perdido definitivamente la cabeza, echa un vistazo aquí.

Missile Three

Vamos a recordar cuales fueron los últimos resultados de nuestro Missile Two:

image_thumb20

Nos quedamos con unos tiempos en el emulador de 3 minutos y 48 segundos, el HTC Mazaa llegó a los 28 minutos y 33 segundos y el Nokia Lumia 800 a los 26 minutos y 21 segundos. Lo que vamos a intentar para optimizar los resultados es ejecutar nuestro código en un hilo diferente al de la interface de usuario, para de esta forma no perjudicar nuestro rendimiento y porque en una aplicación real sería lo que tendríamos que hacer. Simplemente usamos la clase ThreadPool para ejecutar nuestras inserciones en un nuevo hilo:

ThreadPool.QueueUserWorkItem((Object) =>
{
    using (DbContext context = new DbContext("Data Source='isostore:/Db.sdf'; Max Database Size = 128; Max Buffer Size = 4096;"))
    {

        for (int i = 0; i < 500000; i++)
        {
            TestTable insert = new TestTable();
            insert.Id = Guid.NewGuid();
            insert.Payload = load;
            context.Test.InsertOnSubmit(insert);
        }

        context.SubmitChanges();
        watch.Stop();
        this.Dispatcher.BeginInvoke(() => { TotalTime.Text = watch.Elapsed.TotalMinutes.ToString(); });
    }
});

Por sí mismo, este simple cambio ya nos ofrece una ganancia en el tiempo de ejecución:

image

El emulador ha descendido hasta los 3 minutos y 30 segundos, 18 segundos de mejora, mientras que los dispositivos han bajado hasta los 25 minutos y 9 segundos el HTC (más de 3 minutos más rápido) y hasta los 22 minutos y 48 segundos el Nokia Lumia 800 (también otros más de 3 minutos de ganancia). Está claro que el hilo de ejecución principal estaba consumiendo tiempo en otros menesteres y restándolo a nuestro proceso. ¿Podemos mejorarlo más?

Nuclear Strike

Vamos a probar otra cosa distinta partiendo de la base de ejecutarnos en un hilo propio:

Hasta ahora, mi clase TestTable tenía una propiedad de versionado, que mejora el rendimiento a la hora de consultar y actualizar registros:

[Table(Name = "Test")]
public class TestTable
{
    [Column(IsPrimaryKey = true)]
    public Guid Id { get; set; }

    [Column()]
    public string Payload { get; set; }

    [Column(IsVersion = true)]
    public Binary version { get; set; }
}

Pero, ¿Que efecto tiene sobre las inserciones? Pues estamos insertando más datos además de nuestra cadena y nuestro GUID. Además de mejorar el rendimiento de actualización, ¿afectará positiva o negativamente al rendimiento de las inserciones? Pues…

image

Si, si que influye y mucho! en el caso del Nokia Lumia 800, quitando la columna de versión hemos descendido hasta los 17 minutos y 42 segundos… ¡5 minutos más rápido! también hemos mejorado tanto en el emulador como en el HTC Mazaa aunque de forma más comedida.

Y ya solo nos queda otra pequeña cosa por probar… Hasta ahora estamos usando el método InsertOnSubmit para insertar cada elemento en nuestro contexto a la espera de llamar al método SubmitChanges. Existe otro método, llamado InsertAllOnSubmit que nos permite pasarle una colección de objetos a insertar, ¿Mejorará el rendimiento? Vamos a ver como usarlo:

string load = "XXXXXXXXXXXXXXXXXXXX";

Stopwatch watch = Stopwatch.StartNew();
List<TestTable> collection = new List<TestTable>();
ThreadPool.QueueUserWorkItem((Object) =>
{
    using (DbContext context = new DbContext("Data Source='isostore:/Db.sdf'; Max Database Size = 128; Max Buffer Size = 4096;"))
    {

        for (int i = 0; i < 500000; i++)
        {
            TestTable insert = new TestTable();
            insert.Id = Guid.NewGuid();
            insert.Payload = load;
            collection.Add(insert);
        }
        context.Test.InsertAllOnSubmit(collection);
        context.SubmitChanges();
        watch.Stop();
        this.Dispatcher.BeginInvoke(() => { TotalTime.Text = watch.Elapsed.TotalMinutes.ToString(); });
    }
});

Simplemente debemos ir guardando nuestras instancias en una colección y pasarlas todas juntas al InsertAllOnSubmit, ¿El resultado?

image

Pues efectivamente, al usar InsertAllOnSubmit mejoramos los tiempos, más de 1 minuto y 30 segundos en el Nokia Lumia 800 y unos 3 minutos en el caso del HTC Mazaa, el emulador solo mejora en 18 segundos.

¿Como ha mejorado la situación de forma global desde que empezamos con nuestras primeras inserciones? He preparado un grafico profesional, profesional para que no digáis que no me lo trabajo:

image

Como podemos ver, hemos mejorado notablemente el tiempo en todos los dispositivos, en el caso del Nokia Lumia 800 hemos pasado de un tiempo inicial de 29 minutos y 20 segundos a un tiempo final de 16 minutos y 4 segundos. El HTC Mazaa ha pasado de 37 minutos a 20 minutos y 1 segundo. El emulador no ha sufrido una mejoría menos aguda, pasando de 4 minutos y 36 segundos iniciales a 2 minutos 30 segundos. Es sorprendente, en todos los dispositivos hemos logrado de media un 45% de mejora!!

Lecciones aprendidas

Al igual que en la primera parte, este artículo ha servido como un simple conducto por el cual explorar un poco más a fondo SQL Server CE, es hora de ver que hemos aprendido:

  • Siempre, siempre, siempre: USA UN HILO SECUNDARIO para tus procesos, el usuario te lo agradecerá y tu aplicación también, ya no es solo una cuestión de que la interface de usuario responda, hemos dejado claro que además te liberará de los trabajos que está haciendo la UI y hará que tus procesos sean más rápidos.
  • Aunque la columna de versión nos va a mejorar el rendimiento en las recuperaciones de datos o en las actualizaciones, ten cuidado, penaliza el rendimiento de las inserciones. Puede que en pocos registros no se note, pero si realizas inserciones masivas, perderás rendimiento.
  • Además de InsertOnSubmit, disponemos de InsertAllOnSubmit que nos ofrece mayor rendimiento a la hora de insertar en la base de datos múltiples registros en una sola operación.

Conclusión

The Winter is coming… bueno, bastante por hoy, con una reducción del 45% creo que hemos conseguido optimizar muy bien nuestro código de inserción… ¿Se puede hacer más? Siempre se puede hacer más, pero eso, es otra historia.

Dejo por aquí el código de mi Nuclear Strike para que lo tengáis como referencia.

Un saludo, Happy Coding y Feliz Navidad!

[Windows Phone 7.5] Personalizando la System Tray.

En Windows Phone 7.0 la system tray solo nos permitía especificar su visibilidad, esto es, si queríamos mostrar o no las indicaciones de batería, hora, cobertura, etc… personalmente siempre me ha gustado la opción de poder ver esta información, sobretodo si voy a estar mucho tiempo usando una aplicación.

image

El mayor problema aparecía en el hecho de que el background / foreground era el dictado por el sistema y no existía forma de personalizarlo, como podemos ver en la imágen anterior, donde parece que nuestra aplicación ha “perdido” un poco de pantalla. ¿Como aprovechar ese espacio mientras ofrecemos a nuestros usuarios la información standard del dispositivo? En Windows Phone 7.5 ya podemos hacerlo pues tenemos acceso al objeto SystemTray, que podemos encontrar en el namespace Microsoft.Phone.Shell. En este objeto disponemos de varias propiedades, entre ellas, el color de fondo y de letra a usar. Podemos establecerlo directamente en XAML:

shell:SystemTray.IsVisible="True"
shell:SystemTray.BackgroundColor="#FFAAFFAA"
shell:SystemTray.ForegroundColor="DarkGreen"

image

O también podemos realizarlo mediante código, por ejemplo al pulsar sobre el botón cambiar color:

private void Button_Click(object sender, RoutedEventArgs e)
{
    SystemTray.BackgroundColor = Color.FromArgb(255, 160, 255, 160);
    SystemTray.ForegroundColor = Colors.Green;
}

De esta forma hemos podido unificar el aspecto de la system tray con nuestra aplicación, evitando la sensación de perdida de espacio. Además de integrar la system tray visualmente en nuestra aplicación, también podemos darle un uso real, por ejemplo, usando este espacio para mostrar nuestros indicadores de progreso en operaciones largas, en vez de incluirlos en otras partes de la pantalla. Para esto disponemos de la propiedad ProgressIndicator:

private void btnProgress_Click(object sender, RoutedEventArgs e)
{
    SystemTray.ProgressIndicator = new ProgressIndicator() {  IsIndeterminate = true, IsVisible = true };
}

image

De esta forma podemos mostrar nuestras barras de carga directamente en el system tray, al igual que se hace en otras partes del sistema y aprovechando este espacio, antes sin uso.

[TIP] Degradados

Antes de terminar, os dejo un pequeño truco. Si os fijáis en las pantallas de ejemplo, estoy usando un degradado entre dos colores y se muestra totalmente fluido. Originalmente existían problemas con estos fondos, pues Windows Phone solo usaba 16bits para dibujarlos y aparecían unas líneas de degradado horribles. En Windows Phone 7.5 podemos cambiar el comportamiento de nuestra aplicación para que solicite el uso de 32bits por pixel de color. Para ello debemos editar el archivo WMAppManifest.xml y añadir la opción BitsPerPixel establecida a 32:

<App xmlns="" BitsPerPixel="32" ProductID="{79042c1e-48c6-40f5-86a2-0555eed38582}" 

De esta forma disfrutaremos de 32bits de color en nuestra aplicación.

Para terminar os dejo el código fuente de ejemplo para que lo descarguéis

Un saludo y Happy Coding!

[Windows Phone 7.5] Inserciones masivas en SQL Server CE vs SQL Server vs MongoDb

Hola a todos!

Desde hace unas semanas, mis compañeros Pablo Doval y Unai Zorrilla están inmersos en una lucha por ver quien la tiene mas grande por ver quien consigue insertar 500.000 registros en menos tiempo. Pablo está usando SQL Server y Unai MongoDb podéis ver sus batallas aquí:

En sus últimos artículos ya podemos ver como estos auténticos genios han conseguido llegar a números impresionantes (1,6 segundos Unai, 1,8 segundos Pablo).  Estos post, con el trasfondo de la batalla entre dos compis, son un compendio de optimizaciones y buenas maneras a la hora de trabajar tanto con SQL Server como MongoDb y deberían ser un “Must read” para todo el que desee saber algo más sobre estas plataformas.

Yo que soy muy atrevido y me gusta vivir al límite he decidido unirme al campo de batalla de forma unilateral y armado con mi SQL Server CE incluido en Windows Phone 7.5. Si, lo reconozco, esto es como ir a una guerra entre dos superpotencias armado con una pistola de fogeo… pero quien dijo miedo!!

DISCLAIMER: Antes de empezar: soy muy consciente de que es imposible llegar a los números de los que Unai y Pablo hacen gala, además de por sus conocimientos en las respectivas plataformas, por el ambiente en si mismo. Nunca un dispositivo con un solo core, de bajo consumo y con muy poca memoria y SQL Server CE se podrá comparar a esos dos monstruos que son MongoDb y SQL Server, dos de los mejores sistemas de almacenamiento de datos que existen en la actualidad. Pero si creo que por el camino podemos aprender muchas cosas sobre como optimizar la plataforma de bases de datos en Windows Phone y descubrir detalles, cuanto menos, interesantes.

Missile One

Para empezar, vamos a obtener una referencia desde la que podamos empezar a mejorar, quien sabe, incluso sin ningún tipo de mejora llegamos a los tiempos de Unai y Pablo y podemos echarnos a dormir.

Si no habéis visto como se trabaja con SQL Server CE en Windows Phone, podéis echar un vistazo a este artículo de mi blog.

Para empezar, creamos un proyecto standard de Silverlight para Windows Phone y le  añadimos una referencia al ensamblado System.Data.Linq. A Continuación vamos a crear nuestra entidad:

[Table(Name="Test")]
public class TestTable
{
    [Column(IsPrimaryKey=true)]
    public Guid Id { get; set; }

    [Column()]
    public string Payload { get; set; }
}

Como podéis ver, es imposible hacerla más simple. Un campo GUID y un campo string donde insertaremos 20 caracteres por cada registro. Ahora vamos a crear nuestro contexto de datos:

public class DbContext : DataContext
{
    public DbContext(string connectionString) : base(connectionString) { }

    public Table<TestTable> Test
    {
        get
        {
            return this.GetTable<TestTable>();
        }
    }
}

Igual que nuestra tabla, lo más sencillo que pueda realizar el trabajo, sin trampa ni cartón ni esotéricas optimizaciones. Para ejecutar todo vamos a crear la UX de nuestra aplicación, simplemente necesitamos un botón para iniciar el proceso de inserción y un bloque de texto donde mostrar cuanto hemos tardado (la cifra vendrá expresada en milisegundos):

<StackPanel>
    <Button Content="Start" Click="Button_Click"></Button>
    <TextBlock FontSize="{StaticResource PhoneFontSizeLarge}"
                Foreground="{StaticResource PhoneAccentBrush}"
                Text="Total Time:"
                Name="TotalTime"></TextBlock>
</StackPanel>

Si, la respuesta es SI: Hoy me siento minimalista. Una UX realmente sencilla:

image

Y Por último el código que se encargue de crear la base de datos y ejecutar las inserciones al presionar el botón “Start”:

// Constructor
public MainPage()
{
    InitializeComponent();
    
    using (DbContext context = new DbContext("Data Source='isostore:/Db.sdf'"))
    {
        if (context.DatabaseExists())
            context.DeleteDatabase();
        if (!context.DatabaseExists())
            context.CreateDatabase();
    }
}

private void Button_Click(object sender, RoutedEventArgs e)
{
    
    string load = "XXXXXXXXXXXXXXXXXXXX";

    Stopwatch watch = Stopwatch.StartNew();
    using (DbContext context = new DbContext("Data Source='isostore:/Db.sdf'"))
    {
        for (int i = 0; i < 500000; i++)
        {
            TestTable insert = new TestTable();
            insert.Id = Guid.NewGuid();
            insert.Payload = load;
            context.Test.InsertOnSubmit(insert);
        }

        context.SubmitChanges();
    }
    watch.Stop();

    TotalTime.Text = watch.Elapsed.TotalMinutes.ToString();
}

Como podemos ver no tiene mayor secreto. En el constructor de nuestra página comprobamos si la base de datos existe para borrarla y a continuación crearla (de esta forma tenemos un entorno limpio solo con iniciar la aplicación). En el botón, establecemos la carga a insertar, 20 caracteres, conectamos con la base de datos e insertamos mediante un bucle 500000 registros haciendo un SubmitChanges posteriormente que enviará las inserciones a la base de datos. Usamos la clase StopWatch de System.Diagnostics para medir el tiempo que tardamos en realizar el trabajo y mostrar el resultado al usuario.

¿Cual es el resultado? Tensión… redoble de tambores…:

image

FAIL!!! Después de unos agónicos instantes Obtenemos unas excepción de SQL Server CE indicándonos que hemos excedido el tamaño máximo por defecto de la base de datos… no solo no hemos ganado la batalla, es que nos hemos resbalado en la bañera y hemos muerto antes de salir de casa… vamos a arreglar esto, usando las opciones de la cadena de conexión de SQL Server CE. Aunque es una opción poco documentada y algo oscura, SQL Server CE soporta parámetros adicionales en su cadena de conexión en Windows Phone 7.5 y uno de estos parámetros es Max Database Size. Si no conocemos el tamaño posible de la base de datos, podemos establecerlo desde 16MB hasta 512MB. Para este primer ejemplo y pensando en que nos sirva de referencia, lo estableceremos a 128MB:

// Constructor
public MainPage()
{
    InitializeComponent();

    using (DbContext context = new DbContext("Data Source='isostore:/Db.sdf'; Max Database Size = 128;"))
    {
        if (context.DatabaseExists())
            context.DeleteDatabase();
        if (!context.DatabaseExists())
            context.CreateDatabase();
    }
}

private void Button_Click(object sender, RoutedEventArgs e)
{
    
    string load = "XXXXXXXXXXXXXXXXXXXX";

    Stopwatch watch = Stopwatch.StartNew();
    using (DbContext context = new DbContext("Data Source='isostore:/Db.sdf'; Max Database Size = 128;"))
    {
        for (int i = 0; i < 500000; i++)
        {
            TestTable insert = new TestTable();
            insert.Id = Guid.NewGuid();
            insert.Payload = load;
            context.Test.InsertOnSubmit(insert);
        }

        context.SubmitChanges();
    }
    watch.Stop();

    TotalTime.Text = watch.Elapsed.TotalMinutes.ToString();
}

Volvemos a ejecutar… redoble de tambores…

image

Esta vez SÍ! 4,6 minutos (4 minutos y 36 segundos aprox.) en insertar 500000 registros mediante el emulador… bueno, estamos algo lejos de la marca lograda por Pablo y Unai. Aun así, ¿Hasta que punto es el emulador un reflejo fiel del comportamiento del teléfono? Para descubrirlo vamos a realizar esta prueba en los dos Windows Phone 7.5 que tengo en casa: HTC Mazaa y Nokia Lumia 800. A ver que resultados obtenemos.

Para ejecutarse en dispositivos reales, vamos a indicar en el constructor de nuestra clase App que la aplicación no se desactive aunque el usuario no esté interactuando con ella (que no se apague la pantalla del dispositivo). Para esto usaremos la propiedad UserIdleDetectionMode de la clase PhoneApplicationService:

public App()
{
    // Global handler for uncaught exceptions. 
    UnhandledException += Application_UnhandledException;

    // Standard Silverlight initialization
    InitializeComponent();

    // Phone-specific initialization
    InitializePhoneApplication();

    PhoneApplicationService.Current.UserIdleDetectionMode = IdleDetectionMode.Disabled;
}

Y con esto ya estamos listos para ejecutar la aplicación en los dispositivos, la desplegamos y la iniciamos y tras unos tensos instantes, obtenemos los siguientes resultados (gráfica profesional y chula incluida!!):

image

Mientras que el emulador realizó la operación completa en 4 minutos y 36 segundos, en los dispositivos físicos el tiempo se dispara: El HTC Mazaa con procesador de 1Ghz completo la inserción en 37 minutos y el Nokia Lumia 800 con el procesador más potente de 1.4Ghz lo hizo en 29 minutos y 20 segundos. Bueno… andamos algo lejos de los resultados de Unai y Pablo, pero nadie dijo que fuese a ser fácil jeje. Hora de empezar a optimizar!!

Missile Two

Cogiendo la aplicación donde la hemos dejado, necesitamos identificar donde estamos invirtiendo el tiempo para optimizar las operaciones más costosas. En primer lugar, ¿Cual será el resultado al insertar solamente 20.000 registros? vamos a verlo:

image

El emulador tarda unos 0,08 minutos en insertar los 20000 registros, unos 5 segundos. ¿Y los dispositivos?

image

 

En este caso el Nokia Lumia ha tardado 0,42 minutos, unos 25 segundos, mientras que el HTC Mazaa ha tardado 0,32 minutos, unos 19 segundos. Siguen siendo mucho más lentos que el emulador y además en este caso el HTC ha sido varios segundos más rápido que el Nokia… subamos la apuesta… en un mundo ideal, si duplicamos los registros, deberíamos duplicar el tiempo… vamos a ver que pasa con 40.000 registros:

image

En este caso, mientras el emulador ha duplicado su tiempo, el HTC ha multiplicado su tiempo por 2,5 por lo que ya empezamos a ser más lentos insertando 40.000 que haciendo dos inserciones de 20.000. Por su parte el Nokia ha multiplicado su tiempo de ejecución por 1,4 por lo que en su caso si que nos es más rentable insertar 40.000 que 2 veces 20.000. Viendo estos resultados lo primero que se me ocurre es que podemos estar teniendo un problema de buffer al escribir en la base de datos… vamos a usar otro parámetro de la cadena de conexión “Max Buffer Size” para forzar el buffer a 1024Kb y repetimos la prueba con 40.000 registros:

image

Bien! hemos conseguido mejorar el rendimiento en todos los dispositivos y en el emulador. El más beneficiado es el HTC, que ha bajado 7 segundos, mientras que el Nokia y el emulador solo han mejorado en 1 segundo. El tamaño máximo de buffer que podemos usar en SQL Server CE para Windows Phone 7.5 es de 4096Kb, vamos a hacer una última prueba parcial con el buffer más grande y 40.000 registros, ¿Que pasará? Pues algo sorprendente, los tiempos no solo no mejoran, empeoran!! ¡Por Helm! ¿Porque pasa esto? Pues porque estamos reservando más memoria de la necesaria, y el tiempo de esa operación está acabando con nosotros… Vamos a lanzar ya nuestro segundo misil: Vamos a lanzar 5 inserciones de 100.000 registros, con el buffer establecido a 3072Kb:

En un mundo perfecto, si 40.000 registros con un buffer de 1024Kb tardan unos 40 segundos, 100.000 registros con un buffer de 3072 deberían tardar algo menos de 120 segundos, y 500.000 deberían realizarse en unos 550 segundos, algo menos de 10 minutos, con lo que habríamos rebajado el tiempo en mucho, pero… ¿Vivimos en un mundo ideal?

El código de nuestra aplicación sería este:

private void Button_Click(object sender, RoutedEventArgs e)
{

    string load = "XXXXXXXXXXXXXXXXXXXX";

    Stopwatch watch = Stopwatch.StartNew();
    using (DbContext context = new DbContext("Data Source='isostore:/Db.sdf'; Max Database Size = 128; Max Buffer Size = 3072;"))
    {
        for (int j = 0; j < 5; j++)
        {
            for (int i = 0; i < 100000; i++)
            {
                TestTable insert = new TestTable();
                insert.Id = Guid.NewGuid();
                insert.Payload = load;
                context.Test.InsertOnSubmit(insert);
            }

            context.SubmitChanges();
        }
    }

    watch.Stop();

    TotalTime.Text = watch.Elapsed.TotalMinutes.ToString();
}

Y el resultado es:

image

Positivo! Curiosamente, ambos dispositivos, tanto el Nokia como el HTC han clavado los tiempos en 28 minutos y 36 segundos… en el caso del HTC es una gran mejoría con respecto a nuestra primera intentona, pasando de los 37 minutos a 28 y 36 segundos, 8 minutos y 24 segundos más rápido. En el caso del Nokia la mejoría ha sido de solo 44 segundos. Curiosamente, el emulador ha empeorado en esta prueba, pasando de un tiempo de 4 minutos y 36 segundos a 5 minutos y 18 segundos. Sospecho que estos resultados pueden deberse a las diferencias en el hardware de los dispositivos, el Nokia tiene una memoria flash muy rápida, lo cual le permitía aguantar el tipo antes de aumentar el tamaño de buffer, con lo que al aumentarlo, la mejoría ha sido muy pequeña. En el HTC la memoria es algo más lenta, por lo que al gestionar mejor el tamaño de buffer, estamos optimizando mucho el rendimiento y observamos una gran mejoría.

En el caso del emulador, sospecho que las iteraciones se están realizando tan rápidamente que se está saturando la escritura de cambios en el almacenamiento y por eso tenemos un empobrecimiento del rendimiento.

Vamos a realizar una última prueba, nuestro último cartucho en este camino… vamos a realizar la inserción de los 500.000 elementos como en el primer intento, pero con el buffer establecido al máximo, 4096Kb, a ver si arrancamos algo más de rendimiento…:

private void Button_Click(object sender, RoutedEventArgs e)
{

    string load = "XXXXXXXXXXXXXXXXXXXX";

    Stopwatch watch = Stopwatch.StartNew();
    using (DbContext context = new DbContext("Data Source='isostore:/Db.sdf'; Max Database Size = 128; Max Buffer Size = 4096;"))
    {

        for (int i = 0; i < 500000; i++)
        {
            TestTable insert = new TestTable();
            insert.Id = Guid.NewGuid();
            insert.Payload = load;
            context.Test.InsertOnSubmit(insert);
        }

        context.SubmitChanges();
    }

    watch.Stop();

    TotalTime.Text = watch.Elapsed.TotalMinutes.ToString();
}

Y los resultados finales:

image

Para terminar hemos conseguido volver a mejorar el tiempo en nuestro emulador, bajando hasta los 3 minutos y 48 segundos. En cuanto a nuestros dispositivos físicos, HTC Mazaa está al límite de lo que podemos mejorar por esta vía y baja 3 segundos con respecto a la prueba anterior. El Nokia Lumia 800 saca pecho y hace gala de su hardware más nuevo y potente para bajar hasta los 26 minutos y 21 segundos, algo más de 2 minutos más rápido que el HTC Mazaa y casi 3 minutos más rápido que en su primer intento.

Lecciones aprendidas

Lo más interesante de este artículo, no ha sido intentar batir a MongoDb o SQL Server, algo a priori imposible, lo más interesante es ver las lecciones que nos enseña entre líneas:

  • El emulador de Windows Phone nos permite probar la funcionalidad de nuestra aplicación y nos ofrece una visión general del rendimiento de la misma, pero NUNCA, NUNCA, NUNCA va a ofrecernos el rendimiento que nos ofrece un dispositivo físico. Puede que en el emulador todo vaya genial, pero es importantísimo realizar pruebas sobre dispositivos. Por eso las pruebas de certificación del Marketplace se realizan sobre dispositivos, no sobre emulador. Si no tienes un dispositivo físico, en los foros de Windows Phone en msdn varios colaboradores nos ofrecemos a poner los nuestros a tu servicio, si vives cerca de nosotros y tomar un café, ver la app y probarla, mira aquí, si no puedes acercarte a donde nos encontramos, también puedes enviarnos el xap y seguro que podemos sacar algunos minutos para probarlo y responderte con feedback. Por otro lado, escribiendo un mail a sopwp7@microsoft.com (en español) puedes solicitar que te presten un teléfono de desarrollo durante una semana o dos.
  • Por defecto, SQL Server CE usa un tamaño de base de datos de 16MB, si piensas que tu aplicación puede requerir mucho más tamaño puedes usar la propiedad de la cadena de conexión Max Database Size para indicar el tamaño deseado, entre 16 y 512MB, esto te ahorrará más de un problema si tu base de datos crece demasiado.
  • Si necesitamos insertar muchos registros al mismo tiempo, debemos tener en cuenta el tamaño de cada registro y el buffer a usar, mediante el parámetro Max Buffer Size, para optimizar las escrituras. El valor máximo de este parámetro es 4096Kb. En ciertos casos, un valor demasiado grande, puede tener un efecto nocivo sobre nuestro rendimiento.
  • No todos los dispositivos son iguales, pero son muy parecidos, al optimizar nuestro código, hemos visto como dispositivos tan diferentes como el HTC Mazaa, un prototipo de ingeniería que no está a la venta, y el Nokia Lumia 800, uno de los más nuevos y mejores teléfonos con Windows Phone 7.5, se han igualado mucho, con pequeñas diferencias en el uso tan desproporcionado de 500.000 registros. Debemos tener esto en cuenta e intentar obtener un punto común de optimización.

Conclusión

Es hora de retirarnos del campo de batalla, curar nuestras heridas y… volver a la carga!

Después de los misiles 1 & 2, todavía tengo algo en mi arsenal que puede cambiar el curso de la batalla… lo veremos en breve!! Por ahora, os dejo aquí el código del Missile One y Missile Two para que juguéis con el y si os atrevéis a probarlo en otros terminales nos contéis los resultados.

Un saludo y Happy Coding!

[Evento] AppCircus Academy Madrid

Hola a todos!

Este Martes 13 de Diciembre, de la mano de Microsoft y Nokia, tendré el placer de participar como ponente de un workshop sobre Windows Phone 7.5 en el App Circus Academy de Madrid.

AppCircus Academy en Madrid Nokia Microsoft

 

Durante una hora podremos ver las novedades de la versión 7.5 del sistema y una introducción al patrón MVVM y como realizar de forma eficiente pruebas unitarias en nuestras aplicaciones.

Si te interesa, puedes registrarte aquí

Nos vemos en unas semanas!