Avevo già fatto un accenno al tool Emscripten (sudo apt install emscripten
, in ambiente Linux Debian-based) nella discussione Da C a Node.js. Vediamo ora un altro caso molto interessante, ovvero tramite lo standard WebAssembly (nominato anche WASM) possiamo (anziché scrivere a mano un codice decisamente incomprensibile, livello macchina) creare un nostro programma in C e con Emscripten lo compiliamo in un modo particolare, per generare un file HTML che mostra l'output visuale in una sandbox. Significa appunto eseguire codice C all'interno di una pagina web e in genere ha prestazioni molto superiori rispetto a JavaScript (vedi Benchmark performance C, Python, MATLAB - GNU Octave, JavaScript, PHP)! Vediamo una cosa alla volta 😊
- come esempio di riferimento ho preso proprio la discussione linkata, ovvero il calcolo numerico di un integrale, una semigaussiana, estremi 0 e 20, numero iterazioni pari ad un milione
- facciamo un confronto (tempo di esecuzione del codice) quindi fra tre codici diversi: codice C puro e ottimizzato, codice JavaScript puro e WebAssembly
- procedura per creare WebAssembly (WASM): dobbiamo compilare il nostro sorgente in C, in questo modo:
emcc nomefile.c -o nomefile.html
- avviare il file HTML contenente la sandbox di WASM: causa CORS policy, molto spesso occorre lanciare il comando
python3 -m http.server
(terminale nella directory in cui abbiamo i file), da browser andiamo all'indirizzo localhost:8000
(la porta di default, se non la cambiamo, è questa) e vedremo la directory con i nostri file, apriamo quindi nomefile.html che sarà appunto il file generato che contiene la sandbox che avvia il codice in WebAssembly e ne mostra l'output
Codice C:
#include <stdio.h>
#include <math.h>
#include <time.h>
int main() {
register float ris=0;
register float a=0;
register float b=20;
register unsigned int N=1000000;
register unsigned int i;
register float h=(b-a)/N;
clock_t start = clock();
for(i=0;i<N;i++){
ris+=h*exp(a+i*h);
}
ris/=sqrt((2*3.141592653589793));
clock_t end = clock();
printf("%f s\n", (float)(end - start) / CLOCKS_PER_SEC);
return 0;
}
Codice JavaScript (all'interno di una pagina HTML, risultati mostrati nella Console del browser):
let ris=0;
let N=10**6;
let a=0;
let b=20;
let h=(b-a)/N;
let start=performance.now();
function f(x){
return 2.718281828459045**(-0.5*x**2);
}
for(i=0;i<N;i++){
ris+=h*f(a+i*h);
}
ris/=(2*3.141592653589793)**0.5;
let end=performance.now();
console.log(0.001*(end-start),"s");
Il codice WebAssembly è talmente lungo e complicato, poco leggibile e con molti elementi SVG in blocco, che non avrebbe senso riportarlo. Ciò che conta è la procedura per generarlo.
Risultati:
- codice C: tempo di esecuzione 0,006761 secondi
- codice JavaScript: tempo di esecuzione 0,044 secondi
- codice WebAssembly: tempo di esecuzione 0,017 secondi
Questo significa che C è ovviamente sempre al primo posto, in questo test è risultato 6,5 volte più veloce rispetto a JavaScript e 2,5 volte più veloce rispetto a WebAssembly. Significa che WebAssembly è risultato 2,59 volte più veloce rispetto a JavaScript! Questo è interessante, lo stesso codice scritto in modo tradizionale con JavaScript avrebbe richiesto 2,59 volte il tempo richiesto da WASM. Chiaro che in questo esempio abbiamo poche operazioni e quindi un tempo irrisorio, ma la proporzione rende l'idea!
Vediamo infine una serie di immagini, output dei programmi.