Ir al contenido principal

Test 1: Multithreading vs Singlethreading


¡¡ Bien !! Después de unos cuantos arreglos, ya tenemos disponible el primer TEST para nuestra plataforma.

Puesto que me gustaría mediante este blog interactuar con las entradas de Stackoverflow, para este primer test he seleccionado este interesante tema: multithreading slower than singlethreading

Y para introducirnos en la filosofía de los TEST, no es mi intención impartir una conferencia sobre este asunto, sino simplemente implementar las casuísticas que nos interesa evaluar, y medir los rendimientos. Vamos a ello.

En primer lugar grabamos en BBDD (a través del recién desarrollado interfaz) el test, y los testcases. La cosa quedaría así:


A nivel programático, debemos definir una clase cuyo nombre se corresponda con el campo "Clase" del Test. Y tantos métodos como "Casos" asociemos al test, haciendo igualmente coincidir los nombres.

En este punto les recuerdo que pueden descargar el proyecto íntegro en EnigmaLABS - GitHub

Para que la comparación de rendimiento de cada una de las técnicas empleadas sea certera, hemos de ejecutar en cada uno de los casos, un mismo problema. Para este caso he pensado en el cálculo de la serie fibonacci, en concreto vamos a calcular 200 elementos de esta serie:

 public static void CalcFibo(int numelements)
        {
            Dictionary<int, Int64> lstelements = new Dictionary<int, Int64>();

            int cont = 0;

            Int64 anterior = 0;
            Int64 anterioranterior = 0;

            while (cont < numelements)
            {
                if (cont == 0)
                {
                    lstelements[cont] = 0;
                    anterioranterior = 0;
                }
                else if (cont == 1)
                {
                    lstelements[cont] = 1;
                    anterior = 1;
                }
                else
                {
                    Int64 newelem = anterioranterior + anterior;

                    anterioranterior = anterior;
                    anterior = newelem;

                    lstelements[cont] = newelem;
                }

                cont++;
            }
        }

Nota: en próximas publicaciones veremos formas más elegantes de implementar este método.

Vamos a desarrollar tres casos:

1. Cálculo simultáneo de la serie. 500 hilos calcularán cada uno 200 elementos de la serie al unísono.
2. Cálculo secuencial: bucle de 500 iteraciones, en cada una de las cuales calcularemos igualmente 200 elementos de la serie.
3. Híbrido: 20 hilos, cada uno de los cuales calculará 25 veces, 200 elementos de la serie.

¿Adivinan cuál será el resultado?

Antes les dejaré el código de cada uno de los casos, y después le daremos al PLAY para ver los resultados.

CASE 1: Multithreading Case

        /// <summary>
        /// Cálculo simultáneo de la serie fibo (500 hilos, 200 elementos por hilo)
        /// </summary>
        /// <param name="_test"></param>
        /// <returns></returns>
        public TestCases MultithreadingCase(TestCases _test)
        {
            //registra inicio
            _test.dtBegin = DateTime.Now;

            SetMsg("- - - - -");
            SetMsg("MultithreadingCase iniciado a las " + _test.dtBegin);

            //500 hilos calculan la serie fibo
            int cont = 0;

            while (cont < 500)
            {
                Thread thfibo = new Thread(() => CalcFibo1(cont));

                _lst_process_control.Add(new objects.process_control()
                {
                    Estado = objects.process_control.enumEstadoProceso.Ejecutando,
                    Hilo = thfibo
                });

                thfibo.Start();

                Thread.Sleep(55);
                cont++;
            }

            while (_lst_process_control.Exists(pc => pc.Estado != objects.process_control.enumEstadoProceso.Finalizado))
            {
                Thread.Sleep(55);
            }

            //registra fin
            _test.dtEnd = DateTime.Now;

            TimeSpan _ts = _test.dtEnd - _test.dtBegin;

            SetMsg("MultithreadingCase finalizado a las " + _test.dtEnd);
            SetMsg("MultithreadingCase ejecutado en " + _ts.TotalMilliseconds + " milisegundos");

            _testobject.TestRecord(_test);

            _lst_process_control.Clear();

            return _test;
        }

        private static void CalcFibo1(int index)
        {
            try
            {
                //Console.WriteLine("CalcFibo1 - Index " + index.ToString());
                functions.fibo.CalcFibo(200);

                _lst_process_control[index].Estado = objects.process_control.enumEstadoProceso.Finalizado;
            }
            catch (Exception)
            {
                _lst_process_control[index].Estado = objects.process_control.enumEstadoProceso.Erroneo;
            }
        }

