Pruebas de rendimiento Web para Windows Azure

Una de las preguntas más recurrentes que suele tener los clientes a la hora de trabajar en Windows Azure suele ser, ¿cuál será el rendimiento de mi sitio web en Azure?

La respuesta a esta pregunta no es sencilla o universal. Depende de muchos factores, del tamaño de la máquina utilizado en el servicio en la nube, de la tecnología, cuantas consultas se hagan a la base de datos, ect. El número de factores que pueden determinar ese número final es amplísimo, así que no se puede dar una respuesta para todos los casos.

Entonces ¿cuál es la mejor aproximación para dar respuesta a esa pregunta?

Medir. Medir. Medir.

Pero antes de medir el tiempo que tarda el sitio web en responder, cabe mencionar algunos artículos creados por otros desarrolladores sobre cómo aumentar el rendimiento de las aplicaciones web.

Coding Horror – Performance is a Feature

Scott Hanselman – Penny Pinching in the Cloud: Enabling New Relic Performance Monitoring on Windows Azure Websites

Medir

La mejor manera para saber si una aplicación web es lenta o rápida es medir el tiempo que tarda una respuesta. Esto permite tener una media real de como el sitio web funciona, pero eso no es suficiente. Se necesita además, simular cierta carga, varios usuarios, con varios patrones de uso de la web. Y no solo hacer una sola petición, sino una navegación completa del usuario comprobando cookies, parámetros de la url, inicios de sesión, ect.

Una vez que se ha establecido un escenario base, uno puede empezar a realizar optimizaciones en su código para ver cómo se mejora ese tiempo. Es importante realizar estas mediciones en el rendimiento una vez que se ha realizado el sitio web, o por lo menos cuando está a punto de cerrarse el proyecto. No tiene ningún sentido empezar a hacer optimizaciones tempranas, ya que son un anti patrón muy claro.

Premature-Optimization and Performance Anxiety

Performance anti patterns

Visto todo lo anterior, lo ideal sería disponer de algún software que nos permite grabar la navegación de un usuario y que después sea capaz de volver a simular todos los pasos que ese usuario ha hecho. Justamente estamos hablando de los Web Performance Load Test de Visual Studio Ultimate.

Web Performance and Load Test

Visual Studio en su versión Ultimate, incluye un tipo de proyecto dentro de la categoría de Test llamado Web Performance and Load Test, que permite grabar sesiones web.

clip_image002

Cuando se ha creado el proyecto, hay un fichero con extensión de tipo .webtest donde aparecerán todas las peticiones HTTP que se han grabado para esa sesión. Cada fichero representa una colección de peticiones HTTP que pueden ser una historia de usuario, una navegación sobre una característica a medir, o simplemente una invocación a una API Rest.

clip_image004

Se puede hacer una grabación con Internet Explorer donde se registran todos los pasos que el usuario ha hecho en el sitio web.

clip_image005

clip_image006

Una vez que ha terminado de hacer la grabación aparecerán en Visual Studio la lista de Urls a testear.

También se puede utilizar un origen de datos para obtener las urls y así hacerlos parte un test de integración.

Por supuesto se pueden agregar a mano las peticiones, las condiciones para dar como válida una petición y todo lo relacionado con el ciclo de vida de una petición.

clip_image008

Una vez que se ha confeccionado la lista de las Urls que forman parte del test, se puede pasar a hacer la prueba de rendimiento o se puede personalizar las peticiones. Hay un botón en la barra del test que permite generar el código en C# asociado de las peticiones para poder modificarlo a petición del usuario, de esta manera se puede automatizar mucho más el proceso.

clip_image010

Prueba de carga

El siguiente paso es generar y configurar una prueba de carga para la prueba web recién creada. Dentro del proyecto de Test hay que pulsar en añadir nuevo elemento y seleccionar prueba de carga (Load Test).

clip_image012

En el cual inmediatamente creará un asistente en el que se pueden configurar las diferentes opciones para la prueba de carga.

clip_image014

