lunes, 23 de junio de 2014

El Demo del Día: Comparación de Rendimiento al Llenar Listas en WinForms

Comparación de Rendimiento al Llenar Listas en WinForms

En este post, compararemos el rendimiento de diferentes técnicas para llenar listas en Windows Forms desde un arreglo. En WinForms los controles ListBox, ComboBox, CheckedListBox y ListView tienen una colección de Items que representan los elementos del control. Aprenderemos varias formas de mejorar la performance o rendimiento al llenar controles de tipo listas.

Problema al Llenar Listas en WinForms

Si tenemos un arreglo con datos y queremos llenar un control de tipo lista en WinForms, muchos programadores crean un bucle (for, foreach, while) y leen del arreglo cada elemento y lo agregan al control lista. Si son muchos registros a agregar el tiempo aumenta considerablemente en proporción a la cantidad de filas y se nota un parpadeo al momento de pintar el control.

Solución al Problema de Llenar Listas en WinForms

Existen 2 formas de solucionar el problema del rendimiento al llenar un control lista desde un arreglo:

1. Usar el método AddRange de la colección de Items del control lista y agregar directamente el arreglo.

2. Usar el método BeginUpdate antes del bucle para congelar la actualización en pantalla de fila x fila y al final del bucle llamar al método EndUpdate para actualizar el pintado del control lista.

Diseño de la Aplicación WinForms

Crear una aplicación WinForms llamada: "Comparacion_Rendimiento_Listas" en C# y cambiar de nombre al formulario por "frmLista", luego agregar los siguientes controles:
- Un Control Label llamado "lblTiempoProcesamiento"
- Un Control ListBox llamado "lstNumero"
- Cinco Controles Button llamados: "btnLlenarArreglo", "btnLlenarListaForItemsAdd",
  "btnLlenarListaAddRange", "btnLlenarListaForItemsAddBU" y "btnLlenarListaAddRangeBU".


Código Fuente de la Aplicación WinForms

En el código he creado un método "ejecutarMetodo" que tiene como parámetro un método gracias al tipo "Action" lo que me permite crear una rutina que permita "medir el tiempo de procesamiento de cualquier método" para poder comparar los tiempos en cada caso, para lo cual usamos la clase "Stopwatch" de "System.Diagnostics".
Además usamos un arreglo de cadenas, el cual lo llenamos con 10000 registros para poder ser pasados al control ListBox con las diferentes técnicas.

using System.Windows.Forms;
using System.Diagnostics;

namespace Comparacion_Rendimiento_Listas
{
    public partial class frmLista : Form
    {
        private string[] lista;

        public frmLista()
        {
            InitializeComponent();
        }

        public string ejecutarMetodo(Action metodo)
        {
            Stopwatch oReloj = new Stopwatch();
            oReloj.Start();
            metodo();
            oReloj.Stop();
            return (String.Format("Tiempo de Procesamiento: {0:n0} msg",
            oReloj.Elapsed.TotalMilliseconds));
        }

        private void llenarArreglo()
        {
            lista=new string[10000];
            for (int i = 0; i < lista.Length; i++)
            {
                lista[i] = i.ToString();
            }
        }

        private void btnLlenarArreglo_Click(object sender, EventArgs e)
        {
            lblTiempoProcesamiento.Text = ejecutarMetodo(llenarArreglo);
        }

        private void llenarListaForItemsAdd()
        {
            lstNumero.Items.Clear();
            for (int i = 0; i < lista.Length; i++)
            {
                lstNumero.Items.Add(lista[i]);
            }
        }

        private void btnLlenarListaForItemsAdd_Click(object sender, EventArgs e)
        {
            lblTiempoProcesamiento.Text = ejecutarMetodo(llenarListaForItemsAdd);
        }

        private void llenarListaAddRange()
        {
            lstNumero.Items.Clear();
            lstNumero.Items.AddRange(lista);
        }

        private void btnLlenarListaAddRange_Click(object sender, EventArgs e)
        {
            lblTiempoProcesamiento.Text = ejecutarMetodo(llenarListaAddRange);
        }

        private void llenarListaForItemsAddConBeginUpdate()
        {
            lstNumero.BeginUpdate();
            lstNumero.Items.Clear();
            for (int i = 0; i < lista.Length; i++)
            {
                lstNumero.Items.Add(lista[i]);
            }
            lstNumero.EndUpdate();
        }

        private void btnLlenarListaForItemsAddBU_Click(object sender, EventArgs e)
        {
            lblTiempoProcesamiento.Text = ejecutarMetodo(llenarListaForItemsAddConBeginUpdate);
        }

        private void llenarListaAddRangeConBeginUpdate()
        {
            lstNumero.BeginUpdate();
            lstNumero.Items.Clear();
            lstNumero.Items.AddRange(lista);
            lstNumero.EndUpdate();
        }

        private void btnLlenarListaAddRangeBU_Click(object sender, EventArgs e)
        {
            lblTiempoProcesamiento.Text = ejecutarMetodo(llenarListaAddRangeConBeginUpdate);
        }
    }
}

Ejecución y Pruebas de la Aplicación WinForms

Si ejecutamos la aplicación y damos clic al botón "Llenar Arreglo" notamos que el tiempo de procesamiento es de 2 msg (mili segundos), si damos clic al botón "Llenar Lista For Items Add" el tiempo de procesamiento es de 3306 msg (este puede variar cada vez que se da clic entre 2500 y 3500 aproximadamente), además en todo este tiempo se nota la aplicación en espera y con parpadeo.


Si damos clic al botón "Llenar Lista AddRange" o los otros 2 últimos botones (es decir las 3 formas de mejor rendimiento) notamos que el tiempo de procesamiento es de 69 msg (puede variar entre 60 y 80 msg).


Comentario Final

Después de hacer las comparaciones de rendimiento nos damos cuenta que la diferencia es extremadamente abismal, para 10000 filas el método clásico del bucle demora mas de 3000 msg mientras que las otras 3 técnicas solo 70 msg, es decir una diferencia de 3000, la cual aumenta con el aumento de los registros.

Después de esto, espero que ningún programador se olvide de colocar "BeginUpdate" antes del llenado de su lista y al final "EndUpdate" o use directamente el método AddRange.

Descarga:
Demo05_Rendimiento_Listas

El Libro del Día: Windows Phone 8 Recipes

El Libro del Día: 2014-06-23

Titulo: Windows Phone 8 Recipes
Autor: Lori Lalonde, David R Totzke
Editorial: Apress
Nro Paginas: 417

Capítulos:
Chapter 1: Introduction to the Windows Phone SDK
Chapter 2: Multi-Resolution Support and Basic User Interface Components
Chapter 3: Gestures
Chapter 4: Tiles and Lock Screen
Chapter 5: Background Agents and Local Notifications
Chapter 6: Appointments and Contacts
Chapter 7: Camera, Photos, and Media
Chapter 8: Maps, Location, and Routing
Chapter 9: Communications and Speech
Chapter 10: Launching and Resuming Apps
Chapter 11: Data Storage
Chapter 12: Windows Azure Mobile Services
Chapter 13: Using the Microsoft Live SDK
Chapter 14: Publishing Your App

Descarga:
WindowsPhone8_Recipes