CASE 2: Singlethreading Case

        /// <summary>
        /// Cálculo secuencial de la serie fibo (500 iteraciones, 200 elementos por iteración)
        /// </summary>
        /// <param name="_test"></param>
        /// <returns></returns>
        public TestCases SinglethreadingCase(TestCases _test)
        {
            //registra inicio
            _test.dtBegin = DateTime.Now;

            SetMsg("- - - - -");
            SetMsg("SinglethreadingCase iniciado a las " + _test.dtBegin);

            //500 iteraciones calculando 200 elementos de la serie fibo
            int cont = 0;

            while (cont < 500)
            {
                try
                {
                    functions.fibo.CalcFibo(200);
                    cont++;
                    Thread.Sleep(55);
                }
                catch (Exception)
                {

                }
            }

            //registra fin
            _test.dtEnd = DateTime.Now;

            TimeSpan _ts = _test.dtEnd - _test.dtBegin;

            SetMsg("SinglethreadingCase finalizado a las " + _test.dtEnd);
            SetMsg("SinglethreadingCase ejecutado en " + _ts.TotalMilliseconds + " milisegundos");

            _testobject.TestRecord(_test);

            return _test;
        }

CASE 3: Hybrid Case

        public TestCases HybridCase(TestCases _test)
        {
            //registra inicio
            _test.dtBegin = DateTime.Now;

            SetMsg("- - - - -");
            SetMsg("HybridCase iniciado a las " + _test.dtBegin);

            //20 hilos calculan 25 veces cada uno la serie fibo
            int cont = 0;

            while (cont < 50)
            {
                Thread thfibo = new Thread(() => CalcFibo2(cont));

                _lst_process_control.Add(new objects.process_control()
                {
                    Estado = objects.process_control.enumEstadoProceso.Ejecutando,
                    Hilo = thfibo
                });

                thfibo.Start();

                Thread.Sleep(55);
                cont++;
            }

            while (_lst_process_control.Exists(pc => pc.Estado != objects.process_control.enumEstadoProceso.Finalizado))
            {
                Thread.Sleep(55);
            }

            //registra fin
            _test.dtEnd = DateTime.Now;

            TimeSpan _ts = _test.dtEnd - _test.dtBegin;

            SetMsg("HybridCase finalizado a las " + _test.dtEnd);
            SetMsg("HybridCase ejecutado en " + _ts.TotalMilliseconds + " milisegundos");

            _testobject.TestRecord(_test);

            _lst_process_control.Clear();

            return _test;
        }

        private static void CalcFibo2(int index)
        {
            try
            {
                //Console.WriteLine("CalcFibo1 - Index " + index.ToString());
                int cont = 0;

                while (cont < 25)
                {
                    try
                    {
                        functions.fibo.CalcFibo(200);
                        cont++;
                        Thread.Sleep(55);
                    }
                    catch (Exception)
                    {

                    }
                }

                _lst_process_control[index].Estado = objects.process_control.enumEstadoProceso.Finalizado;
            }
            catch (Exception)
            {
                _lst_process_control[index].Estado = objects.process_control.enumEstadoProceso.Erroneo;
            }
        }

Todo listo. Como pueden apreciar la clase va dejando mensajes en una lista que va recogiendo el contenedor para mostrarlos cada N segundos. Estos mensajes dejarán registro del momento de inicio y fin de ejecución de cada caso.

        private void SetMsg(string Msg)
        {
            this.Mensajes.Add(new test_types.mensajes()
            {
                id = Guid.NewGuid(),
                mensaje = Msg,
                leido = false
            });
        }

¿Ejecutamos? ¿Sí? ¡Ay qué nervios! Vamos allá, ¡dale al PLAY!

Aquí tienen los esperados resultados. Saquen Uds. sus propias conclusiones:


Por si los resultados se vieran afectados por lo que la máquina este procesando en el momento de la ejecución conviene ejecutarlo varias veces. Hagas Uds. mismos la prueba. Ya les anticipo que los resultados van a ser siempre similares.

