[JAVA] WebAssembly est-il vraiment rapide? [Calcul numérique]

On dit que rendre la fonction JS WebAssembly rend le calcul plus rapide, mais j'ai aussi vu la théorie selon laquelle ce n'est pas le cas, donc je vais le vérifier brièvement. Ici, je vais me concentrer sur la vitesse du calcul numérique pur, comme la taille du fichier etc. Certains aspects sont exclus de la considération. Le titre est "[Calcul numérique]", mais il n'est pas prévu d'écrire une suite pour le moment.

Recherche précédente:

Suite à l'article "Comparaison des vitesses de calcul dans différentes langues", je reprendrai l'évaluation de la série Leibniz. Je ne sais pas si c'est calculé si souvent ... De plus, WebAssembly est comparé à la vitesse du code natif en termes de vitesse proche de l'exécution native.

[04-22 19: 40] Correction d'une erreur de code et en même temps augmenté le nombre de boucles à 1e9.

Exécution native

L'environnement est Debian 10.3 sur Win10 (WSL), processeur Intel Core i7-6700 à 3,40 GHz.

Langage C

Du point de vue du benchmarking de calcul numérique, je pense que la vitesse du langage C natif est toujours la norme, donc je vais commencer par le langage C.

leibniz.c


# include <stdio.h>
int main(void){
  int i;
  double sum = 0.0;
  int signum = 1;
  double denom = 1.0;
  
  for (i = 0; i<=1e9; i++) {
    sum += signum / denom;
    signum *= -1;
    denom += 2.0;
  };
  printf("Ans:%.16f\n", 4.0*sum);
  return 0;
}

Rust

leibniz.rs


fn main() {
    let mut sum: f64 = 0.0;
    let mut signum = 1;
    let mut denom = 1.0;

    for _ in 0..=1_000_000_000 {
        sum += signum as f64 / denom;
        signum *= -1;
        denom += 2.0;
    }
    println!("Ans:{:.16}", 4.*sum);
}

JS (Node.js)

leibniz.js


var sum = 0.0;
var signum = 1;
var denom = 1.0;

for (var i = 0; i<=1e9; i++) {
    sum += signum / denom;
    signum *= -1;
    denom += 2.0;
}
console.log('%d',4.0*sum);

résultat

Cette valeur est le résultat de son exécution une seule fois, mais elle a été répétée plusieurs fois et il a été confirmé que l'erreur n'était que le dernier chiffre ayant fluctué.

$ gcc --version
gcc (Debian 8.3.0-6) 8.3.0
Copyright (C) 2018 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

$ gcc leibniz.c -o leibniz
$ time ./leibniz
real    0m2.596s
user    0m2.578s
sys     0m0.000s
$ gcc -O3 leibniz.c -o leibniz
$ time ./leibniz
Ans:3.1415926545880506

real    0m1.136s
user    0m1.125s
sys     0m0.016s
$ gcc -O3 -march=native leibniz.c -o leibniz
$ time ./leibniz
Ans:3.1415926545880506

real    0m1.133s
user    0m1.125s
sys     0m0.000s
$ rustc --version
rustc 1.42.0 (b8cedc004 2020-03-09)
$ rustc leibniz.rs
$ time ./leibniz
Ans:3.1415926545880506

real    0m36.107s
user    0m36.094s
sys     0m0.016s
$ rustc -C opt-level=3 leibniz.rs
$ time ./leibniz
Ans:3.1415926545880506

real    0m1.124s
user    0m1.094s
sys     0m0.031s
$ rustc -C opt-level=3 -C target-cpu=native leibniz.rs
$ time ./leibniz
Ans:3.1415926545880506

real    0m1.181s
user    0m1.156s
sys     0m0.031s
$ node --version
v12.16.2
$ time node leibniz.js
3.1415926545880506

real    0m1.989s
user    0m1.953s
sys     0m0.047s

~~ Même avec l'optimisation, C est étonnamment lent ... Rust est environ 4 fois plus rapide, mais je me demande si j'ai fait une erreur. Rust sans optimisation était le plus lent, et Rust avec l'optimisation était le plus rapide. . ~~

Après l'optimisation, le langage C et Rust duraient environ 1,13 s. Le nœud est environ 75% plus lent.

