0

0

asm.js & webassembly-WEB的高性能计算

php中世界最好的语言

php中世界最好的语言

发布时间:2017-11-18 14:46:04

|

2456人浏览过

|

来源于php中文网

原创

之前给大家介绍过webworkers-web的高性能计算,关于javascript的知识都很有意思,那么今天给大家说一说asm.js & webassembly与web高性能计算的关系,之前我们说过要解决高性能计算的两个方法,一个是并发用webworkers,另一个就是用更底层的静态语言。

2012年,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应该是一个前景很好的项目。我们可以看一下目前浏览器的支持情况: 

asm.js & webassembly-WEB的高性能计算

安装Emscripten

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

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

2. 通过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

3. 将下列添加到环境变量PATH中

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

4. 其他

我在执行的时候碰到报错说LLVM版本不对,后来参考文档配置了LLVM_ROOT变量就好了,如果你没有遇到问题,可以忽略。

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

5. 验证是否安装好

执行emcc -v,如果安装好会出现如下信息:

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)

Hello, WebAssembly!

创建一个文件hello.c:

#include 
int main() {
  printf("Hello, WebAssembly!\n");
  return 0;
}

编译C/C++代码:

emcc hello.c

上述命令会生成一个a.out.js文件,我们可以直接用Node.js执行:

node a.out.js

输出

Hello, WebAssembly!

为了让代码运行在网页里面,执行下面命令会生成hello.html和hello.js两个文件,其中hello.js和a.out.js内容是完全一样的。

emcc hello.c -o hello.html
 
➜  webasm-study md5 a.out.js
MD5 (a.out.js) = d7397f44f817526a4d0f94bc85e46429
➜  webasm-study md5 hello.js
MD5 (hello.js) = d7397f44f817526a4d0f94bc85e46429

然后在浏览器打开hello.html,可以看到页面

asm.js & webassembly-WEB的高性能计算

前面生成的代码都是asm.js,毕竟Emscripten是人家作者Alon Zakai最早用来生成asm.js的,默认输出asm.js也就不足为奇了。当然,可以通过option生成wasm,会生成三个文件:hello-wasm.html, hello-wasm.js, hello-wasm.wasm。

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

然后浏览器打开hello-wasm.html,发现报错TypeError: Failed to fetch。原因是wasm文件是通过XHR异步加载的,用file:////访问会报错,所以我们需要启一个服务器。

npm install -g serve
serve

然后访问http://localhost:5000/hello-wasm.html,就可以看到正常结果了。

调用C/C++函数

前面的Hello, WebAssembly!都是main函数直接打出来的,而我们使用WebAssembly的目的是为了高性能计算,做法多半是用C/C++实现某个函数进行耗时的计算,然后编译成wasm,暴露给js去调用。

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

开源电子商务系统(网店) iWebShop
开源电子商务系统(网店) iWebShop

iWebShop基于iWebSI框架开发,在获得iWebSI技术平台库支持的条件下,iWebShop可以轻松满足用户量级百万至千万级的大型电子商务网站的性能要求。站点的集群与分布式技术(分布式计算与存储/高可用性/负载均衡)被屏蔽在SI 平台之内,基于iWebShop并且按照SI平台库扩展规范开发的新增功能模块,也将同时获得这种超级计算与处理的能力。作为开源的LAMP电子商务系统,iWebShop

下载
#include 
int add(int a, int b) {
  return a + b;
}
 
int main() {
  printf("a + b: %d", add(1, 2));
  return 0;
}

有两种方法可以把add方法暴露出来给js调用。

通过命令行参数暴露API

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

注意方法名add前必须加_。 然后我们可以在Node.js里面这样使用:

// 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页面使用的话,执行:

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

然后在生成的add.html中加入如下代码:


  

然后点击button,就可以看到执行结果了。

Module.ccall会直接调用C/C++代码的方法,更通用的场景是我们获取到一个包装过的函数,可以在js里面反复调用,这需要用Module.cwrap,具体细节可以参看文档。

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

定义函数的时候添加EMSCRIPTEN_KEEPALIVE

添加文件add2.c。

#include 
#include 
int EMSCRIPTEN_KEEPALIVE add(int a, int b) {
  return a + b;
}
 
int main() {
  printf("a + b: %d", add(1, 2));
  return 0;
}

执行命令:

emcc add2.c -o add2.html

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


  

但是,当你点击button的时候,报错:

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

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

#include 
#include 
 
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来解决,

emcc add2.c -o add2.js -s NO_EXIT_RUNTIME=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代码来优化前一篇中提过的问题。代码很简单:

// file sum.c
#include 
// #include 
 
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看看代码运行多块呢?

➜  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的优化。

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

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

➜  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了。

#include 
#include 
 
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

然后在sum.html中添加代码


  
  
  
另外,我们修改成编译成WebAssembly看看效果呢?
emcc sum.c -o sum.js -s WASM=1

感觉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

居然没什么变化, 大失所望。号称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的文章,有兴趣的朋友可以持续关注。

相关文章

数码产品性能查询
数码产品性能查询

该软件包括了市面上所有手机CPU,手机跑分情况,电脑CPU,电脑产品信息等等,方便需要大家查阅数码产品最新情况,了解产品特性,能够进行对比选择最具性价比的商品。

下载

相关标签:

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
go语言 注释编码
go语言 注释编码

本专题整合了go语言注释、注释规范等等内容,阅读专题下面的文章了解更多详细内容。

2

2026.01.31

go语言 math包
go语言 math包

本专题整合了go语言math包相关内容,阅读专题下面的文章了解更多详细内容。

1

2026.01.31

go语言输入函数
go语言输入函数

本专题整合了go语言输入相关教程内容,阅读专题下面的文章了解更多详细内容。

1

2026.01.31

golang 循环遍历
golang 循环遍历

本专题整合了golang循环遍历相关教程,阅读专题下面的文章了解更多详细内容。

0

2026.01.31

Golang人工智能合集
Golang人工智能合集

本专题整合了Golang人工智能相关内容,阅读专题下面的文章了解更多详细内容。

1

2026.01.31

2026赚钱平台入口大全
2026赚钱平台入口大全

2026年最新赚钱平台入口汇总,涵盖任务众包、内容创作、电商运营、技能变现等多类正规渠道,助你轻松开启副业增收之路。阅读专题下面的文章了解更多详细内容。

76

2026.01.31

高干文在线阅读网站大全
高干文在线阅读网站大全

汇集热门1v1高干文免费阅读资源,涵盖都市言情、京味大院、军旅高干等经典题材,情节紧凑、人物鲜明。阅读专题下面的文章了解更多详细内容。

73

2026.01.31

无需付费的漫画app大全
无需付费的漫画app大全

想找真正免费又无套路的漫画App?本合集精选多款永久免费、资源丰富、无广告干扰的优质漫画应用,涵盖国漫、日漫、韩漫及经典老番,满足各类阅读需求。阅读专题下面的文章了解更多详细内容。

67

2026.01.31

漫画免费在线观看地址大全
漫画免费在线观看地址大全

想找免费又资源丰富的漫画网站?本合集精选2025-2026年热门平台,涵盖国漫、日漫、韩漫等多类型作品,支持高清流畅阅读与离线缓存。阅读专题下面的文章了解更多详细内容。

19

2026.01.31

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
CSS3 教程
CSS3 教程

共18课时 | 5万人学习

Django 教程
Django 教程

共28课时 | 3.7万人学习

Bootstrap 5教程
Bootstrap 5教程

共46课时 | 3.1万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号