1. Darle un nombre al escenario

clip_image016

2. Ahora se seleccionan el patrón de carga, como se desean simular los usuarios de la prueba. Se puede elegir un valor constante de usuarios y otro incremental en el que se pueden configurar todas las opciones de incremento.

clip_image018

3. En la siguiente pantalla se puede elegir la manera en la que se mezclan los diferentes escenarios de prueba (que son las pruebas web que se han definido antes).

clip_image020

4. En esta pantalla es donde se agregan los diferentes test que se van a formar parte de las pruebas y que tanto porciento representan.

clip_image022

5. Ahora se pueden seleccionar las velocidades de red para calcular el tiempo de descarga.

clip_image024

6. Mezcla de navegadores

clip_image026

7. Ahora se pueden seleccionar las máquinas de las que se quieren obtener los contadores de rendimiento, en el caso que se está tratando ahora (Windows Azure) tendríamos que abrir los puertos de WMI para poder acceder a esa información, pero si se está haciendo una prueba en local con otra máquina se puede poner el nombre de la máquina y qué contadores de rendimiento se quieren monitorizar.

clip_image028

8. La última opción define cuánto tiempo durará la prueba.

clip_image030

Por supuesto todas estas opciones pueden ser modificadas posteriormente en cualquier momento. Una vez finalizado el asistente, en Visual Studio aparece esta pestaña que contiene las propiedades del proyecto de carga

clip_image032

Recopilando datos

La prueba de carga consiste en recopilar datos de muchos contadores, no solo de las máquinas en las que se ejecuta la web, sino de los agentes que ejecutan las pruebas de cargas. Eso hace que una de las opciones a la hora de guardar las muestras de la prueba sea una base de datos SQL Server. Desde la opción de administrar el controlador de la prueba, se puede acceder al almacén donde se guardarán los datos.

clip_image033

Desde aquí se puede configurar en qué instancia de SQL Server se guardaran los datos de muestra. La base de datos tiene que tener un esquema predeterminado para que funcione correctamente. En el directorio de instalación de Visual Studio hay un fichero llamado (C:\Program Files (x86)\Microsoft Visual Studio 12.0\Common7\IDE) loadtestresultsrepository.sql que contiene el script necesario para generar la base de datos desde cero.

Ejecutando la prueba

Una vez que se ha configurado todas las opciones de la prueba de carga, se puede proceder a ejecutar la prueba. Visual Studio ofrece un panel de control para ver toda la información sobre la prueba, los contadores y los resultados de manera provisional. Toda la información que se recopila se guarda en la base de datos antes configurada y se puede volver a abrir para consultar los datos.

Panel de información

Cuando se ha acabado la prueba y se vuelve a abrir el resultado, primero aparece un resumen de todo lo ejecutado.

image

Donde se puede visualizar información relativa a cuando se ha ejecutado la prueba, los principales valores del resultado; carga de usuarios, media de tiempo de respuesta de la web, número de peticiones por segundo, ect.

Además de esta información, se puede visualizar individualmente por cada una de las web que forman parte de la prueba, la información del número de peticiones y de tiempo medio de respuesta.

Al final de todo aparece el detalle del controlador y de los agentes que han realizado la prueba.

Graficas

Otra pestaña muy importante para entender los resultados de la prueba de carga es la de gráficas. En esta pestaña se puede visualizar, a lo largo del tiempo, los valores que se deseen. De manera predeterminada Visual Studio ofrece un cuadrante de cuatro gráficas, donde se pueden añadir métricas sobre las diferentes opciones de la prueba de carga.

image

Conclusión

Con las pruebas de carga Web de Visual Studio se pueden hacer mediciones del tiempo medio de respuesta de los sitios web, numero de excepciones, número de peticiones por segundo antes de dar un error de ocupado, ect.

De esta manera se puede hacer una prueba de carga directamente apuntando a Windows Azure, para ver cuantas peticiones son capaces de responder los servidores antes de dar errores 500. Es importante hacer estas pruebas con el número de servidores que se utilizará en producción para intentar que el entorno de pruebas de carga sea lo más similar al entorno final.

