Optimizar PHP con C y C++

Optimizar PHP con C y C++

Este artículo se compone de:

  1. Introducción
  2. Implementación de un caso real
  3. Resultados
  4. Conclusiones
  5. Comentarios
  6. Futuro: la web 3d

INTRODUCCIÓN C/C++ A TRAVÉS DE PHP:

He estado investigando después de leer una noticia enviada por WebGranada que decía que los chicos de Facebook han hecho una herramienta que transforma el código de PHP a C++ y que al realizar las tareas con lenguaje de la máquina (bajo nivel),como es lógico ,es más rápido ,sin embargo, me preguntaba hasta que punto es más rápido…como no he visto nada serio que me convenza me he decidio por hacer mis propias pruebas con este tipo de construcción de webs a través de lenguaje C/C++ en lugar de PHP, e incluso escribir el código en PHP y pasarlo a C++…

De hecho, esto ya existía desde hace tiempo,…cualquiera puede implementar una herramienta como: The PHP to C++ Translation tool…que no es más que un programa en C++ que analiza sintácticamente un programa en PHP y lo pasa a C++ para que se pueda compilar y colgar en el servidor , por ejemplo la plataforma SWAD está escrita en este lenguaje y la especificación CGI…pero lo difícil es hacerlo bien, como casi siempre jeje

El debate que existe en Internet, ronda, en parte, a la cuestión de que no se va a conseguir una verdadera experiencia de mejora de velocidad en cuanto a interacción y carga de páginas, sobre todo en las que no tienes más de un número máximo, digamos, de más de miles de usuarios…ni tampoco, a la hora de conectarse a la base de datos ya que es algo que no suele mejorarse demasiado de una tecnología a otra ,no más de un 20%…

Sí que se notará cuando se realicen búsquedas y otras operaciones complejas con los datos ya que un programa en C++ está optimizado en memoria mucho más que PHP,siempre y cuando el programador haya llevado cuidado a la hora de escribir el código…además, al funcionar PHP como un módulo (extensión) del Servidor (por ejemplo Apache), tiene sus propios límites de memoria y ciclos de ejecución, darle los datos de entrada a un programa en C++ y que realice tareas complejas es más eficiente según qué casos…

IMPLEMENTACIÓN DE UN CASO REAL


Aquí propongo un ejemplo sencillo para demostrar en qué casos es mejor C++ que PHP, para empezar, si queremos ir probando todo lo que vamos haciendo, podemos crearnos un directorio en nuestro servidor llamado  «c++», recordar añadirlo al .htaccess:
RewriteRule ^c\+\+(.*) – [PT]
…para que Apache no procese ese directorio con otra regla, (si le ponemos un password mejor)…ahora, un script sencillo para ejecutar programas como si estuviéramos en una consola:
shell.php:

 
<html>
<head>
<script type="text/javascript">
function loadXML(_cmd) {
url="/c++/shell_exec.php";
if (window.XMLHttpRequest) {
// codigo para IE7+, Firefox, Chrome, Opera, Safari
xmlhttp=new XMLHttpRequest();
} else {// para IE6, IE5
xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
}
xmlhttp.open("GET",url+"?cmd="+_cmd,false);
xmlhttp.send(null);
document.getElementById('test').innerHTML=
 xmlhttp.responseText;
if (document.frmT.b.checked)
document.frmT.cmd.value="";
document.frmT.cmd.focus();
}
 
</script>
<title>Exec</title>
</head>
 
<body>
 
<div id="test">
<h2>CONSOLA</h2>
</div>
<form name="frmT" onsubmit="
loadXML(document.frmT.cmd.value); 
return false;">
<input type="text" name="cmd" />
<input type="checkbox" name="b" value="1" id="b"/>
<label for="b">Borrar</label>
</form>
<button type="button" onclick="loadXML(document.frmT.cmd.value);
 return false;">run</button>
</body>
</html>

Este código llamará, usando el truquito AJAX, al siguiente fichero PHP (ojo!: más peligroso):

shell_exec.php

 
$t = trim($_GET['cmd']);
if (empty($t)) die("Escribe un comando chatin");
 