Sí cabe la posibilidad que los resultados se vean afectados por las características de la máquina, sobre todo, del procesador o procesadores y núcleos. Esto sería interesante verlo, pero debido a la cuarentena que padecemos no dispongo de medios suficientes. ¿Quieren compartir sus ejecuciones conmigo? ¡No lo duden! Comenten libremente.

Espero les guste la filosofía de los TEST. ¿Quieren proponerme algún desarrollo que poner a prueba? ¿Quieren desarrollar su propia clase? ¡¡No lo duden!! Estaremos encantados de probar soluciones, y más soluciones.

Hasta la próxima amigos.

Comentarios

  1. off topic
    off language
    off todo

    ..en todo caso les recomiendo a mis queridos lectores que si necesitan servicios de fumigación en un país árabe-parlante indeterminado no duden en contactar con nuestro amigo ahmed

    ResponderEliminar

Publicar un comentario

Entradas populares de este blog

Test 3: ¿Son eficientes los ORMs?

Bienvenidos amigos. Me complace anunciar que por fin estrenamos la categoría " SQL Server Tips ", y lo hacemos por todo lo alto, entrando de lleno en un aspecto altamente polémico entre programadores. ¿Es eficiente un ORM en los accesos a datos? Ya conocen la filosofía de nuestros Tests, no vamos a teorizar demasiado, pero sí una pequeña base va a ser necesaria para conseguir una buena respuesta a nuestra pregunta. He leído un interesante  artículo de nuestros súper amigos de Deloitte ( cuando usar ORM ) argumentando que el uso o no de un ORM hay que decidirlo en relación a la complejidad de nuestro modelo de datos, y al rendimiento que requeriremos en nuestras soluciones, pero, ¿cuándo no deseamos el mejor rendimiento para nuestro software? Lo cierto es que, como ya hemos visto, el ORM facilita mucho las cosas, aporta claridad al código, de eso no cabe duda, pero, ¿es eficiente? He ahí la cuestión. Sobre este asunto vamos a poner a funcionar nuestros apreciados Test. C

Proyecto GEOW. Implementando el patrón CQRS. 2ª parte

En el anterior post ( Proyecto GEOW. Implementando el patrón CQRS ) nos adentramos en el funcionamiento del proyecto GEOW que nos va a servir de base para implementar un patrón arquitectónico, CQRS , pensado para dar respuesta a sistemas con alta exigencia de lecturas y escrituras simultáneas. Para ello hemos creado una interfaz gráfica con una serie de figuras geométricas en movimiento. Ahora vamos a ir a la parte de EL DATO. Cada vez que uno de estos cuadrados cambia de posición envía una trama con sus propias características, y sus nuevas coordenadas. Cada uno de los cuadrados realiza un movimiento cada 300 milisegundos, y he llegado a probar con hasta unas 700 figuras. En estos niveles el software empieza a sufrir, pero más la parte gráfica. Aparentemente el sistema de grabación de coordenadas se mantiene en buena forma. Vamos a ver, precisamente, este sistema de grabación: Grabando lotes de coordenadas en BBDD En el objeto PointObj que representa cada una de las figuras, en el

Primeros pasos en Enigma Software Labs

En pocas palabras: vamos a probar y estresar distintas soluciones de software para un mismo problema, en busca de realidades matemáticas. Para ello, lo primero que necesitamos en averiguar el modo de publicar código aquí en Blogger . Para ello voy a usar un sistema súper sencillo y eficiente. Ahí va un ejemplo de código C# private void frmMonitor_Load ( object sender, EventArgs e) { TreeNode _tr = new TreeNode( "SQL Server Tips" ); TreeNode _tr2 = new TreeNode( "C# Code" ); treeCatalogo.Nodes.Add(_tr); treeCatalogo.Nodes.Add(_tr2); //splitContainer.Panel1.Focus(); //lstMonitor.Focus(); } Hilarante ¿verdad?   hilite.me  lo hace posible. Bien, paso 2, fácil y sencillo: vamos a crear una plataforma de testeo para C# en una bonita solución que compartiré en el repositorio GIT, para disfrute de todos. Enigma Software - ZM LABS Como posteriormente me dive