Luis Guerrero.

@guerrerotook

Conoce Windows Azure con los #CloudIO de desarrolloweb.com

Desde Microsoft estamos decididos a que conozcas Windows Azure, así hasta finales de Abril vamos a tener una serie de #AzureIO en desarrolloweb.com.

Se pueden seguir las actualizaciones sobre los #CloudIO en @deswebcloud

Para la gente que no conozca el formato, www.desarrolloweb.com es un portal de ayuda a programadores de Web y otras soluciones que organizan los AzureIO. Se puede encontrar más información en esta dirección: http://www.desarrolloweb.com/en-directo/

Esta es la lista completa de todos los conceptos que vamos a cubrir en estos #AzureIO, así que estad atentos al blog y a las cuentas oficiales de Twitter de Microsoft España donde iremos publicando las direcciones de los eventos.

Windows Azure Virtual Machines

Máquinas virtuales de Windows Server y Linux en la nube

Enlace de YouTube.

Windows Azure Mobile Services

Crear un backend para tu app movil en segundos. Con soporte para Windows Store, Windows Phone, iOS, Android y HTML / Javascript

Windows Azure Notification Hubs

Envía millones de notificaciones push a dispositivos Windows, iOS o Android con una única llamada de API.

Windows Azure Storage

Es el servicio de almacenamiento con tres abstracciones, Tablas, Colas y Blobs

Windows Azure Web Sites

Te permite crear tu sitio web en segundos. Elige entre mucha de las plantillas de aplicación, o conecta tu repositorio de código para publicar directamente.

Windows Azure Cloud Services

Los servicios en la nube, permite a las aplicaciones empresariales escalar hasta el infinito, elije entre roles web o de trabajo.

Active Directory y MFA

Aloja tu controlador de dominio en Windows Azure y habilita a tus usuarios la autenticación en dos pasos. (Multi-Factor Authentication)

Windows Azure Media Services

Es el servicio que te permite la ingesta masiva de videos, la codificación en diferentes formatos y calidades, la protección de tu IP con PlayReady y la distribuccion de los contenidos a través de la CND con soporte para re empaquetar el contenido al vuelo.

HDInsight

Crea tu cluster de Apache Hadoop en segundos y utiliza el algoritmo Map-Reduce.

Windows Azure Cache & Traffic Manager

El servicio de Windows Azure Cache permite guardar objetos en una cache distribuida de alto rendimiento. Traffic Manager es la manera de gestionar como los usuarios acceder a tu servicio mundialmente.

Backup Service

Este servicio te permite hacer backup de tus datos de onpremises en la nube.

Luis Guerrero.

Technical Evangelist Windows Azure

@guerrerotook

3 formas diferentes de publicar una Web en Windows Azure.

Introducción

Windows Azure es una plataforma muy flexible en la que se pueden publicar y consumir todo tipo de aplicaciones. En este post se repasarán las 3 formas que hay de publicar un Web, viéndose las ventajas e inconvenientes de las tres formas.

Las tres maneras son: Máquinas virtuales (Infraestructura como servicio IaaS), Servicios de nube (Plataforma como servicio, PaaS) y Windows Azure Web Sites.

Maquinas Virtual, IaaS

Las máquinas virtuales, como su nombre indica es la capacidad que tiene Windows Azure de hostear una máquina virtual basada en ficheros vhdx. Desde el portal de Windows Azure se puede seleccionar nueva máquina virtual y seleccionar creación rápida. De esa manera se tiene una maquina encendida y funcionado.

Una vez generada la VM se puede instalar desde ahí, Internet Information Services y copiar nuestra aplicación Web en la máquina.

Ventajas

  • Se tiene toda la flexibilidad para instalar y administrar la máquina virtual desde escritorio remoto.
  • Se puede alojar la aplicación web en Linux.
  • La máquina virtual es persistente.

Inconvenientes

  • No hay mecanismo definido para publicar la web. Copiar los ficheros por escritorio remoto a mano o cualquier otro mecanismo.
  • Hay que configurar e instalar IIS y ASP.NET.
  • Se tiene que configurar y administrar el balanceo de carga a mano para el puerto de HTTP/HTTPS. Cuando se quieran agregar más maquinas se tiene que hacer a mano.
  • Las tareas de administración se tienen que repetir para todas las máquinas.
  • El usuario es responsable del mantenimiento del S.O.

Servicios en la nube (Plataforma como servicio)

Este fue el primer servicio con el que se lanzó Windows Azure. Permite empaquetar una aplicación Web, independientemente del tipo de lenguaje o runtime, y publicar esa aplicación en el número de máquinas que se hayan designado en el fichero de configuración.

En este método de publicar aplicaciones web, se genera una máquina virtual, se instala y configura IIS con los valores presentes en el fichero de configuración del servicio y se copia el código de la aplicación web al directorio de publicación de IIS.

El proceso es completamente automático y no requiere atención por el usuario en ningún momento. No tiene límite en cuanto al número de instancias de máquinas. (Cómo curiosidad decir que el máximo de máquinas que el autor ha configurado han sido 750, toda una pasada)

Ventajas

  • Flexibilidad para empaquetar y publicar la aplicación.
  • No requiere configuración por parte del usuario, está completamente automatizado.
  • Permite incrementar y reducir el número de instancias a petición del usuario, también de manera automática.
  • Tiene soporte para el autoscale de Windows Azure.
  • Permite configurar aplicaciones web en subdominios y subcarpetas como aplicaciones (en IIS).
  • Permite configurar todos los certificados SSL que se deseen.

Inconvenientes

  • Las máquinas donde se ejecutan no son persistentes. Hay que guardar todos los datos fuera de la máquina donde se ejecuta. Por ejemplo en un Windows Azure Storage.
  • Las peticiones Http al servicio no tienen afinidad, lo que significa que cualquier petición puede ir a cualquiera de las maquinas del servicio en la nube. Hay que configurar la sesión de ASP.NET para que se guarde en Sql Server, Windows Azure Cache Service o Windows Azure Storage.
  • Hay que ser capaz de empaquetar la aplicación web y todas sus dependencias de software.

Windows Azure Web Sites

Windows Azure Web Sites es otro servicio de Windows Azure enfocado a la publicación de aplicaciones Web pero, ¿Cuál es la diferencia con un servicio en la nube? Los servicios en la nube pueden contener, además de un rol de tipo Web, un rol de tipo de trabajo. Es decir un servicio en la nube es una colección de roles de trabajo y de web. Un rol de trabajo es como un servicio de Windows, pero que se ejecuta en Windows Azure y ejecuta tareas en segundo plano. Esas tareas pueden ser generar informes, consolidad información en la base de datos, envíos masivos de emails, cambio de resolución de imágenes y todo aquello que se tiene que hacer en nuestra aplicación web, pero que no se quiere que se impacte en el rendimiento del sitio web.

Volviendo al tema de Windows Azure Web Sites, este servicio ofrece tres modos de funcionamiento:

  • Gratis: los usuarios tienen hasta un máximo de 10 sitios web de manera gratuita, 1 Gb de espacio en disco y 165 megas de salida al día.
  • Compartida: hasta un máximo de 100 sitios web, que se pueden escalar hasta un máximo de 6 instancias, con 1 Gb de espacio en disco y el tráfico de salida se factura al precio estándar.
  • Estándar: hasta un máximo de 500 sitios web con una CPU dedicada, 10Gb de espacio en disco y el tráfico de salida se factura al precio estándar.

Con estas tres configuraciones se ofrecen diferentes maneras y precios de alojar tu aplicación web según los requerimientos del usuario.

