wasm 系列之 WebAssembly 和 emscripten 暴力上手

news2024/9/20 1:10:28

wasm 是什么?

wasm 是 WebAssembly 的缩写。wasm 不是传统意义上的汇编语言,而是一种编译的中间字节码,可以在浏览器和其他 wasm runtime 上运行非 JavaScript 类型的语言,只要能被编译成 wasm,譬如 kotlin/wasm、Rust/wasm 等。

这是一种高度优化的可执行格式,其运行速度几乎与本机代码一样快,同时具有可移植性和安全性。 Emscripten 通过与 LLVM、 Binaryen、 Closure Compiler 和其他工具的集成,自动为您完成大量优化工作。

耳熟详闻的一个典型成功案例就是 Flutter 编译 Web 端应用,本质就是调用 Skia C++ 库,然后通过 wasm 技术提供了一个 CanvasKit 供 js 端使用。

emscripten 是什么?

Emscripten 是一个完整的 WebAssembly 开源编译器工具链。使用 Emscripten 可以将 C/C++ 代码或使用 LLVM 的任何其他语言编译为 WebAssembly,并在 Web、Node.js 或其他 Wasm 运行时上运行。

实际上,任何可移植的 C/C++ 代码库都可以使用 Emscripten 编译成 WebAssembly,从需要渲染图形、播放声音以及加载和处理文件的高性能游戏,到 Qt 等应用程序框架。 Emscripten 已被用于将一长串现实世界代码库转换为 WebAssembly,其生成小而快速的代码!

emscripten 环境准备

首先我们需要用到 Emscripten。Emscripten 是一个编译器工具链,使用 LLVM 去编译出 wasm。

先通过官网方式安装 Emscripten SDK,不同平台详情参见 https://emscripten.org/docs/getting_started/downloads.html。

# Get the emsdk repo
git clone https://github.com/emscripten-core/emsdk.git

# Enter that directory
cd emsdk

# Fetch the latest version of the emsdk (not needed the first time you clone)
git pull

# 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

# On Windows, run emsdk.bat instead of ./emsdk, and emsdk_env.bat instead of source ./emsdk_env.sh.
# On Windows, if you use the activate command, the step of emsdk_env.bat is optional. If you want to know more, see activate SDK version.

Windows 下执行完emsdk_env.bat后再执行emcc -v有可能提示找不到命令,这时候自己把打印的路径加入环境变量下就行。

编写第一个 wasm 程序

写 wasm 的最流行语言是 Rust 和 C/C++。
C/C++ 的轮子比较丰富,比如 Skia(Canvas 底层调用的库)就是 C++ 写的。可惜的是 C/C++ 没有包管理工具。
而当下最炙手可热的当属 Rust,我不得不说它真的很酷,有包管理工具,工具链也很完善。先选择使用 C/C++ 语言,下一篇再使用 Rust。

在这里插入图片描述
上图可以看到,执行完 emcc 对 c 源代码编译后生成了a.out.jsa.out.wasm两个产物文件。其中 js 文件是胶水代码,用来加载和执行 wasm,因为 wasm 不能直接作为入口文件使用,所以上图 node 命令实际执行的是胶水入口,然后胶水入口调用了 wasm 文件。

我们可以通过 file 命令看下这三个文件类型,如图:

在这里插入图片描述

除过使用 nodejs 环境运行外,我们接下来尝试将上面 wasm 跑在浏览器中。

先确保你本地有 nodejs 环境,并且通过npm -g install http-server安装了方便的 http-server,以便下面启动一个 http 服务器解决无法同源策略加载 wasm 文件的问题。

新建一个 html 文件并引入 wasm 的胶水 js 代码,然后启动服务,如图:

在这里插入图片描述

接着在浏览器打开刚编写的网页可以在控制台看到我们前面在 c 语言中编写的 printf 代码输出,如下:

在这里插入图片描述

我们可以继续看下其网络情况,如下:

在这里插入图片描述

emscripten 用法

上面我们快速使用 emscripten 的 emcc 命令,这里我们可以稍微再看下 emscripten 的其他用法,关于更多 emscripten 用法可以参见官方文档https://emscripten.org/docs/getting_started/Tutorial.html,这里不再赘述。