Exécuter sur le navigateur

Je n'ai pas Google Chrome pour le moment, je l'ai donc essayé uniquement avec Mozilla Firefox 75.0 sur Win10.

JS

Je ne suis pas intéressé par l'heure du rendu HTML, etc., alors cliquez sur le bouton pour démarrer le calcul.

leibniz.html


<!DOCTYPE html>
<html>
  <head>
    <title>Leibniz Series</title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
  </head>

  <body>
    <button id="start">Start!</button>
    
    <script>
      function leibniz() {
          var sum = 0.0;
          var signum = 1;
          var denom = 1.0;

          for (var i = 0; i<=1e9; i++) {
              sum += signum / denom;
              signum *= -1;
              denom += 2.0;
          }
          console.log('%f',4.0*sum);
      }

      let button = document.getElementById("start");
      button.addEventListener("click", () => {
          const startTime = performance.now()
          leibniz();
          const endTime = performance.now();
          console.log(endTime - startTime); 
      });
    </script>
  </body>
</html>

Le résultat a été de ~~ 0,168 sec ~~ 1,660 sec. C'est seulement ~~ 30% ~~ 45% plus lent que le langage optimisé C. C'est effrayant rapidement ...

Rust (wasm)

src/lib.rs


use wasm_bindgen::prelude::*;

#[wasm_bindgen]
extern "C" {
    #[wasm_bindgen(js_namespace=console)]
    fn log(s: String);
}

#[wasm_bindgen]
pub fn leibniz() {
    let mut sum: f64 = 0.0;
    let mut signum = 1;
    let mut denom = 1.0;

    for _ in 0..=1_000_000_000 {
        sum += signum as f64 / denom;
        signum *= -1;
        denom += 2.0;
    }
    log(format!("Ans:{:.16}", 4.*sum));
}

index.html


<!DOCTYPE html>
<html>
  <head>
    <title>Leibniz Series</title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
  </head>

  <body>
    <button id="start">Start!</button>

    <script type="module">
        import init, {leibniz} from '/pkg/rust_wasm.js';

        let button = document.getElementById("start");
        button.addEventListener("click", async () => {
            const startTime = performance.now()
            await init();
            leibniz();
            const endTime = performance.now();
            console.log(endTime - startTime); 
        });
    </script>
  </body>
</html>

Le résultat était ~~ 0,035 sec ~~ 1,462 sec. ~~ C'est presque la même vitesse que Rust après optimisation (environ 10% plus lent) ~~ Plus rapide que JS et environ 30% plus lent que natif.

Le temps d'appeler la fonction d'initialisation ʻinit () ʻ est également mesuré, mais même la personne native mesure le temps entre la lecture du binaire et la fin du processus, ce qui rendra la mesure aussi égale que possible. Au fait, quand j'ai mesuré le temps après avoir appelé ʻinit`, il était d'environ ~~ 0,015 sec ~~ 1,381 sec.

Conclusion

Il a été confirmé que WebAssembly est certainement ~ ~ 5 fois plus rapide que JS dans le calcul numérique pur lui-même, et il est environ 15% plus rapide que natif, mais pas aussi rapide que natif. Étant donné que le code ne contient que quatre règles pour les entiers et les nombres à virgule flottante, les résultats peuvent changer à nouveau dans d'autres opérations (telles que l'alimentation), les appels de fonctions et l'accès à la mémoire. , Dans les études précédentes, il semble que le wasm n'ait pas encore gagné JS. ~~

Au fait, que se passe-t-il si vous appelez wasm dans Node.js? Je pensais essayer, mais je suis épuisé alors je le referai.

Recommended Posts

WebAssembly est-il vraiment rapide? [Calcul numérique]
Calcul numérique orienté objet
WebAssembly est-il vraiment rapide? [Calcul numérique]
Coopération Ruby / Rust (3) Calcul numérique avec FFI
Liaison Ruby / Rust (4) Calcul numérique avec Rutie
Coopération Ruby / Rust (5) Calcul numérique avec Rutie ② Veggie
Calcul numérique orienté objet
L'évaluation des courts-circuits est-elle vraiment rapide? Différence entre && et & en Java