Otra de las diferencias con respecto a los servicios en la nube, es la forma de publicar tu sitio web. WAWS ofrece varias maneras de publicar:

  • Microsoft Web Publishing: Permite publicar un proyecto de web directamente desde Visual Studio.
  • Team Foundation Service: se pude publicar directamente desde TFS Service, la solución en la nube de TFS. No funciona con TFS on premises.
  • Repositorio local de Git: puedes hacer push desde un repositorio local a uno online que representa tu sitio web.
  • Github es un servicio de hosting en Git de terceros.
  • Dropbox: te permite configurar una carpeta de la cuenta para publicar directamente en tu sitio web.
  • Bitbucket: otro servicio de hosting de Git como Github.
  • Codeplex: el servicio de hosting de proyectos de Microsoft.
  • External repository: un repositorio externo en una URL.

Otro de los aspectos que se pueden configurar en el servicio es la versión de .NET Framework en la que se ejecuta la Web, V3.5 o V4.5. Otro de los lenguajes que vienen instalados es PHP que se puede deshabilitar o cambiar la versión de 5.3 a 5.4.

Ventajas

  • Flexibilidad en cuanto al método de publicación de la web.
  • Perfecta para trabajo en equipo.
  • Rapidez, dar de alta un sitio web en Windows Azure son segundos y tener tu web online también.

Inconvenientes

  • No se puede acceder a la configuración de IIS, solamente la configuración que se ofrece desde el portal de administración de Windows Azure.
  • El número de instancias de los modos de compartido y estándar son máximo 6 y 10 respectivamente. En los servicios de la nube no hay límite.

Espero que el artículo haya sido de ayuda para decidirse con qué servicio de Windows Azure se ajusta mejor a las necesidades de cada proyecto.

Optimizaciones de JavaScript utilizadas en el proyecto de Prometheus

Introducción

Para los que no lo sepáis, próximamente se estrena la película Prometheus en Estados Unidos y desde Plain Concepts hemos desarrollado el training center del sitio web. Se puede acceder desde este enlace: http://www.projectprometheus.com/trainingcenter/. El proyecto ha estado financiado por Microsoft, más concretamente por el equipo de Internet Explorer, así que como página web que es, se ha desarrollado utilizando las últimas tecnologías web: HTML5 + CSS3.

Training center

El centro de entrenamiento es un sitio web donde los candidatos al proyecto Prometheus, de la empresa Weyland, puede probar su valía. El entrenamiento cuenta con 5 pruebas (juegos) que el recluta tiene que completar en un tiempo determinado. Una vez superadas las cinco pruebas el recluta puede formar parte de Weyland Industries. Los cinco juegos han sido desarrollados por gente de Plain Concepts:

Cada uno de los cuales ha desarrollado uno de los juegos del centro de entrenamiento. En mi caso he desarrollado el cubo de rubick en 2 y 3 dimensiones.

clip_image001

Aquí se puede ver una captura del juego en Internet Explorer llamado Prefrontal Cortex.

HTML5 / Javascript

Todos los juegos han sido desarrollados en HTML5 utilizando JavaScript para la parte de programación, en mi caso he utilizado Canvas para dibujar los cubos. Eso significa que todos los juegos funcionan perfectamente en todos los navegadores modernos, incluyendo Google Chrome, Firefox, Safari, Opera y Internet Explorer 9 y 10.

Para el desarrollo de los juegos se creo un motor en JavaScript que nos permitiera dibujar en un Canvas la geometría de los modelos de los cubos. Este motor no utiliza WebGL para renderizar los cubos, porque Internet Explorer no tiene soporte (además de en el resto es experimental), por lo que se opto por hacer un motor grafico completo desde cero. Es decir, todo el pipeline de grafica se tiene que hacer en JavaScript, esto significa, entre otras cosas, que tenemos que emular por software como funciona una tarjeta gráfica y eso normalmente es más lento que el propio hardware. Así que el desafío de implementar un pipeline gráfico por software es mayor ya que tiene que tener un rendimiento aceptable.

JavaScript 101

JavaScript es un lenguaje en el que existen varios tipos de datos básicos con los que podemos trabajar.

  • Object
  • Array
  • Number
  • String
  • Boolean

