日本综合一区二区|亚洲中文天堂综合|日韩欧美自拍一区|男女精品天堂一区|欧美自拍第6页亚洲成人精品一区|亚洲黄色天堂一区二区成人|超碰91偷拍第一页|日韩av夜夜嗨中文字幕|久久蜜综合视频官网|精美人妻一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問(wèn)題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷解決方案
WebAssembly 初體驗(yàn):從零開始重構(gòu)計(jì)算模塊

WebAssembly 的概念、意義以及未來(lái)帶來(lái)的性能提升相信已是耳熟能詳,筆者在前端每周清單系列中也是經(jīng)常會(huì)推薦 WebAssembly 相關(guān)文章。不過(guò)筆者也只是了解其概念而未真正付諸實(shí)踐,本文即是筆者在將我司某個(gè)簡(jiǎn)單項(xiàng)目中的計(jì)算模塊重構(gòu)為 WebAssembly 過(guò)程中的總結(jié)。在簡(jiǎn)單的實(shí)踐中筆者個(gè)人感覺(jué),WebAssembly 的抽象程度會(huì)比 JavaScript 高不少,未來(lái)對(duì)于大型項(xiàng)目的遷移,對(duì)于純前端工程師而言可能存在的坑也是不少,仿佛又回到了被指針統(tǒng)治的年代。本文筆者使用的案例已經(jīng)集成到了 React 腳手架 create-react-boilerplate 中 ,可以方便大家快速本地實(shí)踐。

成都創(chuàng)新互聯(lián)公司專注于峰峰礦網(wǎng)站建設(shè)服務(wù)及定制,我們擁有豐富的企業(yè)做網(wǎng)站經(jīng)驗(yàn)。 熱誠(chéng)為您提供峰峰礦營(yíng)銷型網(wǎng)站建設(shè),峰峰礦網(wǎng)站制作、峰峰礦網(wǎng)頁(yè)設(shè)計(jì)、峰峰礦網(wǎng)站官網(wǎng)定制、成都小程序開發(fā)服務(wù),打造峰峰礦網(wǎng)絡(luò)公司原創(chuàng)品牌,更為您提供峰峰礦網(wǎng)站排名全網(wǎng)營(yíng)銷落地服務(wù)。

編譯環(huán)境搭建

我們使用 Emscripten 將 C 代碼編譯為 wasm 格式,官方推薦的方式是首先下載 Portable Emscripten SDK for Linux and OS X (emsdk-portable.tar.gz) 然后利用 emsdk 進(jìn)行安裝:

 
 
 
 
  1. $ ./emsdk update 
  2. $ ./emsdk install latest 
  3. # 如果出現(xiàn)異常使用 ./emsdk install sdk-1.37.12-64bit 
  4. # https://github.com/kripken/emscripten/issues/5272 

安裝完畢后激活響應(yīng)環(huán)境即可以進(jìn)行編譯:

 
 
 
 
  1. $ ./emsdk activate latest  
  2. $ source ./emsdk_env.sh # you can add this line to your .bashrc 

筆者在本地執(zhí)行上述搭建步驟時(shí)一直失敗,因此改用了 Docker 預(yù)先配置好的鏡像進(jìn)行處理:

 
 
 
 
  1. # 拉取 Docker 鏡像 
  2. docker pull 42ua/emsdk 
  3.  
  4. # 執(zhí)行編譯操作 
  5. docker run --rm -v $(pwd):/home/src 42ua/emsdk emcc hello_world.c 

對(duì)應(yīng)的 Dockfile 如下所示,我們可以自行修改以適應(yīng)未來(lái)的編譯環(huán)境:

 
 
 
 
  1. FROM ubuntu 
  2.  
  3. RUN \ 
  4.     apt-get update && apt-get install -y build-essential \ 
  5.     cmake python2.7 python nodejs-legacy default-jre git-core curl && \ 
  6.     apt-get clean && \ 
  7.     cd ~/ && \ 
  8.     curl -sL https://s3.amazonaws.com/mozilla-games/emscripten/releases/emsdk-portable.tar.gz | tar xz && \ 
  9.     cd emsdk-portable/ && \ 
  10.     ./emsdk update && \ 
  11.     ./emsdk install -j1 latest && \ 
  12.     ./emsdk activate latest && \ 
  13.     rm -rf ~/emsdk-portable/clang/tag-*/src && \ 
  14.     find . -name "*.o" -exec rm {} \; && \ 
  15.     find . -name "*.a" -exec rm {} \; && \ 
  16.     find . -name "*.tmp" -exec rm {} \; && \ 
  17.     find . -type d -name ".git" -prune -exec rm -rf {} \; && \ 
  18.     apt-get -y --purge remove curl git-core cmake && \ 
  19.     apt-get -y autoremove && apt-get clean 
  20.  
  21. # http://docs.docker.com/engine/reference/run/#workdir 
  22. WORKDIR /home/src 

到這里基本環(huán)境已經(jīng)配置完畢,我們可以對(duì)簡(jiǎn)單的 counter.c 進(jìn)行編譯,源文件如下:

 
 
 
 
  1. int counter = 100; 
  2.  
  3. int count() {   
  4.     counter += 1; 
  5.     return counter; 

編譯命令如下所示,如果本地安裝好了 emcc 則可以直接使用,否則使用 Docker 環(huán)境進(jìn)行編譯:

 
 
 
 
  1. $ docker run --rm -v $(pwd):/home/src 42ua/emsdk emcc counter.c -s WASM=1 -s SIDE_MODULE=1 -o counter.wasm 
  2. $ emcc counter.c -s WASM=1 -s SIDE_MODULE=1 -o counter.wasm 
  3.  
  4. # 如果出現(xiàn)以下錯(cuò)誤,則是由如下參數(shù) 
  5. # WebAssembly Link Error: import object field 'DYNAMICTOP_PTR' is not a Number 
  6. emcc counter.c -O1 -s WASM=1 -s SIDE_MODULE=1 -o counter.wasm  

這樣我們就得到了 WebAssembly 代碼:

與 JavaScript 集成使用

獨(dú)立的 .wasm 文件并不能直接使用,我們需要在客戶端中使用 JavaScript 代碼將其加載進(jìn)來(lái)。最樸素的加載 WebAssembly 的方式就是使用 fetch 抓取然后編譯,整個(gè)過(guò)程可以封裝為如下函數(shù):

 
 
 
 
  1. // 判斷是否支持 WebAssembly 
  2.     if (!('WebAssembly' in window)) { 
  3.       alert('當(dāng)前瀏覽器不支持 WebAssembly!'); 
  4.     } 
  5.     // Loads a WebAssembly dynamic library, returns a promise. 
  6.     // imports is an optional imports object 
  7.     function loadWebAssembly(filename, imports) { 
  8.       // Fetch the file and compile it 
  9.       return fetch(filename) 
  10.         .then(response => response.arrayBuffer()) 
  11.         .then(buffer => WebAssembly.compile(buffer)) 
  12.         .then(module => { 
  13.           // Create the imports for the module, including the 
  14.           // standard dynamic library imports 
  15.           imports = imports || {}; 
  16.           imports.env = imports.env || {}; 
  17.           imports.env.memoryBase = imports.env.memoryBase || 0; 
  18.           imports.env.tableBase = imports.env.tableBase || 0; 
  19.           if (!imports.env.memory) { 
  20.             imports.env.memory = new WebAssembly.Memory({ initial: 256 }); 
  21.           } 
  22.           if (!imports.env.table) { 
  23.             imports.env.table = new WebAssembly.Table({ initial: 0, element: 'anyfunc' }); 
  24.           } 
  25.           // Create the instance. 
  26.           return new WebAssembly.Instance(module, imports); 
  27.         }); 
  28.     } 

我們可以使用上述工具函數(shù)加載 wasm 文件:

 
 
 
 
  1. loadWebAssembly('counter.wasm') 
  2.      .then(instance => { 
  3.        var exports = instance.exports; // the exports of that instance 
  4.        var count = exports. _count; // the "_count" function (note "_" prefix) 
  5.        // 下面即可以調(diào)用 count 函數(shù) 
  6.      } 
  7.    ); 

而在筆者的腳手架中,使用了 wasm-loader 進(jìn)行加載,這樣可以將 wasm 直接打包在 Bundle 中,然后通過(guò) import 導(dǎo)入:

 
 
 
 
  1. import React, { PureComponent } from "react"; 
  2.  
  3. import CounterWASM from "./counter.wasm"; 
  4. import Button from "antd/es/button/button"; 
  5.  
  6. import "./Counter.scss"; 
  7.  
  8. /** 
  9.  * Description 簡(jiǎn)單計(jì)數(shù)器示例 
  10.  */ 
  11. export default class Counter extends PureComponent { 
  12.   state = { 
  13.     count: 0 
  14.   }; 
  15.  
  16.   componentDidMount() { 
  17.     this.counter = new CounterWASM({ 
  18.       env: { 
  19.         memoryBase: 0, 
  20.         tableBase: 0, 
  21.         memory: new window.WebAssembly.Memory({ initial: 256 }), 
  22.         table: new window.WebAssembly.Table({ initial: 0, element: "anyfunc" }) 
  23.       } 
  24.     }); 
  25.     this.setState({ 
  26.       count: this.counter.exports._count() 
  27.     }); 
  28.   } 
  29.  
  30.   /** 
  31.    * Description 默認(rèn)渲染函數(shù) 
  32.    */ 
  33.   render() { 
  34.     const isWASMSupport = "WebAssembly" in window; 
  35.  
  36.     if (!isWASMSupport) { 
  37.       return ( 
  38.         
     
  39.           瀏覽器不支持 WASM 
  40.         
 
  •       ); 
  •     } 
  •  
  •     return ( 
  •        
  •          
  •           簡(jiǎn)單計(jì)數(shù)器示例: 
  •          
  •         {this.state.count} 
  •         
  •           type="primary" 
  •           onClick={() => { 
  •             this.setState({ 
  •               count: this.counter.exports._count() 
  •             }); 
  •           }} 
  •         > 
  •           點(diǎn)擊自增 
  •          
  •       
  •  
  •     ); 
  •   } 
  • 在使用 wasm-loader 時(shí),其會(huì)調(diào)用 new WebAssembly.Instance(module, importObject);

    module 即 WebAssembly.Module 實(shí)例。

    importObject 即默認(rèn)的由 wasm-loader 提供的對(duì)象。

    簡(jiǎn)單游戲引擎重構(gòu)

    上文我們討論了利用 WebAssembly 重構(gòu)簡(jiǎn)單的計(jì)數(shù)器模塊,這里我們以簡(jiǎn)單的游戲?yàn)槔?,交互式的感?WebAssembly 帶來(lái)的性能提升,可以直接查看游戲的在線演示。這里的游戲引擎即是執(zhí)行部分計(jì)算與重新賦值操作,譬如這里的計(jì)算下一個(gè)位置狀態(tài)的函數(shù)在 C 中實(shí)現(xiàn)為:

     
     
     
     
    1. EMSCRIPTEN_KEEPALIVE 
    2. void computeNextState() 
    3.   loopCurrentState(); 
    4.  
    5.   int neighbors = 0; 
    6.   int i_m1, i_p1, i_; 
    7.   int j_m1, j_p1; 
    8.   int height_limit = height - 1; 
    9.   int width_limit = width - 1; 
    10.   for (int i = 1; i < height_limit; i++) 
    11.   { 
    12.     i_m1 = (i - 1) * width; 
    13.     i_p1 = (i + 1) * width; 
    14.     i_ = i * width; 
    15.     for (int j = 1; j < width_limit; j++) 
    16.     { 
    17.       j_m1 = j - 1; 
    18.       j_p1 = j + 1; 
    19.       neighbors = current[i_m1 + j_m1]; 
    20.       neighbors += current[i_m1 + j]; 
    21.       neighbors += current[i_m1 + j_p1]; 
    22.       neighbors += current[i_ + j_m1]; 
    23.       neighbors += current[i_ + j_p1]; 
    24.       neighbors += current[i_p1 + j_m1]; 
    25.       neighbors += current[i_p1 + j]; 
    26.       neighbors += current[i_p1 + j_p1]; 
    27.       if (neighbors == 3) 
    28.       { 
    29.         next[i_ + j] = 1; 
    30.       } 
    31.       else if (neighbors == 2) 
    32.       { 
    33.         next[i_ + j] = current[i_ + j]; 
    34.       } 
    35.       else 
    36.       { 
    37.         next[i_ + j] = 0; 
    38.       } 
    39.     } 
    40.   } 
    41.   memcpy(current, next, width * height); 

    而對(duì)應(yīng)的 JS 版本引擎的實(shí)現(xiàn)為:

     
     
     
     
    1. computeNextState() { 
    2.   let neighbors, iM1, iP1, i_, jM1, jP1; 
    3.  
    4.   this.loopCurrentState(); 
    5.  
    6.   for (let i = 1; i < this._height - 1; i++) { 
    7.     iM1 = (i - 1) * this._width; 
    8.     iP1 = (i + 1) * this._width; 
    9.     i_ = i * this._width; 
    10.     for (let j = 1; j < this._width - 1; j++) { 
    11.       jM1 = j - 1; 
    12.       jP1 = j + 1; 
    13.       neighbors = this._current[iM1 + jM1]; 
    14.       neighbors += this._current[iM1 + j]; 
    15.       neighbors += this._current[iM1 + jP1]; 
    16.       neighbors += this._current[i_ + jM1]; 
    17.       neighbors += this._current[i_ + jP1]; 
    18.       neighbors += this._current[iP1 + jM1]; 
    19.       neighbors += this._current[iP1 + j]; 
    20.       neighbors += this._current[iP1 + jP1]; 
    21.       if (neighbors === 3) { 
    22.         this._next[i_ + j] = 1; 
    23.       } else if (neighbors === 2) { 
    24.         this._next[i_ + j] = this._current[i_ + j]; 
    25.       } else { 
    26.         this._next[i_ + j] = 0; 
    27.       } 
    28.     } 
    29.   } 
    30.   this._current.set(this._next); 

    本部分的編譯依舊是直接將 [engine.c]() 編譯為 engine.wasm,不過(guò)在導(dǎo)入的時(shí)候我們需要?jiǎng)討B(tài)地向 wasm 中注入外部函數(shù):

     
     
     
     
    1. this.module = new EngineWASM({ 
    2.       env: { 
    3.         memoryBase: 0, 
    4.         tableBase: 0, 
    5.         memory: new window.WebAssembly.Memory({ initial: 1024 }), 
    6.         table: new window.WebAssembly.Table({ initial: 0, element: "anyfunc" }), 
    7.         _malloc: size => { 
    8.           let buffer = new ArrayBuffer(size); 
    9.           return new Uint8Array(buffer); 
    10.         }, 
    11.         _memcpy: (source, target, size) => { 
    12.           let sourceEnd = source.byteLength; 
    13.  
    14.           let i, j; 
    15.  
    16.           for ( 
    17.             (i = 0), (j = 0), (k = new Uint8Array(target)), (l = new Uint8Array( 
    18.               source 
    19.             )); 
    20.             i < sourceEnd; 
    21.             ++i, ++j 
    22.           ) 
    23.             k[j] = l[i]; 
    24.         } 
    25.       } 
    26.     }); 

    到這里文本告一段落,筆者***需要聲明的是因?yàn)檫@只是隨手做的實(shí)驗(yàn),***的代碼包括對(duì)于內(nèi)存的操作可能存在潛在問(wèn)題,請(qǐng)讀者批評(píng)指正。

    【本文是專欄作者“張梓雄 ”的原創(chuàng)文章,如需轉(zhuǎn)載請(qǐng)通過(guò)與作者聯(lián)系】

    戳這里,看該作者更多好文


    網(wǎng)站名稱:WebAssembly 初體驗(yàn):從零開始重構(gòu)計(jì)算模塊
    網(wǎng)站路徑:http://www.dlmjj.cn/article/djdchje.html