Maison > interface Web > js tutoriel > asm.js & webassembly-WEB calcul haute performance

asm.js & webassembly-WEB calcul haute performance

php中世界最好的语言
Libérer: 2017-11-18 14:46:04
original
2237 Les gens l'ont consulté

Je vous ai déjà présenté le calcul haute performance WebWorkers-WEB. Les connaissances sur javascript sont très intéressantes. Aujourd'hui, je vais donc vous parler de la relation entre asm.js & webassembly et le calcul haute performance WEB. avant cela, nous devons le résoudre. Il existe deux méthodes pour le calcul haute performance, l'une consiste à utiliser WebWorkers simultanément et l'autre consiste à utiliser un langage statique de niveau inférieur.

En 2012, l'ingénieur Mozilla Alon Zakai a eu une idée soudaine alors qu'il faisait des recherches sur le compilateur LLVM : le C/C++ peut-il être compilé en Javascript et essayer d'atteindre la vitesse du code natif ? Il a donc développé le compilateur Emscripten, qui est utilisé pour compiler du code C/C++ en asm.js, un sous-ensemble de Javascript. Les performances représentent près de 50 % du code natif. Vous pouvez jeter un œil à ce PPT.


Plus tard, Google a développé Portable Native Client, qui est également une technologie qui permet aux navigateurs d'exécuter du code C/C++. Plus tard, je suppose que tout le monde a pensé qu'il était impossible de faire ce qu'il voulait. En fait, Google, Microsoft, Mozilla, Apple et d'autres grandes entreprises ont travaillé ensemble pour développer un projet de format binaire et texte universel pour le Web, qui est WebAssembly. le site officiel est :


WebAssembly ou wasm est un nouveau format portable, efficace en termes de taille et de temps de chargement, adapté à la compilation sur le Web.


WebAssembly est actuellement conçu comme un standard ouvert par un groupe communautaire du W3C qui comprend des représentants de tous les principaux navigateurs.


Ainsi, WebAssembly devrait être une solution prometteuse Bon projet. Nous pouvons jeter un œil au support actuel du navigateur :

asm.js & webassembly-WEB calcul haute performance



Installer Emscripten


Visitez https://kripken.github.io/emscripten-site/docs/getting_started/downloads.html


1. Téléchargez le SDK correspondant à la version de la plateforme


2 Obtenez la dernière version de l'outil via emsdk

# Fetch the latest registry of available tools.
./emsdk update
 
# Download and install the latest SDK tools.
./emsdk install latest
 
# Make the "latest" SDK "active" for the current user. (writes ~/.emscripten file)
./emsdk activate latest
 
# Activate PATH and other environment variables in the current terminal
source ./emsdk_env.sh
Copier après la connexion


3. Ajoutez ce qui suit à la variable d'environnement PATH

~/emsdk-portable
~/emsdk-portable/clang/fastcomp/build_incoming_64/bin
~/emsdk-portable/emscripten/incoming
Copier après la connexion


Autres


<🎜. >J'exécute Parfois, j'ai rencontré une erreur indiquant que la version de LLVM était erronée. Plus tard, j'ai simplement configuré la variable LLVM_ROOT en vous référant à la documentation. Si vous ne rencontrez aucun problème, vous pouvez l'ignorer.