Los objetos son la forma más común a la hora de trabajar en JavaScript y se pueden utilizar de muchas maneras.

La forma más sencilla de crear un objeto es:

   1: var myObject = {}

A partir de ahí se pueden ir agregando propiedades al objeto sin ningún tipo de restricción. No son propiedades como las que se puede estar acostumbrado en C#, sino que el tipo object se comporta como una especie de diccionario de pares nombre valor.

Se pueden crear propiedades de la siguiente manera a un objeto previamente definido.

   1: myObject.name = ‘Luis’;

   2: myObject.number = 42;

Así el objeto pasará a tener dos propiedades, una llamada ‘name’ con el valor ‘Luis’ y otra llamada ‘number’ con el valor ‘42’.

También se puede acceder a esas propiedades como si de un diccionario se tratase. Las dos formas son igual de válidas y correctas.

   1: var name = myObject[‘name’];

Después de la ejecución de esta línea de código lo que se establece en la variable name es el valor de ‘Luis’, previamente establecido.

Con esta nueva forma de acceder a las propiedades no solo se pueden leer valores almacenados en un objeto sino que también se pueden guardar.

myObject[‘currentDate’] = new Date();

Vector3

Como ejemplo de objeto se va a definir Vector3; un vector de 3 dimensiones.

var vector3 = {x: 1, y: 1, z: 1}

¿Cuál es el problema con este Vector3?

El rendimiento. Como se ha dicho antes todos los objetos en JavaScript se comportan como un diccionario de pares nombre / valor, así que en cada una de las operaciones en las que se tenga que leer o escribir el valor de x, y o z, el runtime de JavaScript tiene que comprobar que la propiedad existe o no y luego leerla o almacenarla. Todo esto lleva tiempo. Es como si se programase todo el acceso a propiedades y campos en .NET utilizando únicamente la API de Reflexion (System.Reflection).

En el caso del motor de 3D en JavaScript Vector2, Vector3, Vector4, Color y Matrix son tipos que se están usando constantemente para dibujar la geometría de los cubos, así que esos tipos fueron los primero en ser optimizados.

La solución por la que se opto fue eliminar la definición de los tipos, es decir, que por ejemplo Vector2, Vector3, Vector4 y Color pasaron a ser un array de 2, 3, 4 y 4 posiciones respectivamente. Así que por convención lo que se hizo fue que la posición dentro del array representaba una coordenada de las dimensiones del vector.

  • X: array[0]
  • Y: array[1]
  • Z: array[2]
  • W: arrat[3]

En el caso de Matrix que se tenía M11, M12…M21,M22..M31..M44 pasaron a ser también las posiciones de un array.

Veamos como se ha cambiado la multiplicación de matrices, uno de los cuellos de botella, en cuanto a rendimiento se refiere.