echo $_GET['cmd']."<br />";
echo str_replace("\n","<br />",passthru($_GET['cmd']));


Ahora podemos utilizarlo invocando directamente la URL:


Ya podemos probar nuestros programas en C++ subiéndolos y compilándolos online (si nuestro servidor tiene gcc), o bien, haciendo una llamada al programa compilado directamente por nosotros en local…Para los que son más de Windows, podéis instalar cygwin y descargar las librerías para hacer compilaciones de Linux: crosscompilers for cygwin (existe una versión para 64bits de las librerías), descomprimiéndolas en el directorio cygwin para poder llamar a g++-linux, por Ejemplo para compilar The PHP to C++ Translation tool:

$ g++-linux -o php2cpp php2cpp.cpp

No debe de darnos ningún error o advertencia…ahora podemos usar el comando desde el servidor para pasar un fichero en lenguaje php a un fichero en lenguaje C++ mediante:

./php2cpp entrada.php salida.cpp

de nuevo debemos compilar la salida para obtener un binario…

$g++-linux -o salida salida.cpp

Una vez que tenemos los binarios podemos usar la consola improvisada en el navegador, para dar permisos de ejecución a aquellos mediante
chmod 750 salida

Y entonces podemos cargar el programa desde el servidor como antes (una vez subido,claro)
./salida

Un ejemplo básico de C,sería una búsqueda binaria de datos,por ejemplo de triadas de vértices (x,y,z) de un conjunto de polígonos, dentro de un conjunto mayor,pero para no complicarnos, usaremos un array unidimensional, de valores dobles, un código como el siguiente nos vale:

#include <stdio .h>
#include <time .h>
#define tamanio 500
int busquedaBinaria(double *matrizOrdenada, int primero,
int ultimo, double llave);
 
int main ()
{
clock_t t_antes,t_despues;
double t,aux;
int i,k;
srandom(time(0));
 
for (k=1000; k< =3000; k+=1000){
t_antes = clock();
double *m =  malloc (sizeof(double)*tamanio*k);
for (i=0; i<tamanio*k; i++) {
m[i] = (double) (rand()%1000)+1;
}
 
aux = busquedaBinaria(m,0,tamanio*k,m[(int) (tamanio*k)/2]);
free(m);
t_despues = clock();
t = (((double)(t_despues - t_antes)) / CLOCKS_PER_SEC);
printf("<br><h1>%f</h1>segundos",t);
}
return 0;
}
 
/**
* @brief function busquedaBinaria:
*     Busca desde matrizOrdenada[primero]..
       matrizOrdenada[ultimo] por llave.
*  @returns: indice del elemento encontrado, la llave,
*          en otro caso -(indice donde se puede insertar)-1.
*  @param int matrizOrdenada array de sorted (ascending) values.
*  @param int primero
*  @param int ultimo
*  @llave double llave
*  @return int
*/
int busquedaBinaria(double *matrizOrdenada, int primero,
int ultimo, double llave) {
while (primero < = ultimo) {
// calcula el punto medio
int mid = (primero + ultimo) / 2;
if (llave > matrizOrdenada[mid])
// repite la busqueda si no esta en la mitad
primero = mid + 1;
else if (llave < matrizOrdenada[mid])
// repite la busqueda en la otra mitad
ultimo = mid - 1;
else // encuentra el item, lo devuelve
return mid;
}
// no encontrado
return -(primero + 1);
}

Para compilar con gcc…

Ahora el mismo programa compilado con gcc, pero en PHP:

o2.php:

 
srand(null);
$tamanio = 500;
for ($k=1000; $k< =3000; $k+=1000){
$t_antes = microtime(true);
$m =  array();
for ($i=0; $i<$tamanio*$k; $i++) {
$m[i] = doubleval(rand(1,1000));
}
$aux = busquedaBinaria($m,0,$tamanio*$k,
$m[($tamanio*$k)/2]);
unset($m);
$t_despues = microtime(true);
$t = $t_despues - $t_antes;
printf("<br><h1>%f</h1>segundos",$t);
}
 