Generating HTML

我们可以将上面 emcc 编译命令换成emcc test.c -o test.html,然后可以一步到位生成网页,产物如下图:

在这里插入图片描述

接着使用浏览器直接打开 html,而不用我们自己再编写 html 引入 wasm 的胶水代码,如下:

在这里插入图片描述

可以看到我们 c 语言打印的 printf 输出已经出现在网页中了。上面网页其实有两部分,下部分是我们控制台输出的显示,上部分其实是一个 Canvas,我们通过下面例子就能在上面绘制彩色立方体的东西,如下:

#include <stdio.h>
#include <SDL/SDL.h>

#ifdef __EMSCRIPTEN__
#include <emscripten.h>
#endif

int main(int argc, char** argv) {
  printf("hello, world!\n");

  SDL_Init(SDL_INIT_VIDEO);
  SDL_Surface *screen = SDL_SetVideoMode(256, 256, 32, SDL_SWSURFACE);

#ifdef TEST_SDL_LOCK_OPTS
  EM_ASM("SDL.defaults.copyOnLock = false; SDL.defaults.discardOnLock = true; SDL.defaults.opaqueFrontBuffer = false;");
#endif

  if (SDL_MUSTLOCK(screen)) SDL_LockSurface(screen);
  for (int i = 0; i < 256; i++) {
    for (int j = 0; j < 256; j++) {
#ifdef TEST_SDL_LOCK_OPTS
      // Alpha behaves like in the browser, so write proper opaque pixels.
      int alpha = 255;
#else
      // To emulate native behavior with blitting to screen, alpha component is ignored. Test that it is so by outputting
      // data (and testing that it does get discarded)
      int alpha = (i+j) % 255;
#endif
      *((Uint32*)screen->pixels + i * 256 + j) = SDL_MapRGBA(screen->format, i, j, 255-i, alpha);
    }
  }
  if (SDL_MUSTLOCK(screen)) SDL_UnlockSurface(screen);
  SDL_Flip(screen);

  printf("you should see a smoothly-colored square - no sharp lines but the square borders!\n");
  printf("and here is some text that should be HTML-friendly: amp: |&| double-quote: |\"| quote: |'| less-than, greater-than, html-like tags: |<cheez></cheez>|\nanother line.\n");

  SDL_Quit();

  return 0;
}

上面官方例子的代码位于https://github.com/emscripten-core/emscripten/blob/main/test/hello_world_sdl.cpp。

我们通过emcc hello_world_sdl.cpp -o hello.html编译然后浏览器打开产物网页,如下:

在这里插入图片描述

官方还有很多 Canvas 有趣例子,自己可以探索一下。

Using files

先放官方文档这句最重要的话:

Your C/C++ code can access files using the normal libc stdio API (fopen, fclose, etc.)

JavaScript 通常在 web 浏览器的沙盒环境中运行,不直接访问本地文件系统。Emscripten模拟了一个虚拟的文件系统使得我们可以使用普通的 libc stdio API。我们要访问的文件应该预加载或嵌入到这个虚拟文件系统中。

官方文件系统架构机制如下图:

在这里插入图片描述

我们来看一段名叫hello_world_file.cpp的 cpp 代码:

#include <stdio.h>

int main() {
  FILE *file = fopen("hello_world_file.txt", "rb");
  if (!file) {
    printf("cannot open file\n");
    return 1;
  }
  while (!feof(file)) {
    char c = fgetc(file);
    if (c != EOF) {
      putchar(c);
    }
  }
  fclose (file);
  return 0;
}

官方这个例子的源码位于https://github.com/emscripten-core/emscripten/blob/main/test/hello_world_file.cpp。

下面的命令用于指定要在运行编译后的代码之前先预加载到 Emscripten 的虚拟文件系统中的数据文件。这种方法很有用,因为浏览器只能从网络异步加载数据(Web Workers除外),而许多本机代码使用同步文件系统访问。

emcc hello_world_file.cpp -o hello.html --preload-file hello_world_file.txt

在这里插入图片描述

运行效果如下:

在这里插入图片描述

Optimizing code

与gcc和clang一样,Emscripten在默认情况下生成未优化的代码。你可以使用- 01命令行参数生成稍微优化的代码:

