Tramite Emscripten (strumento per la compilazione in WebAssembly, argomento interessante che andremo sicuramente ad approfondire!!) ho fatto questo test interessante: tradurre codice C in JavaScript, da eseguire poi in locale tramite Node.js.
L'utilità di questa operazione può essere:
- portabilità: Node.js è disponibile multi-piattaforma, essendo codice JavaScript può quindi essere eseguito praticamente ovunque
- varie funzionalità: sono disponibili funzionalità di input/output per il sistema operativo, tramite specifiche API
- semplicità: Node.js è l'ideale per quanto riguarda la facilità di utilizzo, tramite un tool come Emscripten il codice C può essere compilato in JavaScript e poi eseguito
- perfetta integrazione con JavaScript: ovviamente Node.js è un applicativo (runtime system) JavaScript, quindi tradurre il codice C in JavaScript diventa poi più omogeneo e comodo da maneggiare, dovendo lavorare unicamente con JavaScript (così come gestire poi l'integrazione con altre librerie e strumenti)
Vediamo i vari step (nel mio caso tramite Linux Mint):
- installare Emscripten:
sudo apt install emscripten
- installare Node.js, se non è presente:
sudo apt install nodejs
- creare un file C
- compilare il file C in JavaScript (Node.js):
emcc nomefile.c
, ovviamente se siamo nella directory contenente il sorgente
- avviare Node.js da terminale:
node a.out.js
(a.out.js è il nome che viene dato di default al file C che viene compilato), verà creato anche un altro file di nome a.out.wasm (che occorre tenere, anche se noi ignoriamo il funzionamento)
- nel caso il comando tramite "node" non funzionasse, rifare la precedente operazione di compilazione in questo modo:
emcc nomefile.c --emrun
Fatto questo, ho voluto fare un interessante benchmark di prestazioni, per riprendermi a quanto fatto nella ormai celebre discussione Benchmark performance C, Python, MATLAB - GNU Octave, JavaScript, PHP.
Il codice utilizzato è lo stesso (quello denominato "C ottimizzato") e vediamo i risultati del tempo di esecuzione medio del codice, in questi casi:
- codice C
- codice convertito in Node.js (lato server)
- codice JavaScript puro (lato client)
- PHP 7.4
- PHP 8.1.2
Il codice in C è il seguente (ripeto, per avere confrontabilità, uguale a quanto fatto la volta precedente e anche stessa macchina):
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <math.h>
int main(){
clock_t start = clock();
register int N=1000000;
register float ris=0;
register float a=0;
register float b=20;
register float h=(b-a)/N;
register int i;
for(i=0;i<N;i++){
ris+=pow(1/(2*M_PI),0.5)*pow(M_E,(-0.5*(a+i*h)*(a+i*h)));
}
clock_t end = clock();
printf("%f\n",(float)(end-start)/CLOCKS_PER_SEC);
return 0;
}
Per quanto riguarda JavaScript puro (integrato in una pagina HTML), il codice è il seguente:
<html>
<script>
var start=performance.now();
var ris=0;
var N=10**6;
var a=0;
var b=20;
var h=(b-a)/N;
function f(x){
return 1/(2*3.141592653589793)**0.5*2.718281828459045**(-0.5*x**2)
}
for(i=0;i<N;i++){
ris+=h*f(a+i*h);
}
var end=performance.now();
document.write("ris="+ris+"<br>")
document.write(end-start+"ms")
</script>
</html>
Per quanto riguarda PHP invece abbiamo:
<?php
$start=microtime(TRUE);
$ris=0;
$N=10**6;
$a=0;
$b=20;
$h=($b-$a)/$N;
function f($x){
return 1/(2*3.141592653589793)**0.5*2.718281828459045**(-0.5*$x**2);
}
for($i=0;$i<$N;$i++){
$ris+=$h*f($a+$i*$h);
}
$end=microtime(TRUE);
echo "ris=".$ris."\n";
echo $end-$start."s";
?>
Vediamo ora i risultati:
- codice C: 0,043 secondi
- codice convertito in Node.js (lato server): 0,085 secondi
- codice JavaScript puro (lato client): 0,167 secondi (dal test precedente)
- PHP 7.4: 0,116 secondi (dal test precedente)
- PHP 8.1.2: 0,116 secondi (test attuale)
In conclusione, i risultati sono interessanti e in una situazione di questo tipo Node.js conferma un'ottima efficienza! Risulta infatti nettamente migliore di JavaScript puro lato client (il doppio più veloce!!) e anche migliore di PHP (versione 7.4 e 8.1.2) per un 35% circa. Il primo posto spetta sempre ovviamente a C, il doppio più veloce anche rispetto a Node.js per l'esecuzione di questo programma. Da considerare comunque l'interessante utilità e portabilità di Node.js.