Antes

   1: function Multiply(matrix1, matrix2) {

   2:     var matrix = new Matrix();

   3:     matrix.M11 = (((matrix1.M11 * matrix2.M11) + (matrix1.M12 * matrix2.M21)) + (matrix1.M13 * matrix2.M31)) + (matrix1.M14 * matrix2.M41);

   4:     matrix.M12 = (((matrix1.M11 * matrix2.M12) + (matrix1.M12 * matrix2.M22)) + (matrix1.M13 * matrix2.M32)) + (matrix1.M14 * matrix2.M42);

   5:     matrix.M13 = (((matrix1.M11 * matrix2.M13) + (matrix1.M12 * matrix2.M23)) + (matrix1.M13 * matrix2.M33)) + (matrix1.M14 * matrix2.M43);

   6:     matrix.M14 = (((matrix1.M11 * matrix2.M14) + (matrix1.M12 * matrix2.M24)) + (matrix1.M13 * matrix2.M34)) + (matrix1.M14 * matrix2.M44);

   7:     matrix.M21 = (((matrix1.M21 * matrix2.M11) + (matrix1.M22 * matrix2.M21)) + (matrix1.M23 * matrix2.M31)) + (matrix1.M24 * matrix2.M41);

   8:     matrix.M22 = (((matrix1.M21 * matrix2.M12) + (matrix1.M22 * matrix2.M22)) + (matrix1.M23 * matrix2.M32)) + (matrix1.M24 * matrix2.M42);

   9:     matrix.M23 = (((matrix1.M21 * matrix2.M13) + (matrix1.M22 * matrix2.M23)) + (matrix1.M23 * matrix2.M33)) + (matrix1.M24 * matrix2.M43);

  10:     matrix.M24 = (((matrix1.M21 * matrix2.M14) + (matrix1.M22 * matrix2.M24)) + (matrix1.M23 * matrix2.M34)) + (matrix1.M24 * matrix2.M44);

  11:     matrix.M31 = (((matrix1.M31 * matrix2.M11) + (matrix1.M32 * matrix2.M21)) + (matrix1.M33 * matrix2.M31)) + (matrix1.M34 * matrix2.M41);

  12:     matrix.M32 = (((matrix1.M31 * matrix2.M12) + (matrix1.M32 * matrix2.M22)) + (matrix1.M33 * matrix2.M32)) + (matrix1.M34 * matrix2.M42);

  13:     matrix.M33 = (((matrix1.M31 * matrix2.M13) + (matrix1.M32 * matrix2.M23)) + (matrix1.M33 * matrix2.M33)) + (matrix1.M34 * matrix2.M43);

  14:     matrix.M34 = (((matrix1.M31 * matrix2.M14) + (matrix1.M32 * matrix2.M24)) + (matrix1.M33 * matrix2.M34)) + (matrix1.M34 * matrix2.M44);

  15:     matrix.M41 = (((matrix1.M41 * matrix2.M11) + (matrix1.M42 * matrix2.M21)) + (matrix1.M43 * matrix2.M31)) + (matrix1.M44 * matrix2.M41);

  16:     matrix.M42 = (((matrix1.M41 * matrix2.M12) + (matrix1.M42 * matrix2.M22)) + (matrix1.M43 * matrix2.M32)) + (matrix1.M44 * matrix2.M42);

  17:     matrix.M43 = (((matrix1.M41 * matrix2.M13) + (matrix1.M42 * matrix2.M23)) + (matrix1.M43 * matrix2.M33)) + (matrix1.M44 * matrix2.M43);

  18:     matrix.M44 = (((matrix1.M41 * matrix2.M14) + (matrix1.M42 * matrix2.M24)) + (matrix1.M43 * matrix2.M34)) + (matrix1.M44 * matrix2.M44);

  19:     return matrix;

  20: }

La multiplicación simplemente accedía a cada uno de los índices de la matriz, los multiplicaba y luego los asignada de vuelta a la matriz de resultado. Como hemos dicho antes, esto implica leer una gran cantidad de propiedades durante el dibujado de un frame de la escena.

