viernes, 27 de enero de 2012

Comparativa entre PHP, NodeJS y .NET #php, #nodejs, #dotnet


Inciso:
Este post iba a ser inicialmente una respuesta al comentario de Guillermo en el anterior post en forma de otro comentario, pero como me estaba extendiendo demasido he decidido escribir un post completo.

PHP vs Node.JS vs .NET

En el anterior post escribí sobre la nueva función de PHP5.4 que integra un servidor HTTP con un ejemplo sencillo para windows.
En otro post escribí sobre cómo iniciarse en Node.JS y las ventajas que ofrecía de cara a la concurrencia.
Y en diferentes posts he ido hablando sobre .NET 4.5, y hasta monté un servidor FASTCGI asíncrono para .NET.

Entonces, ¿qué lenguaje es mejor? ¿qué lenguaje uso yo?




Tabla comparativa:


PHP Node.JS .NET  4.5 (C#)
Madurez MaduroEn pañalesMaduro
Asíncrono No Sí, únicamente 4.5 y no forzado
Permite procesos de larga duración (no leaks) A partir de 5.3, parcialmente Sí, está pensado para ello
Tipado estático No, pero tiene typehinting NO SÍ, el mejor tipado estático de su clase muy por encima de Java: rendimiento y asistencia increíble en IDE.
Ejecución Intérprete (aunque con Phalanger se puede conseguir JIT, pero sigue sin tener tipado estático) JIT de V8, muy bueno pero sin tipado estático que limita el rendimiento.JIT de .NET. Uno de los mejores que hay en máquinas virtuales, muy por encima del de Java.
Fácil acceso para empezar con pocos recursos PHP casi viene instalado por defecto y lo tenemos en hasta servidores web gratuitos. ¿Hosting con Node.JS?  Já. Solo planteable en dedicados y control casi total. Limitado. Pocos servidores con soporte .NET, aunque haberlos haylos. Gratuitos más complicado.
Deploy Upload & Go Ejectar un comando en un server dedicado Hay que hacer bastantes más cosas con las soluciones existentes
Libraría básica / peso / complejidad Librería en C con infinidad de funciones y extensiones. No demasiado lightweight. Librería de Javascript con un API asíncrono en C++ y casi todo el resto programando en JavaScript. Bastante lightweight. Un framework integrado entero con una gran parte programada en .NET y lo básico en C. Bastante heavy.
Potencia del lenguaje Muchas carencias importantes. Carencias importantes pero con flexibilidad (y peligro) que no ofrece PHP. Muy potente
Asistencia de IDE El PDT basado en eclipse tiene muchas limitaciones por culpa de Zend que lo capa para ofrecer valor añadido a su producto. El Zend Studio está mucho mejor, pero es de pago. Mantiene bastantes más tipos y ayuda a detectar problemas en tiempo de edición. Los intentos de IDE son un fiasco; el lenguaje es demasiado flexible. Lo mejor que he visto es el VJET de ebay y solo soporta el core básico de Node.JS, hay que hacerse "headers para todo". La asistencia de IDE en javascript es la peor de su clase. Intenta suplirlo con APIs pequeñas y fáciles pero a largo plazo y con proyectos grandes eso no funciona. Visual Studio Express (Gratis)
La mejor asistencia en código que hay. Con templates sólo se pierden tipos con reflexión y no siempre y sin perder rendimiento ni exceso de memoria con la covariancia.
Unittesting Hay herramientas, pero al estar tan extendido mucha gente que lo usa no tiene experiencia y pocos proyectos opensource hacen uso de unittesting. Tiene soporte nativo limitado, pero se suele hacer bastante. Es la única forma de hacer cosas estables con algo tan flexible. La gente que se embarca en node.js suele tener más experiencia. Visual Studio integra el unittesting de base y hace que crear unittesting sea una gozada. Permite ejecución selectiva a nivel de método, clase y paquete. Además genera informes y todo integrado y bien planteado.
Uso asíncrono (I) Prácticamente nulo. Hay que usar libevent únicamente disponible en *nix. La escritura de funciones anónimas es muy tediosa por requerir el uso de "use" para acceder a variables del ámbito superior. Las funciones anónimas permiten el acceso al ámbito superior, aunque la indentación acaba siendo un problema. Escribir funciones a parte hace que seguir el flujo acabe siendo una tarea infernal. El asíncrono tradicional, pese a que la escritura de funciones anónimas es más lightweight generalmente, es más tediosa por el tipado estático. También hay acceso al ámbito superior. Con .NET 4.5 todo cambia. Escritura asíncrona secuencial.
Uso asíncrono (II) Soporte en pañales limitado a *unix. No pensado para ello y poco soporte. Tedioso y engorroso. Se pierden los stacktraces. Soporte maduro y multiplataforma, y pensado para ello. Algo tedioso y se pierden los stacktraces. Soporte todavía no madurado. Multiplataforma con Mono 2.10. Se mantienen stacktraces Es infinitamente más fácil seguir el flujo siendo secuencial como el asíncrono. No se puede usar asíncrono en bloques finally. Hay que usar async/await. Más flexible y potente, permite hacer programación asíncrona mal hecha, aunque el IDE intenta ayudar el hecho de que permita hacerlo mal es su peor baza. Las nuevas APIs soportarán únicamente I/O asíncrono, pero al venir tarde, hay mucho codebase que dejará de poderse usar para hacer bien el asíncrono.
Paquetes pear, peclnpm nuget
FFI Hay que hacer un wrapper en C. Extender PHP es EL INFIERNO. Montones de macros inusables, bugs, problemas... Solo hay que ver lo que les ha costado sacar la 5.4 para saber lo difícil que es tocar las tripas de PHP. Hay que hacer un wrapper sencillito en C++. Con V8 es muy fácil hacer wrappers. Pero hay que hacerlos y compilarlos. Permite llamar a librerías de forma nativa simplemente añadiendo una línea en el código.
Posibilidades de optimización a nivel de ejecución Typehinting que no ayuda y tipado dinámico. Un único ámbito en el que buscar variables, más engorroso pero más eficiente. Gente poco competente haciendo el runtime de PHP. Tipado completamente dinámico, múltiples ámbitos en los que encontrar las variables. Cracks haciendo el JIT de javascript. Templates reales con covariancia, tipos lightweight sin necesidad unboxing, acceso punteros con código unsafe. Rendimiento cercano a C/C++ sin optimizar  y en la misma escala de magnitud.


Comparativa detallada y comentarios:

En relación al comentario de Guillermo:

No creo que se pueda "comparar" esto (PHP con soporte de servidor HTTP) con Node.JS. Te explico:
Para empezar esto integra un framework y un sistema de templates. PHP además es interpretado mientras que NodeJS utiliza V8 que tiene un JIT. El V8 le da mil vueltas en cuanto a rendimiento a PHP. Algo más comparable sería usar Phalanger (http://www.php-compiler.net/).

En cuanto a concurrencia tampoco hay color. Mientras que NodeJS utiliza I/O asíncrono, PHP utiliza I/O síncrono. NodeJS puede atender varias peticiones a la vez con un solo thread, mientras que PHP para atender varias peticiones necesita no solo threads distintos sino procesos distintos completamente. Pools de FastCGI o forks del servidor web. Por si fuera poco en PHP se ejecuta toda la inicialización del código y la carga de datos en cada puñetera petición, mientras que en NodeJS tienes todo cargado en memoria (que has cargado una sola vez) antes de gestionar la petición: menos memoria, mayor rendimiento... etc.
A partir de PHP5.3 empezaron a solucionar memory leaks permitiendo, y un programador montó un sistema para usar I/O asíncrono con libevent en PHP. Tuvo que programarse TODO desde 0, incluyendo conectores asíncronos a MySQL, redis y demás. Como está prácticamente solo, los conectores son pocos y limitados PHP-Daemon (https://github.com/shaneharter/PHP-Daemon). En NodeJS como va asíncrono, aunque tocó hacerse todos los conectores a mano, tiene un soporte brutal de la comunidad.

NodeJS al ser monothread hace que la gente tienda más a hacer escalabilidad horizontal. Montar varios node.js en un mismo ordenador para hacer mayor uso de threads, que está más en consonancia con el cloudcomputing y con el ir añadiendo elemenos on demand. En PHP al poder usar todos los recursos del ordenador sin hacer nada, hace que se descuide más el escalado horizontal.

Sin embargo, la comunidad de PHP es mucho mayor, es más maduro, tiene más soporte de IDE para escritura de código y el deploy es mucho más sencillo. La programación síncrona es mucho más sencilla, más mantenible y fácil de seguir. Simplemente por el hecho de que la escritura es secuencial.

.NET intentó solucionar este problema en la versión 4.5 con sus asíncronos, que tienen un muy buen planteamiento. Para empezar, permite programación asíncrona con código secuencial, multiplexing, estados asíncronos MEGA lightweights (una estructura simplemente con las variables que serían las locales) y propagación de excepciones (manteniendo el stacktrace en estados asíncronos). Vamos, el no va más. C# es un lenguaje muy bien planteado. Tiene un par de cosas que se plantearon mal y ya no pueden arreglar sin romper código, pero son muy menores y los que han ido diseñando el lenguaje son unos cracks.
El problema de los asíncronos es que su uso no es forzado y se puede programar usando síncronos. Se intenta guiar un poco, pero se puede hacer mal. Y eso es un problema. Porque si estás haciendo un servidor que tiene que ser asíncrono para que no te lo tiren y hay puntos del código que por un error pueden no ser asíncronos, puedes tirarte de los pelos.

Hay otras formas de hacer programación monothread con multiplexing: programación orientada a procesos, green threads, fibers. Viene siendo como el asíncrono de .NET pero sin necesidad de que las funciones guarden un estado propio. Sin embargo, y pese a ser mucho más lightweight que los threads, cada fiber requiere guardar todos los registros y mantener una pila própia. Con lo cual consumen mucha más memoria que la solución asíncrona de .NET. Pero sigue siendo una opción a investigar y que se puede llegar a implementar en .NET. De hecho en MONO ya está implementado con los continuations. Molaría que fuese estándar también con el runtime de windows, así como el soporte SIMD de mono que es genial.

Conclusiones:

  • PHP es mucho menos eficiente, pero es más fácil de programar para proyectos grandes.
  • Node.JS es una buena iniciativa, pero javascript es lejos de ser el lenguaje ideal para cosas grandes. Yo esperaré a Node.Dart, que sin duda acabará saliendo. Y si no lo saca nadie, lo sacaré yo :)
  • El soporte asíncrono de .NET es bueno, pero está en pañales y falta ver si habrá alguna forma de forzar el uso asíncrono correcto sin miedo a cagarla (de cara a servidores).
Yo por ahora sigo usando PHP. Aunque eso no hace que me guste más PHP con todas las cosas malas que tiene; y tampoco me impide que siga experimentando tecnologías hasta que encuentre alguna en la que esté cómodo y solucione problemas actuales.

4 comentarios:

  1. Excelente articulo amigo, te felicito, yo he realizado varios proyectos en Java y .Net y actualmente he iniciado un proyecto nuevo en Node.JS haciendo uso de http://expressjs.com/ como framework base y creo que los resultados han sido geniales, el rendimiento y la velocidad son impresionantes. En cuando al IDE para Node.JS se puede utilizar WebStorm que trae soporte completo para node.js incluyendo la posibilidad de hacer debug.

    ResponderEliminar
  2. Gracias :) ¡Se ve excelente WebStorm.

    Yo actualmente estoy usando Typescript (con Visual Studio) + Node.Js + ExpressJs también.

    ResponderEliminar
  3. Excelente artículo!!!

    ResponderEliminar
  4. Excelente aporte, aunque por el momento prefiero PHP, estoy empezando a incursionar en Node.JS+Express.

    ResponderEliminar