当前位置: 首页 > article >正文

Emscripten学习笔记之内存模型

 

编译目标选择:

在WebAssembly标准出现前的很长一段时间内,Emscripten的编译目标是asm.js。自1.37.3起,Emscirpten才开始正式支持WebAssembly。

asm.js为编译目标时,C/C代码被编译为.js文件;以WebAssembly为编译目标时,C/C代码被编译为.wasm文件及对应的.js胶水代码文件。两种编译目标从应用角度来说差别不大——它们使用的内存模型、函数导出规则、JavaScript与C相互调用的方法等都是一致的。我们在实际使用中遇到的主要区别在于模块加载的同步和异步:当编译目标为asm.js时,由于C/C++代码被完全转换成了asm.js(JavaScript子集),因此可以认为模块是同步加载的;而以WebAssembly为编译目标时,由于WebAssembly的实例化方法本身是异步指令,因此模块加载为异步加载。

单向透明的内存模型

  • Module.buffer

无论编译目标是asm.js还是wasm,C/C++代码眼中的内存空间实际上对应的都是Emscripten提供的ArrayBuffer对象:Module.bufferC/C++内存地址与Module.buffer数组下标一一对应。

info ArrayBuffer是JavaScript中用于保存二进制数据的一维数组。在本书的语境中,Module.buffer”、“C/C++内存”、“Emscripten堆”三者是等价的。

C/C++代码能直接通过地址访问的数据全部在内存中(包括运行时堆、运行时栈),而内存对应Module.buffer对象,C/C++代码能直接访问的数据事实上被限制在Module.buffer内部,JavaScript环境中的其他对象无法被C/C++直接访问——因此我们称其为单向透明的内存模型。

在当前版本的Emscripten中,指针(既地址)类型为int32,因此单一模块的最大可用内存范围为2GB-1。未定义的情况下,内存默认容量为16MB,其中栈容量为5MB

  • Module.HEAPX

JavaScript中的ArrayBuffer无法直接访问,必须通过某种类型的TypedArray方可对其进行读写。例如下列JavaScript代码创建了一个容量为12字节的ArrayBuffer,并在其上创建了类型为int32的TypedArray,通过该View依次向其中存入了1111111、2222222、3333333三个int32型的数:

var buf = new ArrayBuffer(12);
var i32 = new Int32Array(buf);
i32[0] = 1111111;
i32[1] = 2222222;
i32[2] = 3333333;

tips ArrayBuffer与TypedArray的关系可以简单理解为:ArrayBuffer是实际存储数据的容器,在其上创建的TypedArray则是把该容器当作某种类型的数组来使用。

Emscripten已经为Module.buffer创建了常用类型的TypedArray,见下表:

对象TypedArray对应C数据类型
Module.HEAP8Int8Arrayint8
Module.HEAP16Int16Arrayint16
Module.HEAP32Int32Arrayint32
Module.HEAPU8Uint8Arrayuint8
Module.HEAPU16Uint16Arrayuint16
Module.HEAPU32Uint32Arrayuint32
Module.HEAPF32Float32Arrayfloat
Module.HEAPF64Float64Arraydouble

在JavaScript中访问C/C++内存

我们通过一个简单的例子展示如何在JavaScript中访问C/C++内存。创建C源代码mem.cc如下:

//mem.cc
#include <stdio.h>

int g_int = 42;
double g_double = 3.1415926;

EM_PORT_API(int*) get_int_ptr() {
	return &g_int;
}

EM_PORT_API(double*) get_double_ptr() {
	return &g_double;
}

EM_PORT_API(void) print_data() {
	printf("C{g_int:%d}\n", g_int);
	printf("C{g_double:%lf}\n", g_double);
}

将其编译为mem.jsmem.wasm

JavaScript部分代码如下:

      var int_ptr = Module._get_int_ptr();
      var int_value = Module.HEAP32[int_ptr >> 2];
      console.log("JS{int_value:" + int_value + "}");

      var double_ptr = Module._get_double_ptr();
      var double_value = Module.HEAPF64[double_ptr >> 3];
      console.log("JS{double_value:" + double_value + "}");
      
      Module.HEAP32[int_ptr >> 2] = 13;
      Module.HEAPF64[double_ptr >> 3] = 123456.789      
      Module._print_data();

我们在JavaScript中调用了C函数get_int_ptr(),获取了全局变量g_int的地址,然后通过Module.HEAP32[int_ptr >> 2]获取了该地址对应的int32值。由于Module.HEAP32每个元素占用4字节,因此int_ptr需除以4(既右移2位)方为正确的索引。获取g_double的方法类似不赘述。


http://www.kler.cn/a/158402.html

相关文章:

  • PTA 7-236 验证哥德巴赫猜想
  • Spring系列注解补充
  • 微服务实战系列之Cache(技巧篇)
  • ChatGPT哪些行业需要学习?
  • 数字串最大乘积切分(动态规划)
  • Java分布式系统:了解分布式系统设计和通信协议
  • ReactDomServer 将react组件转化成html静态标签(SSR服务器渲染)
  • 游戏配置表的导入使用
  • windows下DSS界面本地集成linkis管理台
  • 记一次使用Java8新特性中的parallelStream⭐️数据流引发的接口异常以及解决方法
  • 手写promise A+、catch、finally、all、allsettled、any、race
  • 【CTA认证】Android CTA资料及信息安全要求
  • 微信小程序之猜数字和猜拳小游戏
  • Java导出word
  • 为什么游戏公司不太愿意采用“微服务”架构?
  • 【FreeRTOS】消息队列——简介、常用API函数、注意事项、项目实现
  • python中的集合
  • IDEA中,光标移动快捷键(Shift + 滚轮前后滚动:当前文件的横向滚动轴滚动。)
  • 深入理解 Vue 组件:构建优雅的前端应用
  • JAVA代码优化:Token验证处理