emcc -O1 test/hello_world.cpp

还有其他-O2-O3-Og-Os-Oz等都和 clang 等一样的含义,不再赘述。

总结

到此我们算是入门了 wasm(WebAssembly) 和 emscripten,其他高级进阶请关注和作者链接等待更新,欢迎期待~

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1613036.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

鸿蒙OpenHarmony【轻量系统编写“Hello World”程序】 (基于Hi3861开发板)

编写“Hello World”程序 下方将通过修改源码的方式展示如何编写简单程序&#xff0c;输出“Hello world”。请在下载的源码目录中进行下述操作。 前提条件 已参考鸿蒙开发指导文档&#xff1a;gitee.com/li-shizhen-skin/harmony-os/blob/master/README.md点击或者复制转到…

编写函数fun,它的功能是:根据以下公式求P的值,结果由函数值带回。m与n为两个正整数且要求m>n

本文收录于专栏:算法之翼 https://blog.csdn.net/weixin_52908342/category_10943144.html 订阅后本专栏全部文章可见。 本文含有题目的题干、解题思路、解题思路、解题代码、代码解析。本文分别包含C语言、C++、Java、Python四种语言的解法完整代码和详细的解析。 题干 编写…

【JavaEE初阶系列】——网络层IP协议(地址管理和路由选择)

目录 &#x1f6a9;网络层 &#x1f388;IP协议 &#x1f469;&#x1f3fb;‍&#x1f4bb;IP协议"拆包组包"功能 &#x1f388;地址管理 &#x1f469;&#x1f3fb;‍&#x1f4bb;IP地址的分类 &#x1f469;&#x1f3fb;‍&#x1f4bb;NAT机制如何工作的…

记录:阿里云服务器网站搭建(2)

Docker安装Mysql mysql版本 查看开发环境中mysql版本 &#xff1a;select version()&#xff1b;安装时版本尽量保证一致&#xff0c;最低要求大版本要一致 docker 拉取mysql镜像 docker pull mysql:8.0.36 docker启动mysql容器 docker run -d \ # 创建并运行一个容器&…

下班族张亮的副业赚钱故事

张亮是一个普通的上班族&#xff0c;每天过着朝九晚五的生活。他渴望改变现状&#xff0c;却又觉得生活缺乏突破口。直到有一天&#xff0c;他在网络上偶然发现了水牛社这个平台&#xff0c;这为他打开了一扇新的大门。 张亮开始利用下班后的空闲时间&#xff0c;认真浏览水牛社…

IDEA下载与安装

1.下载 链接&#xff1a;百度网盘 请输入提取码 提取码&#xff1a;7v5q 2.安装

综合案例(前端代码练习):猜数字和表白墙

目录 一、猜数字 html代码&#xff1a; 点击 猜 按钮的js代码&#xff1a; 点击 重开游戏 按钮的js代码&#xff1a; 整体代码&#xff1a; 页面效果&#xff1a; 二、留言板 css代码&#xff1a; html代码&#xff1a; js代码&#xff08;主逻辑在这&#xff09;&am…

JAVA高阶私房菜:JVM虚拟机核心概念及参数微调实验

目录 基础快速掌握 什么是JVM虚拟机 JVM的的实现 操作系统-虚拟机-JRE-JDK的关系 生产环境部署JDK还是JRE JVM内存组成部分和堆空间分布 内存组成 堆空间内存分布 内存分布 堆空间分配 JVM堆空间垃圾回收流程及JVM参数 垃圾回收流程 JVM参数分类 JVM参数格式分类 …

山东大学操作系统实验一(Linux虚拟机实现)

目录 实验题目 实验要求 示例程序 主程序 头文件 重点代码解析 一、main函数的参数 参数介绍 参数输入方式 本块代码 二、信号处理 本块代码 原理介绍 实现效果 三、kill函数 功能介绍 使用方式 本块代码 四、头文件处理 本块代码 代码作用 实验程序 …

Python数据可视化:频率统计条形图countplot()

【小白从小学Python、C、Java】 【计算机等考500强证书考研】 【Python-数据分析】 Python数据可视化&#xff1a; 频率统计条形图 countplot() [太阳]选择题 请问关于以下代码表述正确的选项是&#xff1f; import seaborn as sns import matplotlib.pyplot as plt data { …

二、python+前端 实现MinIO分片上传

python前端 实现MinIO分片上传 一、背景二、流程图三、代码 一、背景 问题一&#xff1a;前端 -> 后端 ->对象存储 的上传流程&#xff0c;耗费带宽。 解决方案&#xff1a;上传流程需要转化为 前端 -> 对象存储&#xff0c;节省上传带宽 问题二&#xff1a;如果使用…

Leetcode 第394场周赛 问题和解法

题目 统计特殊字母的数量 I 给你一个字符串word。如果word中同时存在某个字母的小写形式和大写形式&#xff0c;则称这个字母为特殊字母。 返回word中特殊字母的数量。 示例 1: 输入&#xff1a;word "aaAbcBC"输出&#xff1a;3解释&#xff1a;word 中的特殊…

【Entity Framework】聊一聊EF如何使用数据库函数

【Entity Framework】聊一聊EF如何使用数据库函数 文章目录 【Entity Framework】聊一聊EF如何使用数据库函数一、数据库函数的类型二、内置函数与用户定义的函数四、聚合函数、标量函数和表值函数五、Niladic函数六、EF Core 中的数据库函数映射6.1 内置函数映射6.2 EF.Functi…

【iOS开发】(四)react Native第三方组件五个20240419-20

react native 外的 第三方组件 目录标题 react native 外的 第三方组件&#xff08;一&#xff09;与rn核心组件的使用步骤区别&#xff1a;&#xff08;二&#xff09;第三方组件概览1 WebView2 Picker3 Swiper4 AsyncStorage5 Geolocation6 Camera (三)详细学习1 WebViewCoco…

ROS1快速入门学习笔记 - 01Linux基础

目录 一、Linux极简基础 二、C与Python极简基础 1. for循环 2. while循环 3. 面向对象 一、Linux极简基础 终端快捷键&#xff1a;ctrlaltt 命令行的操作方式 查看当前终端所在路径&#xff1a;pwd切换路径cd&#xff1b;例如cd /home/ 进入home文件夹&#xff1b;cd …

Oracle Hint 语法详解

什么是Hint Hint 是 Oracle 提供的一种 SQL 语法&#xff0c;它允许用户在 SQL 语句中插入相关的语法&#xff0c;从而影响 SQL 的执行方式。 因为 Hint 的特殊作用&#xff0c;所以对于开发人员不应该在代码中使用它&#xff0c;Hint 更像是 Oracle 提供给 DBA 用来分析诊断问…

2024数学建模时间汇总与竞赛攻略

目录 2024数学建模汇总&#xff08;时间、报名费、获奖率、竞赛级别、是否可跨校&#xff09; 中国高校大数据挑战赛 “华数杯”国际大学生数学建模竞赛 美国大学生数学建模竞赛&#xff08;美赛&#xff09; 数学中国&#xff08;认证杯&#xff09;数学建模网络挑战赛 …

从国九条的颁布简单看待未来的因子轮动

上周4月12日《关于加强监管防范风险推动资本市场高质量发展的若干意见》又称国九条出台后&#xff0c;除了本周五中东局势对大盘的影响&#xff0c;本周一波三折的行情很大程度上都是围绕着国九条展开的。一个很有意思的现象是前两次国九条发布后&#xff0c;市场都诞生了波澜壮…

【Linux开发 第八篇】定时任务

定时任务 crond任务调度at定时任务 crond任务调度 任务调度&#xff1a; 是指系统在某个时间执行特定的命令或程序 任务调度分类&#xff1a; 系统工作&#xff1a;有些重要的工作必须周而复始地执行&#xff0c;如病毒扫描等 个别用户工作&#xff1a;个别用户可能希望执行某…

107页 | 企业数字化转型规划设计(免费下载)

【1】关注本公众号&#xff0c;转发当前文章到微信朋友圈 【2】私信发送 【企业数字化转型规划设计】 【3】获取本方案PDF下载链接&#xff0c;直接下载即可。 如需下载本方案PPT原格式&#xff0c;请加入微信扫描以下方案驿站知识星球&#xff0c;获取上万份PPT解决方案&…