/**
* @brief function busquedaBinaria:
*     Busca desde matrizOrdenada[primero]..
  matrizOrdenada[ultimo] por llave.
*  @returns: indice del elemento encontrado, la llave,
*          en otro caso -(indice donde se puede insertar)-1.
*  @param int matrizOrdenada array de sorted (ascending) values.
*  @param int primero
*  @param int ultimo
*  @llave double llave
*  @return int
*/
function busquedaBinaria(&$matrizOrdenada,
$primero, $ultimo, $llave) {
while ($primero < = $ultimo) {
$mid = ($primero + $ultimo) / 2;  // calcula el punto medio
if ($llave > $matrizOrdenada[$mid])
$primero = $mid + 1;  // repite la busqueda si no esta en la mitad
else if ($llave < $matrizOrdenada[$mid])
$ultimo = $mid - 1; // repite la busqueda en la otra mitad
else
return $mid;     // encuentra el item, lo devuelve
}
return -($primero + 1);    // no encontrado
}

RESULTADOS


Resultados C/C++:

Resultados PHP:


He elegido las búsquedas ya que estas operaciones de ordenación, concatenación de cadenas, etc. son más rápidas que en PHP y se pueden ver en los resultados.

Gráficas:

La gráfica muestra la diferencia de eficiencia entre C/C++ y PHP a la hora de ejecutar algoritmos complejos de búsqueda y ordenación…

Esta gráfica corresponde al benchmark entre C++ y PHP: tiempo usado, memoria usada ,código usado versus velocidad y tamaño comparando los programas más rápidos de PHP

CONCLUSIONES


Después de saber que C y C++ es mucho más rápido que PHP para manejar la memoria y realizar operaciones complejas como algoritmos de ordenación, podemos consultar algunas webs que han hecho análisis más profundos y sacan sus propias conclusiones al respecto:

  • C++ vs Java vs Python vs Ruby : la primera impresión, analiza como he hecho yo, un programa en todos estos lenguajes añadiendo Java, Ruby y Python,realizando comparativas de lo mejor y lo peor de cada uno, por ejemplo la poca información de Ruby a la hora de mostrar errores en tiempo de ejecución y compilación, o los requerimientos de cada sintaxis, para poder hacer un programa, que hace que baje el tiempo de producción de una solución por ejemplo teniendo que recordar que en Python hay que escribir «self» y aumente el coste del proyecto…acaba concluyendo que Java es más productivo que C++ pero nomás que Ruby o Python que además tienen un código más bonito…, mencionando que depende de nuestros conocimientos de estos dos últimos la curva final,…como colofón, los comentarios [flame!]
  • Tamaño y velocidad: una serie de análisis de lenguajes: este es uno de esos análisis de titanes, compara cualquier lenguaje, como si de un comparador de móviles o coches se tratara, dando datos y gráficas de las estadísticas de ejecución, además provee de enlaces con los ficheros con los que se realizaron las pruebas.

Como conclusión personal, pienso en C siempre que necesite un algoritmo que consuma mucho tiempo o mucho espacio en memoria del servidor, por supuesto, este tipo de programas se usan en empresas para realizar diversas operaciones…si bien, no está mal recordar que si lo que necesitamos en un listado de productos con condiciones únicas podemos utilizar un algoritmo de C o C++ embebido en PL/SQL tal como dicta el manual de Oracle…

¿Casos reales? Por ejemplo, se podría implementar un algoritmo para una búsqueda en una red social como Badoo.com, si necesitamos encontrar todas las personas con los ojos azules, aplicarle a la primera foto de cada contacto un algoritmo, primero con el comando wget() (o con PHP) nos descargamos la imagen o un rango de ellas, a un directorio de caché, llamamos al programa en C++ (exec() ,CGI, o lo que sea) que analiza la/s foto/s y nos dá una probabilidad de que los ojos azules se encuentren en ella y basándose en estos datos, marcar el perfil de la persona pasado al script como apto para añadirlo a la lista de candidatos de nuestra base de datos…esto en PHP podría tardar pues…imaginaos jaja

FUTURO: LA WEB 3D


Ni que decir tiene que el futuro nos depara webs en 3D programadas en C++ o las generaciones siguientes a este como LUA que transforma internamente el código a C++…y esto amigos, si que se nota comparado con un applet de Java :]

