您的位置:新葡亰496net > 新葡亰官网 > 新葡亰496net:为你写的网页,前端高质量计算之

新葡亰496net:为你写的网页,前端高质量计算之

发布时间:2019-07-28 19:26编辑:新葡亰官网浏览(119)

    前面贰个高品质计算之一:WebWorkers

    2017/10/21 · HTML5 · WebWorkers

    原版的书文出处: magicly   

    前段时间做一个类型,里面涉及到在前端做大量划算,直接用js跑了瞬间,大致须要15s的时间, 约等于用户的浏览器会卡死15s,这么些完全接受不了。

    固然有V8那样牛逼的内燃机,但咱们清楚js并不切合做CPU密集型的持筹握算,一是因为单线程,二是因为动态语言。大家就从那多个突破口出手,首先消除“单线程”的范围,尝试用WebWorkers来加快总结。

    前端高品质计算之二:asm.js & webassembly

    2017/10/21 · HTML5 · webassembly

    初稿出处: magicly   

    前一篇我们说了要化解高质量总结的三个法子,贰个是并发用WebWorkers,另三个就是用更底层的静态语言。

    二〇一三年,Mozilla的程序猿Alon Zakai在研究LLVM编写翻译器时突发奇想:能否把C/C 编写翻译成Javascript,何况尽量达到Native代码的速度吗?于是她付出了Emscripten编写翻译器,用于将C/C 代码编写翻译成Javascript的二个子集asm.js,质量大致是原生代码的一半。大家能够看看这个PPT。

    之后Google开发了Portable Native Client,也是一种能让浏览器运维C/C 代码的才具。 后来打量大家都以为各搞各的充裕呀,居然谷歌(Google), Microsoft, Mozilla, Apple等几家大公司一齐同盟开拓了一个面向Web的通用二进制和文本格式的体系,那正是WebAssembly,官英特网的牵线是:

    WebAssembly or wasm is a new portable, size- and load-time-efficient format suitable for compilation to the web.

    WebAssembly is currently being designed as an open standard by a W3C Community Group that includes representatives from all major browsers.

    所以,WebAssembly有道是是三个前景很好的档案的次序。我们能够看一下日前浏览器的支撑景况: 新葡亰496net 1

    认识Web Worker

    1. Web Worker是 运营在后台的javascript,也正是说worker其实正是就贰个js文件对象,worker可以让他所包罗的js代码运转在后台
    • 特点:

      • 充足利用多核CPU的优势
      • 对三十二线程补助蛮好
      • 不会影响页面包车型大巴习性
      • 不可能访谈web页面和DOM API
      • 享有的主流浏览器均支持web worker,除了Internet Explorer
    1. Worker提供API

      1)质量评定当前浏览器是或不是扶助Worker

      typeof(Worker) !== "undefined“ 
      

      2)创建Worker文件
      创立普通的 JS 文件,都能够用来 Web Worker 文件

      3)创建Web Worker对象

        var worker = new Worker("myTime.js");
      

    参数就是在第二步创立的js文件的路线

      4)worker事件
    
    * onmessage事件
    

    用于监听 Web Worker 传递音讯,通过回调函数接收传递的消息,通过回调函数的平地风波目的data 属性能够收获传递的消息

    • postMessage()

         w.postMessage( “worker success.” );
      

    经过postMessage() 方法传递音信内容

         w.terminate();
    

    在HTML页面中,通过调用 Web Worker 对象的terminate( ) 方法终止 Web Worker。

    流程:
    
    • 创建WebWorker对象
    • Worker对象
    • Worder事件
      * onmessage事件,当施行postMessage事件时会触发
      * postMessage()方法
      * terminate()方法

    Web Worker — 基础知识

    1. Web Worker是 运维在后台的javascript,也正是说worker其实正是就二个js文件对象,worker能够让他所饱含的js代码运营在后台

    2. 特点:

    • 充足利用多核CPU的优势
    • 对多线程支持极度好
    • 不会影响页面包车型客车性质
    • 不能够访谈web页面和DOM API,worker文件里的变量都属于Worker,所以在worker文件里不支持document 情势
    • 装有的主流浏览器均协理web worker,除了较老Internet Explorer版本
    1. Worker提供API
    • 检查测验当前浏览器是不是接济Worker

       在浏览器的控制台输入  typeof(Worker) !== "undefined";  
       输出 true 证明浏览器支持 Worker
      
    • 创建Worker文件

       创建普通的 JS 文件,都可以用于 Web Worker 文件
      
    • 创建Web Worker对象

        var worker = new Worker("myTime.js");
      
        参数就是在第二步创建的js文件的路径
      
    • worker事件

      • onmessage事件

        用来监听 Web Worker 传递消息,通过回调函数接收传递的音讯,
        因此回调函数的平地风波目标data 属性能够拿走传递的新闻

      • postMessage()

        在意:postMessage() 只写在开创的Worker文件中
        经过postMessage() 方法传递音信内容
        worker.postMessage( "worker success");

      • worker.terminate();

        在HTML页面中,通过调用 Web Worker 对象的 terminate( ) 方法终止 Web Worker。

    1.Web Worker是 运维在后台的javascript,也等于说worker其实正是就三个js文件对象,worker能够让她所含有的js代码运营在后台
    2.特点:

    什么是WebWorkers

    简单说,WebWorkers是二个HTML5的新API,web开荒者能够因此此API在后台运维二个剧本而不阻塞UI,能够用来做须要一大波计量的作业,丰盛利用CPU多核。

    世家能够看看这篇文章介绍https://www.html5rocks.com/en/tutorials/workers/basics/, 或者相应的汉语版。

    The Web Workers specification defines an API for spawning background scripts in your web application. Web Workers allow you to do things like fire up long-running scripts to handle computationally intensive tasks, but without blocking the UI or other scripts to handle user interactions.

    能够张开其一链接温馨经验一下WebWorkers的增长速度效果。

    最近浏览器基本都支持WebWorkers了。 新葡亰496net 2

    安装Emscripten

    访问

    1. 下载对应平台版本的SDK

    2. 透过emsdk获取最新版工具

    JavaScript

    # 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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    # 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
    1. 将下列增多到情状变量PATH中

    JavaScript

    ~/emsdk-portable ~/emsdk-portable/clang/fastcomp/build_incoming_64/bin ~/emsdk-portable/emscripten/incoming

    1
    2
    3
    ~/emsdk-portable
    ~/emsdk-portable/clang/fastcomp/build_incoming_64/bin
    ~/emsdk-portable/emscripten/incoming
    1. 其他

    自家在进行的时候遭逢报错说LLVM本子不对,后来参考文档配置了LLVM_ROOT变量就好了,假若你未有碰着标题,能够忽略。

    JavaScript

    LLVM_ROOT = os.path.expanduser(os.getenv('LLVM', '/home/ubuntu/a-path/emscripten-fastcomp/build/bin'))

    1
    LLVM_ROOT = os.path.expanduser(os.getenv('LLVM', '/home/ubuntu/a-path/emscripten-fastcomp/build/bin'))
    1. 表明是还是不是安装好

    执行emcc -v,假若设置好会师世如下音讯:

    JavaScript

    emcc (Emscripten gcc/clang-like replacement linker emulating GNU ld) 1.37.21 clang version 4.0.0 ( 974b55fd84ca447c4297fc3b00cefb6394571d18) ( 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)

    1
    2
    3
    4
    5
    6
    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)
    使用手续
    • 创建WebWorker对象
    • Worker对象
    • onmessage事件,当实践postMessage事件时会触发
    • postMessage()方法
    • terminate()方法
    • 足够利用多核CPU的优势

    • 对多线程匡助非常好

    • 不会影响页面的个性

    • 不可能访谈web页面和DOM API

    • 抱有的主流浏览器均援助web worker,除了Internet Explorer
      3.Worker提供API

    • 检查实验当前浏览器是还是不是帮忙Worker
      typeof(Worker) !== "undefined“

    • 创建Worker文件
      成立普通的 JS 文件,都能够用来 Web Worker 文件

    • 创建Web Worker对象
      var worker = new Worker("myTime.js");
      参数正是在其次步创设的js文件的渠道

    • worker事件

    • onmessage事件
      用于监听 Web Worker 传递新闻,通过回调函数接收传递的音讯,通过回调函数的平地风波目的data 属性能够博得传递的新闻

    • postMessage()
      w.postMessage( “worker success.” );
      透过postMessage() 方法传递音信内容

    • w.terminate();
      在HTML页面中,通过调用 Web Worker 对象的terminate( ) 方法终止 Web Worker。
      4.创建WebWorker对象
      Worker对象
      onmessage事件,当实行postMessage事件时会触发
      postMessage()方法
      terminate()方法
      代码示例:

    • 计时器
      html
      <!DOCTYPE html>
      <html>
      <head>
      <meta charset="UTF-8">
      <title></title>
      </head>
      <body>
      <button id="start">起初计时</button>
      <button id="stop">停止计时</button>
      <br />
      <div id="showTime"></div>
      </body>
      <script type="text/javascript">
      var show = document.getElementById("showTime");
      document.getElementById("start").onclick = function(){
      //1.确定是不是协理worker
      if (typeof(Worker)!="undefined") {
      //2,创设js文件,将想在后台实行的js
      //3.创制worker对象,并早先实践worker中的代码
      worker = new Worker("myTime.js");
      //5.绑定接收worker传过来数据的平地风波
      worker.onmessage = function(event){
      show.innerHTML = event.data;
      }
      } else{
      alert("您使用的浏览器不补助worker")
      }
      }
      document.getElementById("stop").onclick= function(){
      //6.终止worker执行
      worker.terminate();
      }
      </script>
      </html>
      js
      var time = 0;
      function timer(){
      time ;
      //4.从worker中发送数据worker对应js中的全局变量是worker对象
      postMessage(time);
      setTimeout(timer,1000);
      }
      timer();

    • 依傍订票系统
      html
      <!DOCTYPE html>
      <html>
      <head>
      <meta charset="UTF-8">
      <title></title>
      <style>
      div{
      width:150px;
      height:150px;
      border:1px solid black;
      float: left;
      margin: 10px;
      }
      .hasticket {
      background: green;
      }
      .noTicket {
      background: red;
      }
      </style>
      </head>
      <body align="center">
      <h1>买票系统</h1>
      <div id="win0">定票窗口1</div>
      <div id="win1">买票窗口2</div>
      <div id="win2">定票窗口3</div>
      <div id="win3">订票窗口4</div>
      <div id="win4">购票窗口5</div>
      <button id="button">抢票</button>
      </body>
      <script type="text/javascript">
      var myWorkers = [];
      var btn = document.getElementById("button");
      btn.onclick = function(){
      myWorkers.length = 0;
      if(typeof(Worker) !== "undefined"){
      for(var i=0;i<5;i ){
      var worker = new Worker("getT.js");
      worker.onmessage = getMessage;
      myWorkers.push(worker);
      }
      }
      function getMessage(event){
      var w = event.target;
      var index = myWorkers.indexOf(w);
      var win = document.getElementById("win" index);
      if(event.data == 1){
      win.className = "hasticket";
      }else{
      win.className = "noTicket";
      }
      }
      }
      </script>
      </html>
      js
      //模拟一成的概率抢到票
      var rand = Math.floor(Math.random()*101);
      if (rand<50) {
      //重返数据
      postMessage(1);
      console.log(1)
      } else{
      postMessage(0);
      console.log(0)
      }

    Parallel.js

    直白选拔WebWorkers接口依旧太烦琐,幸亏有人一度对此作了打包:Parallel.js。

    注意Parallel.js能够由此node安装:

    $ npm install paralleljs

    1
    $ npm install paralleljs

    只是那些是在node.js下用的,用的node的cluster模块。假若要在浏览器里使用的话, 须要一向利用js:

    <script src="parallel.js"></script>

    1
    <script src="parallel.js"></script>

    下一场能够拿走多个全局变量,ParallelParallel提供了mapreduce七个函数式编制程序的接口,能够非常便利的进展并发操作。

    我们先来定义一下大家的难题,由于事情相比复杂,作者那边把难点简化成求1-1,0000,0000的和,然后在各类减去1-1,0000,0000,答案昭昭: 0! 那样做是因为数字太大的话会有多少精度的标题,三种格局的结果会有点出入,会令人感到互相的方式离谱。此难点在本身的mac pro chrome61下直接省略地跑js运转的话大约是1.5s(大家其实职业难点须要15s,这里为了幸免用户测验的时候把浏览器搞死,大家简化了难题)。

    const N = 100000000;// 总次数1亿 function sum(start, end) { let total = 0; for (let i = start; i<=end; i = 1) total = i; for (let i = start; i<=end; i = 1) total -= i; return total; } function paraSum(N) { const N1 = N / 10;//咱们分成10分,没分分别交由多少个web worker,parallel.js会依照计算机的CPU核数创立适用的workers let p = new Parallel([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]) .require(sum); return p.map(n => sum((n - 1) * 10000000 1, n * 一千0000))// 在parallel.js里面无法直接运用外界变量N1 .reduce(data => { const acc = data[0]; const e = data[1]; return acc e; }); }

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    const N = 100000000;// 总次数1亿
     
    function sum(start, end) {
      let total = 0;
      for (let i = start; i<=end; i = 1) total = i;
      for (let i = start; i<=end; i = 1) total -= i;
      return total;
    }
     
    function paraSum(N) {
      const N1 = N / 10;//我们分成10分,没分分别交给一个web worker,parallel.js会根据电脑的CPU核数建立适量的workers
      let p = new Parallel([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
        .require(sum);
      return p.map(n => sum((n - 1) * 10000000 1, n * 10000000))// 在parallel.js里面没法直接应用外部变量N1
        .reduce(data => {
          const acc = data[0];
          const e = data[1];
          return acc e;
        });
    }

    代码比较轻松,我这边说多少个刚用的时候遭遇的坑。

    • require全体需求的函数

    譬喻说在上诉代码中用到了sum,你需求提前require(sum),倘诺sum中由用到了另叁个函数f,你还须要require(f),同样要是f中用到了g,则还须求require(g),直到你require了富有应用的概念的函数。。。。

    • 没法require变量

    咱俩上诉代码我当然定义了N1,但是没办法用

    • ES6编译成ES5后来的主题材料以及Chrome没报错

    实则项目中一齐头我们用到了ES6的特性:数组解构。本来那是极粗略的特色,现在当先57%浏览器都曾经支撑了,但是自个儿立时计划的babel会编写翻译成ES5,所以会生成代码_slicedToArray,大家能够在线上Babel测试,然后Chrome下边始终不work,也远非任何报错音信,查了十分久,后来在Firefox下开荒,有报错音信:

    ReferenceError: _slicedToArray is not defined

    1
    ReferenceError: _slicedToArray is not defined

    看来Chrome亦非万能的啊。。。

    世家能够在此Demo页面测量试验, 提速大致在4倍左右,当然照旧得看自个儿计算机CPU的核数。 别的小编后来在同样的微处理器上Firefox55.0.3(六12个人)测量试验,上诉代码居然只要190ms!!!在Safari9.1.1下也是190ms左右。。。

    Hello, WebAssembly!

    创立一个文件hello.c

    JavaScript

    #include <stdio.h> int main() { printf("Hello, WebAssembly!n"); return 0; }

    1
    2
    3
    4
    5
    #include <stdio.h>
    int main() {
      printf("Hello, WebAssembly!n");
      return 0;
    }

    编译C/C 代码:

    JavaScript

    emcc hello.c

    1
    emcc hello.c

    上述命令会变卦一个a.out.js文件,大家能够一直用Node.js执行:

    JavaScript

    node a.out.js

    1
    node a.out.js

    输出

    JavaScript

    Hello, WebAssembly!

    1
    Hello, WebAssembly!

    为了让代码运营在网页里面,实施上边发号施令会变卦hello.htmlhello.js两个文本,当中hello.jsa.out.js内容是完全一样的。

    emcc hello.c -o hello.html<code>

    1
    2
    emcc hello.c -o hello.html<code>
     

    JavaScript

    ➜ webasm-study md5 a.out.js MD5 (a.out.js) = d7397f44f817526a4d0f94bc85e46429 ➜ webasm-study md5 hello.js MD5 (hello.js) = d7397f44f817526a4d0f94bc85e46429

    1
    2
    3
    4
    ➜  webasm-study md5 a.out.js
    MD5 (a.out.js) = d7397f44f817526a4d0f94bc85e46429
    ➜  webasm-study md5 hello.js
    MD5 (hello.js) = d7397f44f817526a4d0f94bc85e46429

    然后在浏览器张开hello.html,能够见见页面 新葡亰496net 3

    眼下生成的代码都以asm.js,毕竟Emscripten是每户小编Alon Zakai最早用来扭转asm.js的,暗许输出asm.js也就欠缺为奇了。当然,能够由此option生成wasm,会转移多少个文本:hello-wasm.html, hello-wasm.js, hello-wasm.wasm

    JavaScript

    emcc hello.c -s WASM=1 -o hello-wasm.html

    1
    emcc hello.c -s WASM=1 -o hello-wasm.html

    下一场浏览器张开hello-wasm.html,开采报错TypeError: Failed to fetch。原因是wasm文本是因此XHR异步加载的,用file:////拜谒会报错,所以大家须求启二个服务器。

    JavaScript

    npm install -g serve serve

    1
    2
    npm install -g serve
    serve

    接下来访谈http://localhost:5000/hello-wasm.html,就足以看看符合规律结果了。

    例子
    <body>
       <button id="start">开始计时</button>
       <button id="stop">结束计时</button>
       <br/>
       <div id="showTime"></div>    
    </body>
    
    <script type="text/javascript">
        var show=document.getElementById("showTime");
        var start=document.getElementById('start');
        var stop=document.getElementById('stop');
        start.onclick=function(){
            if(typeof(Worker) !== "undefined"){
                worker=new Worker("worker.js");
                worker.onmessage=function(event){
                    show.innerHTML=event.data;
                };
            }else{
                alert("你的浏览器不支持");
            };
        };
    
        stop.onclick=function(){
            worker.terminate();
        };
    </script>    
    

    Refers

    • https://developer.mozilla.org/zh-CN/docs/Web/API/Web*Workers*API/Using*web*workers
    • 1 赞 1 收藏 评论

    新葡亰496net 4

    调用C/C 函数

    前面的Hello, WebAssembly!都是main函数直接打出来的,而笔者辈选取WebAssembly的指标是为了高品质总计,做法多半是用C/C 达成有个别函数实行耗费时间的持筹握算,然后编写翻译成wasm,暴露给js去调用。

    在文件add.c中写如下代码:

    JavaScript

    #include <stdio.h> int add(int a, int b) { return a b; } int main() { printf("a b: %d", add(1, 2)); return 0; }

    1
    2
    3
    4
    5
    6
    7
    8
    9
    #include <stdio.h>
    int add(int a, int b) {
      return a b;
    }
     
    int main() {
      printf("a b: %d", add(1, 2));
      return 0;
    }

    有三种方法能够把add主意暴暴光来给js调用。

    worker.js 文件
    var time=0;
    (function _start(){
       time  ;
       postMessage(time);
       t=setTimeout(_start,1000);
    })();             
    

    未完待续······

    经过命令行参数暴光API

    JavaScript

    emcc -s EXPORTED_FUNCTIONS="['_add']" add.c -o add.js

    1
    emcc -s EXPORTED_FUNCTIONS="['_add']" add.c -o add.js

    小心方法名add前务必加_。 然后我们得以在Node.js其间这么使用:

    JavaScript

    // file node-add.js const add_module = require('./add.js'); console.log(add_module.ccall('add', 'number', ['number', 'number'], [2, 3]));

    1
    2
    3
    // file node-add.js
    const add_module = require('./add.js');
    console.log(add_module.ccall('add', 'number', ['number', 'number'], [2, 3]));

    执行node node-add.js会输出5。 即使急需在web页面使用的话,施行:

    JavaScript

    emcc -s EXPORTED_FUNCTIONS="['_add']" add.c -o add.html

    1
    emcc -s EXPORTED_FUNCTIONS="['_add']" add.c -o add.html

    下一场在变化的add.html中出席如下代码:

    JavaScript

    <button onclick="nativeAdd()">click</button> <script type='text/javascript'> function nativeAdd() { const result = Module.ccall('add', 'number', ['number', 'number'], [2, 3]); alert(result); } </script>

    1
    2
    3
    4
    5
    6
    7
      <button onclick="nativeAdd()">click</button>
      <script type='text/javascript'>
        function nativeAdd() {
          const result = Module.ccall('add', 'number', ['number', 'number'], [2, 3]);
          alert(result);
        }
      </script>

    然后点击button,就足以看看进行结果了。

    Module.ccall会一直调用C/C 代码的不二等秘书诀,更通用的光景是我们收获到叁个包裹过的函数,能够在js里面频频调用,那亟需用Module.cwrap,具体细节能够参照文档。

    JavaScript

    const cAdd = add_module.cwrap('add', 'number', ['number', 'number']); console.log(cAdd(2, 3)); console.log(cAdd(2, 4));

    1
    2
    3
    const cAdd = add_module.cwrap('add', 'number', ['number', 'number']);
    console.log(cAdd(2, 3));
    console.log(cAdd(2, 4));

    概念函数的时候增进EMSCRIPTEN_KEEPALIVE

    添Gavin书add2.c

    JavaScript

    #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)); return 0; }

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    #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));
      return 0;
    }

    推行命令:

    JavaScript

    emcc add2.c -o add2.html

    1
    emcc add2.c -o add2.html

    同样在add2.html中加多代码:

    JavaScript

    <button onclick="nativeAdd()">click</button> <script type='text/javascript'> function nativeAdd() { const result = Module.ccall('add', 'number', ['number', 'number'], [2, 3]); alert(result); } </script>

    1
    2
    3
    4
    5
    6
    7
      <button onclick="nativeAdd()">click</button>
      <script type='text/javascript'>
        function nativeAdd() {
          const result = Module.ccall('add', 'number', ['number', 'number'], [2, 3]);
          alert(result);
        }
      </script>

    可是,当您点击button的时候,报错:

    JavaScript

    Assertion failed: the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)

    1
    Assertion failed: the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)

    能够经过在main()中添加emscripten_exit_with_live_runtime()解决:

    JavaScript

    #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; }

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    #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;
    }

    依然也足以平素在指令行中增添-s NO_EXIT_RUNTIME=1来解决,

    JavaScript

    emcc add2.c -o add2.js -s NO_EXIT_RUNTIME=1

    1
    emcc add2.c -o add2.js -s NO_EXIT_RUNTIME=1

    可是会报多个警戒:

    JavaScript

    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)

    1
    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)

    就此建议使用第一种方法。

    上述变化的代码都以asm.js,只须求在编译参数中增多-s WASM=1中就足以生成wasm,然后利用办法都一样。

    用asm.js和WebAssembly推行耗费时间计算

    眼下希图工作都做完了, 今后我们来试一下用C代码来优化前一篇中提过的难点。代码很粗大略:

    JavaScript

    // 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; }

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    // 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;
    }

    注意用gcc编写翻译的时候需求把跟emscriten连锁的两行代码注释掉,不然编写翻译不过。 我们先直接用gcc编译成native code看看代码运营多块吧?

    JavaScript

    ➜ 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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    ➜  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

    能够见到有未有优化差异依然异常的大的,优化过的代码实施时间是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的优化。

    JavaScript

    #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; }

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    #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;
    }

    奉行结果大概要正规一些了。

    新葡亰496net:为你写的网页,前端高质量计算之二。JavaScript

    ➜ 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

    1
    2
    3
    ➜  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

    ok,大家来编写翻译成asm.js了。

    JavaScript

    #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; }

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    #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;
    }

    执行

    JavaScript

    emcc sum.c -o sum.html

    1
    emcc sum.c -o sum.html

    然后在sum.html中增添代码``

    JavaScript

    <button onclick="nativeSum()">NativeSum</button> <button onclick="jsSumCalc()">JSSum</button> <script type='text/javascript'> function nativeSum() { t1 = Date.now(); const result = Module.ccall('sum', 'number', ['number', 'number'], [0, 100000000]); t2 = Date.now(); console.log(`result: ${result}, cost time: ${t2 - t1}`); } </script> <script type='text/javascript'> 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>

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    <button onclick="nativeSum()">NativeSum</button>
      <button onclick="jsSumCalc()">JSSum</button>
      <script type='text/javascript'>
        function nativeSum() {
          t1 = Date.now();
          const result = Module.ccall('sum', 'number', ['number', 'number'], [0, 100000000]);
          t2 = Date.now();
          console.log(`result: ${result}, cost time: ${t2 - t1}`);
        }
      </script>
      <script type='text/javascript'>
        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看看效果呢?

    JavaScript

    emcc sum.c -o sum.js -s WASM=1

    1
    emcc sum.c -o sum.js -s WASM=1
    Browser webassembly asm.js js
    Chrome61 1300ms 600ms 3300ms
    Firefox55 600ms 800ms 700ms
    Safari9.1 不支持 2800ms 因不支持ES6我懒得改写没测试

    倍感Firefox有一点点不成立啊, 暗中同意的JS太强了吧。然后认为webassembly也并没有非常强啊,猛然意识emcc编写翻译的时候未有一些名牌产品优质产品化增选-O2。再来二次:``

    JavaScript

    emcc -O2 sum.c -o sum.js # for asm.js emcc -O2 sum.c -o sum.js -s WASM=1 # for webassembly

    1
    2
    emcc -O2 sum.c -o sum.js # for asm.js
    emcc -O2 sum.c -o sum.js -s WASM=1 # for webassembly
    Browser webassembly -O2 asm.js -O2 js
    Chrome61 1300ms 600ms 3300ms
    Firefox55 650ms 630ms 700ms

    乃至没什么变化, 白璧微瑕。可以称作asm.js能够高达native的百分之五十速度么,那个倒是好像到达了。不过现年Compiling for the Web with WebAssembly (Google I/O ‘17)里说WebAssembly是1.2x slower than native code,感到狼狈呢。asm.js还应该有三个好处是,它正是js,所以尽管浏览器不帮忙,也能算作分化的js试行,只是未有加速效果。当然WebAssembly未遭各大商家一致重申,作为三个新的规范,确定前景会越来越好,期待会有更加好的变现。

    Rust

    当然还想写Rust编译成WebAssembly的,可是以为本文已经太长了, 中期再写就算结合Rust做WebAssembly吧。

    发急的能够先看看这两篇

    • Compiling to the web with Rust and emscripten
    • Rust ⇋ JavaScript

    Refers

    • 1 赞 收藏 评论

    新葡亰496net 5

    本文由新葡亰496net发布于新葡亰官网,转载请注明出处:新葡亰496net:为你写的网页,前端高质量计算之

    关键词: