为您写的网页,前端高质量计算之一

前端高品质总结之一: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,质量差不离是原生代码的50%。我们可以看看这个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有道是是一个前景很好的项目。大家能够看一下此时此刻浏览器的支持情况:
美高梅开户网址 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。

什么是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了。
美高梅开户网址 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()方法

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 * 10000000))//
在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的特性:数组解构。本来那是很粗略的表征,现在多数浏览器都已经帮衬了,可是我当下安顿的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(64位)测试,上诉代码居然只要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,可以看看页面
美高梅开户网址 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 收藏
    评论

美高梅开户网址 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

累加文书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;
}

实践结果大致要正常一些了。

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的50%速度么,那个倒是好像达到了。不过现年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 赞 收藏
    评论

美高梅开户网址 5

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图