Abrir grecia del futuro

COMENTARIOS

De hecho existen algoritmos que estudian su probabilidad de éxito en el marketing…no es nada descabellada la idea :)

Es la hora de comentarme o insultarme, lo que más os guste o lo que más rabia os de :D

Artículos relacionados:

  1. Friki_man dice:

    Ah, te podemos insultar?

    C…….. de ………

    T….. l……. p….

    y además….. pfoaksjdñlkrñokhasfd oink!!

    Fdo:
    El jaker del museo del prado.

  2. Jorge dice:

    No me compila el archivo c++ me tira estos errores al compilarlos con gcc
    c++.cpp: In function ‘int main()’:
    c++.cpp:12: error: ‘srandom’ was not declared in this scope
    c++.cpp:16: error: ‘malloc’ was not declared in this scope
    c++.cpp:18: error: ‘rand’ was not declared in this scope
    c++.cpp:22: error: ‘free’ was not declared in this scope

    • Juan Belón dice:

      Hola Jorge, son problemas típicos de usar un lenguaje u otro, como ves yo he usado g++ de Linux para cygshell sobre Windows, tienes que utilizar la instrucción g++ y cambiar las lineas de referencias a librerías quitando los «.h» para que sea c++, añadiendo un include de la librería cstdlib.
      En cualquier caso, este problema es muy típico en C y una búsqueda en internet te hubiera solucionado la duda en un periquete jeje
      Un saludo

    • Enrique dice:

      jejeje!!
      #include <stdlib.h>

  3. El Yugoslavo dice:

    Hola muy interesante el ejemplo, cuando lo ejecuto directo en la consola funciona perfecto, sin embargo, en el navegador me muestra el mismo texto que le paso al textfield y no me muestra los segundos de ejecución.

    Gracias de antemano.

    • Juan Belón dice:

      tendrás que comprobar si tu servidor tiene permisos de ejecución de comandos comprometidos como exec( ) o passthru( ), esto suele estar en la información que devuelve la función phpmyinfo(); y tiene que ver con el parámetro de configuración safe_mode (y safe_mode_exec_dir) que como verás, está obsoleta a partir de versiones 5.3.0 …

      Mira esos parámetros, puedes intentar probar el script en local mientras …un saludo

  4. puentesdiaz dice:

    La verdad, te felicito por este analisis. Me ahorraste mucho tiempo, pues siempre es bueno comprobar las cosas por uno mismo… Me repito … muy buen aporte a la comunidad

  5. Gildus dice:

    Buen articulo,

    El C++, tiene dos parametros en el main (int argc, char *argv[]), y en java tambien tiene pero es solo un parametro sino me equivoco, bueno la idea es que seria mas buena la interaccion con esos parametros y procesarlos en el C++, y en el PHP solo lo ejecutamos o le enviamos los parametros.

    Saludos
    Gildus

    • Juan Belón dice:

      Hola Gildus, en Java depende de como lances el programa, si es un applet o un webservice los parámetros serían como en PHP ya que es el navegador quien hospeda la máquina virtual, en el caso de lanzarlo como línea de comandos con java o el compilador tienes el mismo caso que en C.
      No cambia mucho el tiempo de los ejemplos ya que el procesado de dichos parámetros es trivial…es decir, tendrías que cambiar algo más para poder medirlo, te invito a realizar las pruebas y compartirlas ;-)

  6. Danielrve dice:

    Saludos, qué se puede hacer en el caso de base de datos y JSON y hacer un listener http, osea en vez de ejecutar con exec dejarlo en un listener http:8080

    Q opinan?

    • Juan Belón dice:

      Daniel, si creas un servidor de sockets en el puerto 8080 y quieres lanzarlo ,en lugar de un exec usa una tarea cron,al ser un programa en c++ será más rápido que con uno en PHP, luego ya depende de lo que hagas en ese programa…

  7. Enrique dice:

    por si alguien prueba y le sale error en esta parte:
    double *m = malloc sizeof(double)*tamanio*k);

    su corrección:
    double *m = (double*) malloc(sizeof(double)*tamanio*k);

 

footer
jbelon © | sitemap.xml