Ahora

   1: function Multiply(matrix1, matrix2) {

   2:     var matrix = new Matrix();

   3:     var position = matrix.position;

   4:     var position1 = matrix1.position;

   5:     var position2 = matrix2.position;

   6:     position[0] = (((position1[0] * position2[0]) + (position1[1] * position2[4])) + (position1[2] * position2[8])) + (position1[3] * position2[12]);

   7:     position[1] = (((position1[0] * position2[1]) + (position1[1] * position2[5])) + (position1[2] * position2[9])) + (position1[3] * position2[13]);

   8:     position[2] = (((position1[0] * position2[2]) + (position1[1] * position2[6])) + (position1[2] * position2[10])) + (position1[3] * position2[14]);

   9:     position[3] = (((position1[0] * position2[3]) + (position1[1] * position2[7])) + (position1[2] * position2[11])) + (position1[3] * position2[15]);

  10:     position[4] = (((position1[4] * position2[0]) + (position1[5] * position2[4])) + (position1[6] * position2[8])) + (position1[7] * position2[12]);

  11:     position[5] = (((position1[4] * position2[1]) + (position1[5] * position2[5])) + (position1[6] * position2[9])) + (position1[7] * position2[13]);

  12:     position[6] = (((position1[4] * position2[2]) + (position1[5] * position2[6])) + (position1[6] * position2[10])) + (position1[7] * position2[14]);

  13:     position[7] = (((position1[4] * position2[3]) + (position1[5] * position2[7])) + (position1[6] * position2[11])) + (position1[7] * position2[15]);

  14:     position[8] = (((position1[8] * position2[0]) + (position1[9] * position2[4])) + (position1[10] * position2[8])) + (position1[11] * position2[12]);

  15:     position[9] = (((position1[8] * position2[1]) + (position1[9] * position2[5])) + (position1[10] * position2[9])) + (position1[11] * position2[13]);

  16:     position[10] = (((position1[8] * position2[2]) + (position1[9] * position2[6])) + (position1[10] * position2[10])) + (position1[11] * position2[14]);

  17:     position[11] = (((position1[8] * position2[3]) + (position1[9] * position2[7])) + (position1[10] * position2[11])) + (position1[11] * position2[15]);

  18:     position[12] = (((position1[12] * position2[0]) + (position1[13] * position2[4])) + (position1[14] * position2[8])) + (position1[15] * position2[12]);

  19:     position[13] = (((position1[12] * position2[1]) + (position1[13] * position2[5])) + (position1[14] * position2[9])) + (position1[15] * position2[13]);

  20:     position[14] = (((position1[12] * position2[2]) + (position1[13] * position2[6])) + (position1[14] * position2[10])) + (position1[15] * position2[14]);

  21:     position[15] = (((position1[12] * position2[3]) + (position1[13] * position2[7])) + (position1[14] * position2[11])) + (position1[15] * position2[15]);

  22:     return matrix;

  23: }

Lo primero que se observa es que el código pasa a ser más críptico que el anterior, es decir, que ahora únicamente se tiene son los diferentes índices de position dentro de tres arrays, que representan las tres matrices con las que se esta trabajando en este momento.

Así que se ha pasado de,

matrix.M11 = (((matrix1.M11 * matrix2.M11) + (matrix1.M12 * matrix2.M21)) + 

(matrix1.M13 * matrix2.M31)) + (matrix1.M14 * matrix2.M41);

a esto:

position[0] = (((position1[0] * position2[0]) + (position1[1] * position2[4])) + 

(position1[2] * position2[8])) + (position1[3] * position2[12]);

Ya que ahora todas las posiciones de la matriz están almacenadas en un array de 16 posiciones lo que se tiene que hacer si se quiere acceder al valor M11 es acceder a la posición 0 de array, en el caso del valor M31 a la posición 8 de array y así sucesivamente.

Otras optimizaciones

Tamaño de los arrays

Si se tiene un array que tiene una propiedad length por la que se quiere iterar para realizar una acción por cada uno de los elementos del array, es recomendable no poner directamente el valor de myArray.length para comprobar si se ha llegado al final de array, sino guardar el tamaño del array en una variable y usar esta variable.

   1: var myArray = new Array();

   2:  

   3: for (var i = 0; i < myArray.length; i++) {

   4:     myArray[i] = i;

   5: }

   6:  

   7: var length = myArray.length;

   8: for (var i = 0; i < length; i++) {

   9:     myArray[i] = i;

  10: }

Cachear variables

Si durante la ejecución de un método se tiene variables que vamos a usar y estas variables son propiedades de un objeto, es mejor definirlas como variables en el ámbito del método que no referenciarlas desde el objeto original.

   1: var myObject =

   2:     {

   3:     name: 'luis',

   4:     company: {

   5:         name: 'PlainConcepts',

   6:         location: 'address'

   7:         }

   8:     };

   9:  

  10: var companyAddress = myObject.company.location;

  11: var company = myObject.company;

  12: companyAddress = company.location;

Espero que estas notas sobre optimización de JavaScript os sean útiles.

Luis Guerrero.