LLVM_ROOT = os.path.expanduser(os.getenv(&#39;LLVM&#39;, &#39;/home/ubuntu/a-path/emscripten-fastcomp/build/bin&#39;))
Copier après la connexion


5. Vérifiez s'il est installé


Exécutez emcc -v. les informations suivantes apparaîtront. Informations :

emcc (Emscripten gcc/clang-like replacement + linker emulating GNU ld) 1.37.21
clang version 4.0.0 (https://github.com/kripken/emscripten-fastcomp-clang.git 974b55fd84ca447c4297fc3b00cefb6394571d18) (https://github.com/kripken/emscripten-fastcomp.git 9e4ee9a67c3b67239bd1438e31263e2e86653db5) (emscripten 1.37.21 : 1.37.21)
Target: x86_64-apple-darwin15.5.0
Thread model: posix
InstalledDir: /Users/magicly/emsdk-portable/clang/fastcomp/build_incoming_64/bin
INFO:root:(Emscripten: Running sanity checks)
Copier après la connexion
Bonjour WebAssembly !


Créer un fichier hello.c :

#include <stdio.h>
int main() {
  printf("Hello, WebAssembly!\n");
  return 0;
}
Copier après la connexion
Compiler le code C/C++ :

emcc hello.c
Copier après la connexion
La commande ci-dessus générera un fichier a.out.js, que nous pourrons exécuter directement avec Node.js :

node a.out.js
Copier après la connexion
Sortie

Hello, WebAssembly!
Copier après la connexion
Afin d'exécuter le code dans la page Web, l'exécution de la commande suivante générera deux fichiers, hello.html et hello.js. Le contenu de hello.js et a.out.js est exactement. le même.

emcc hello.c -o hello.html<code>
 
➜  webasm-study md5 a.out.js
MD5 (a.out.js) = d7397f44f817526a4d0f94bc85e46429
➜  webasm-study md5 hello.js
MD5 (hello.js) = d7397f44f817526a4d0f94bc85e46429
Copier après la connexion
Ensuite ouvrez hello.html dans le navigateur, vous pouvez voir la page

asm.js & webassembly-WEB calcul haute performance

Les codes générés précédemment sont tous des asm.js , Après tout, Emscripten a été utilisé pour la première fois par l'auteur Alon Zakai pour générer asm.js, il n'est donc pas surprenant que asm.js soit affiché par défaut. Bien sûr, wasm peut être généré via l'option, et trois fichiers seront générés : hello-wasm.html, hello-wasm.js, hello-wasm.wasm.

emcc hello.c -s WASM=1 -o hello-wasm.html
Copier après la connexion
Ensuite, le navigateur a ouvert hello-wasm.html et a trouvé une erreur TypeError : Failed to fetch. La raison en est que le fichier wasm est chargé de manière asynchrone via XHR et que l'accès à celui-ci à l'aide de

file://// signalera une erreur, nous devons donc démarrer un serveur.

npm install -g serve
serve
Copier après la connexion
Ensuite, visitez http://localhost:5000/hello-wasm.html et vous pourrez voir les résultats normaux.

Appel de fonctions C/C++

Les précédents Hello, WebAssembly ! sont tous tapés directement par la fonction principale, et le but de l'utilisation de WebAssembly est le calcul haute performance, qui se fait principalement en C/ C++ implémente une certaine fonction pour effectuer des calculs fastidieux, puis la compile dans wasm et l'expose à js pour l'appeler.


Écrivez le code suivant dans le fichier add.c :

#include <stdio.h>
int add(int a, int b) {
  return a + b;
}
 
int main() {
  printf("a + b: %d", add(1, 2));
  return 0;
}
Copier après la connexion
Il existe deux façons d'exposer la méthode add pour les appels js.

Exposer l'API via les paramètres de ligne de commande

emcc -s EXPORTED_FUNCTIONS="[&#39;_add&#39;]" add.c -o add.js
Copier après la connexion
Notez que _ doit être ajouté avant l'ajout du nom de la méthode. Ensuite, nous pouvons l'utiliser dans Node.js comme ceci :

// file node-add.js
const add_module = require(&#39;./add.js&#39;);
console.log(add_module.ccall(&#39;add&#39;, &#39;number&#39;, [&#39;number&#39;, &#39;number&#39;], [2, 3]));
Copier après la connexion
L'exécution du nœud node-add.js affichera 5. Si vous devez l'utiliser sur une page Web, exécutez :

emcc -s EXPORTED_FUNCTIONS="[&#39;_add&#39;]" add.c -o add.html
Copier après la connexion
puis ajoutez le code suivant au add.html généré :

<button onclick="nativeAdd()">click</button>
  <script type=&#39;text/javascript&#39;>
    function nativeAdd() {
      const result = Module.ccall(&#39;add&#39;, &#39;number&#39;, [&#39;number&#39;, &#39;number&#39;], [2, 3]);
      alert(result);
    }
  </script>
Copier après la connexion
Copier après la connexion
Cliquez ensuite sur le bouton pour voir le résultat de l'exécution.

Module.ccall appellera directement la méthode de code C/C++. Un scénario plus courant est que nous obtenons une fonction enveloppée qui peut être appelée à plusieurs reprises dans js. Cela nécessite Module.cwrap. documentation.

const cAdd = add_module.cwrap(&#39;add&#39;, &#39;number&#39;, [&#39;number&#39;, &#39;number&#39;]);
console.log(cAdd(2, 3));
console.log(cAdd(2, 4));
Copier après la connexion

Ajoutez EMSCRIPTEN_KEEPALIVE lors de la définition de la fonction

Ajoutez le fichier add2.c.

#include <stdio.h>
#include <emscripten.h>
Copier après la connexion
int EMSCRIPTEN_KEEPALIVE add(int a, int b) {
  return a + b;
}
 
int main() {
  printf("a + b: %d", add(1, 2));
  return 0;
}
Copier après la connexion
Exécutez la commande :

emcc add2.c -o add2.html
Copier après la connexion
Ajoutez également le code dans add2.html :

<button onclick="nativeAdd()">click</button>
  <script type=&#39;text/javascript&#39;>
    function nativeAdd() {
      const result = Module.ccall(&#39;add&#39;, &#39;number&#39;, [&#39;number&#39;, &#39;number&#39;], [2, 3]);
      alert(result);
    }
  </script>
Copier après la connexion
Copier après la connexion
Cependant, lorsque vous cliquez sur le bouton , rapport d'erreur :

Assertion failed: the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)
Copier après la connexion

可以通过在main()中添加emscripten_exit_with_live_runtime()解决:

#include <stdio.h>
#include <emscripten.h>
 
int EMSCRIPTEN_KEEPALIVE add(int a, int b) {
  return a + b;
}
 
int main() {
  printf("a + b: %d", add(1, 2));
  emscripten_exit_with_live_runtime();
  return 0;
}
Copier après la connexion

或者也可以直接在命令行中添加-s NO_EXIT_RUNTIME=1来解决,

emcc add2.c -o add2.js -s NO_EXIT_RUNTIME=1
Copier après la connexion

不过会报一个警告:

exit(0) implicitly called by end of main(), but noExitRuntime, so not exiting the runtime (you can use emscripten_force_exit, if you want to force a true shutdown)exit(0) implicitly called by end of main(), but noExitRuntime, so not exiting the runtime (you can use emscripten_force_exit, if you want to force a true shutdown)
Copier après la connexion

所以建议采用第一种方法。

上述生成的代码都是asm.js,只需要在编译参数中添加-s WASM=1中就可以生成wasm,然后使用方法都一样。

用asm.js和WebAssembly执行耗时计算

前面准备工作都做完了, 现在我们来试一下用C代码来优化前一篇中提过的问题。代码很简单:

// file sum.c
#include <stdio.h>
// #include <emscripten.h>
 
long sum(long start, long end) {
  long total = 0;
  for (long i = start; i <= end; i += 3) {
    total += i;
  }
  for (long i = start; i <= end; i += 3) {
    total -= i;
  }
  return total;
}
 
int main() {
  printf("sum(0, 1000000000): %ld", sum(0, 1000000000));
  // emscripten_exit_with_live_runtime();
  return 0;
}
Copier après la connexion

注意用gcc编译的时候需要把跟emscriten相关的两行代码注释掉,否则编译不过。 我们先直接用gcc编译成native code看看代码运行多块呢?

➜  webasm-study gcc sum.c
➜  webasm-study time ./a.out
sum(0, 1000000000): 0./a.out  5.70s user 0.02s system 99% cpu 5.746 total
➜  webasm-study gcc -O1 sum.c
➜  webasm-study time ./a.out
sum(0, 1000000000): 0./a.out  0.00s user 0.00s system 64% cpu 0.003 total
➜  webasm-study gcc -O2 sum.c
➜  webasm-study time ./a.out
sum(0, 1000000000): 0./a.out  0.00s user 0.00s system 64% cpu 0.003 total
Copier après la connexion

可以看到有没有优化差别还是很大的,优化过的代码执行时间是3ms!。really?仔细想想,我for循环了10亿次啊,每次for执行大概是两次加法,两次赋值,一次比较,而我总共做了两次for循环,也就是说至少是100亿次操作,而我的mac pro是2.5 GHz Intel Core i7,所以1s应该也就执行25亿次CPU指令操作吧,怎么可能逆天到这种程度,肯定是哪里错了。想起之前看到的一篇rust测试性能的文章,说rust直接在编译的时候算出了答案, 然后把结果直接写到了编译出来的代码里, 不知道gcc是不是也做了类似的事情。在知乎上GCC中-O1 -O2 -O3 优化的原理是什么?这篇文章里, 还真有loop-invariant code motion(LICM)针对for的优化,所以我把代码增加了一些if判断,希望能“糊弄”得了gcc的优化。

#include <stdio.h>
// #include <emscripten.h>
 
// long EMSCRIPTEN_KEEPALIVE sum(long start, long end) {
long sum(long start, long end) {
  long total = 0;
  for (long i = start; i <= end; i += 1) {
    if (i % 2 == 0 || i % 3 == 1) {
      total += i;
    } else if (i % 5 == 0 || i % 7 == 1) {
      total += i / 2;
    }
  }
  for (long i = start; i <= end; i += 1) {
    if (i % 2 == 0 || i % 3 == 1) {
      total -= i;
    } else if (i % 5 == 0 || i % 7 == 1) {
      total -= i / 2;
    }
  }
  return total;
}
 
int main() {
  printf("sum(0, 1000000000): %ld", sum(0, 100000000));
  // emscripten_exit_with_live_runtime();
  return 0;
}
Copier après la connexion


执行结果大概要正常一些了。

➜  webasm-study gcc -O2 sum.c
➜  webasm-study time ./a.out
sum(0, 1000000000): 0./a.out  0.32s user 0.00s system 99% cpu 0.324 total
Copier après la connexion

ok,我们来编译成asm.js了。

#include <stdio.h>
#include <emscripten.h>
 
long EMSCRIPTEN_KEEPALIVE sum(long start, long end) {
// long sum(long start, long end) {
  long total = 0;
  for (long i = start; i <= end; i += 1) {
    if (i % 2 == 0 || i % 3 == 1) {
      total += i;
    } else if (i % 5 == 0 || i % 7 == 1) {
      total += i / 2;
    }
  }
  for (long i = start; i <= end; i += 1) {
    if (i % 2 == 0 || i % 3 == 1) {
      total -= i;
    } else if (i % 5 == 0 || i % 7 == 1) {
      total -= i / 2;
    }
  }
  return total;
}
 
int main() {
  printf("sum(0, 1000000000): %ld", sum(0, 100000000));
  emscripten_exit_with_live_runtime();
  return 0;
}
执行
emcc sum.c -o sum.html
Copier après la connexion


然后在sum.html中添加代码

<button onclick="nativeSum()">NativeSum</button>
  <button onclick="jsSumCalc()">JSSum</button>
  <script type=&#39;text/javascript&#39;>
    function nativeSum() {
      t1 = Date.now();
      const result = Module.ccall(&#39;sum&#39;, &#39;number&#39;, [&#39;number&#39;, &#39;number&#39;], [0, 100000000]);
      t2 = Date.now();
      console.log(`result: ${result}, cost time: ${t2 - t1}`);
    }
  </script>
  <script type=&#39;text/javascript&#39;>
    function jsSum(start, end) {
      let total = 0;
      for (let i = start; i <= end; i += 1) {
        if (i % 2 == 0 || i % 3 == 1) {
          total += i;
        } else if (i % 5 == 0 || i % 7 == 1) {
          total += i / 2;
        }
      }
      for (let i = start; i <= end; i += 1) {
        if (i % 2 == 0 || i % 3 == 1) {
          total -= i;
        } else if (i % 5 == 0 || i % 7 == 1) {
          total -= i / 2;
        }
      }
 
      return total;
    }
    function jsSumCalc() {
      const N = 100000000;// 总次数1亿
      t1 = Date.now();
      result = jsSum(0, N);
      t2 = Date.now();
      console.log(`result: ${result}, cost time: ${t2 - t1}`);
    }
  </script>
另外,我们修改成编译成WebAssembly看看效果呢?
emcc sum.c -o sum.js -s WASM=1
Copier après la connexion

感觉Firefox有点不合理啊, 默认的JS太强了吧。然后觉得webassembly也没有特别强啊,突然发现emcc编译的时候没有指定优化选项-O2。再来一次:

emcc -O2 sum.c -o sum.js # for asm.js
emcc -O2 sum.c -o sum.js -s WASM=1 # for webassembly
Copier après la connexion

居然没什么变化, 大失所望。号称asm.js可以达到native的50%速度么,这个倒是好像达到了。但是今年Compiling for the Web with WebAssembly (Google I/O ‘17)里说WebAssembly是1.2x slower than native code,感觉不对呢。asm.js还有一个好处是,它就是js,所以即使浏览器不支持,也能当成不同的js执行,只是没有加速效果。当然WebAssembly受到各大厂商一致推崇,作为一个新的标准,肯定前景会更好,期待会有更好的表现。

这就是asm.js & webassembly与web高性能计算的关系了,之后还有想法写一份结合Rust做WebAssembly的文章,有兴趣的朋友可以持续关注。


Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Étiquettes associées:
source:php.cn
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal