Emscripten 中的“Module”变量机制,同时将 Pthread 编译为 worker

Posted

技术标签:

【中文标题】Emscripten 中的“Module”变量机制,同时将 Pthread 编译为 worker【英文标题】:The `Module` variable's mechanism in Emscripten while compiling Pthread to workers 【发布时间】:2020-09-02 03:35:44 【问题描述】:

在将 Pthread 编译为 Web Worker + Wasm 时,我对 Emsripten 中的 Module 变量感到困惑

有一个简单的 Pthread 代码,它只是在每个线程的共享变量 sum 中添加 1。 (最后附上simple.c。)

我们可以使用命令编译Pthread代码:

$ emcc simple.c  -s USE_PTHREADS=1  -s PTHREAD_POOL_SIZE=4 -o simple.html

Emscripten 将生成 simple.htmlsimple.jssimple.worker.jssimple.wasm

simple.worker.js,有一个sn-p:

// simple.worker.js
var Module = ;

// ...

Module['instantiateWasm'] = function(info, receiveInstance) 
  // Instantiate from the module posted from the main thread.
  // We can just use sync instantiation in the worker.
  var instance = new WebAssembly.Instance(Module['wasmModule'], info);
  // We don't need the module anymore; new threads will be spawned from the main thread.
  Module['wasmModule'] = null;
  receiveInstance(instance); // The second 'module' parameter is intentionally null here, we don't need to keep a ref to the Module object from here.
  return instance.exports;
;

注意它在worker中声明了var Module = ,并定义了Module['instantiateWasm']

但是,Module['instantiateWasm'] 只被simple.js 调用,代码 sn-p 如下:

//simple.js

var Module = 

// ...
  if (Module['instantiateWasm']) 
    try 
      var exports = Module['instantiateWasm'](info, receiveInstance);
      return exports;
     catch(e) 
      err('Module.instantiateWasm callback failed with error: ' + e);
      return false;
    
  
// ...

我们可以看到,simple.js 也声明了var Module =

AFAIK,VAR 全局变量无法通过主线程及其工作线程访问。 我不明白为什么simple.js 可以调用Module['instantiateWasm'] 作为simple.jsModulesimple.worker.jsModule 应该不是一回事。


线程代码:

// simple.c
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>

#define NUMTHRDS 4
#define MAGNIFICATION 1e9

typedef struct

    int thread_id;
    double *sum;
 Arg;

pthread_t callThd[NUMTHRDS];
pthread_mutex_t mutexsum;

void *count_pi(void *arg)


    Arg *data = (Arg *)arg;
    int thread_id = data->thread_id;
    double *sum = data->sum;
    pthread_mutex_lock(&mutexsum);
    *sum += 1;
    pthread_mutex_unlock(&mutexsum);

    printf("Thread %d: sum=%f\n", thread_id, *sum);

    pthread_exit((void *)0);


int main(int argc, char *argv[])

    pthread_mutex_init(&mutexsum, NULL);

    pthread_attr_t attr;
    pthread_attr_init(&attr);
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);

    double *sum = malloc(sizeof(*sum));
    *sum = 0;

    Arg arg[NUMTHRDS];
    for (int i = 0; i < NUMTHRDS; i++)
    
        arg[i].thread_id = i;
        arg[i].sum = sum;
        pthread_create(&callThd[i], &attr, count_pi, (void *)&arg[i]);
    

    pthread_attr_destroy(&attr);

    void *status;
    for (int i = 0; i < NUMTHRDS; i++)
    
        pthread_join(callThd[i], &status);
    

    printf("Final Sum =  %f \n", *sum);

    free(sum);

    pthread_mutex_destroy(&mutexsum);
    pthread_exit(NULL);

【问题讨论】:

【参考方案1】:

主程序将自身发送给工作人员。

// simple.js
// Ask the new worker to load up the Emscripten-compiled page. This is a heavy operation.
worker.postMessage(
  'cmd': 'load',
  // If the application main .js file was loaded from a Blob, then it is not possible
  // to access the URL of the current script that could be passed to a Web Worker so that
  // it could load up the same file. In that case, developer must either deliver the Blob
  // object in Module['mainScriptUrlOrBlob'], or a URL to it, so that pthread Workers can
  // independently load up the same main application file.
  'urlOrBlob': Module['mainScriptUrlOrBlob'] || _scriptDir,
  'wasmMemory': wasmMemory,
  'wasmModule': wasmModule,
  'DYNAMIC_BASE': DYNAMIC_BASE,
  'DYNAMICTOP_PTR': DYNAMICTOP_PTR
);

然后工人将其导入。

// simple.worker.js
if (typeof e.data.urlOrBlob === 'string') 
  importScripts(e.data.urlOrBlob);
 else 
  var objectUrl = URL.createObjectURL(e.data.urlOrBlob);
  importScripts(objectUrl);
  URL.revokeObjectURL(objectUrl);

因此Module 不是共享的,而是独立初始化的。

【讨论】:

这太奇怪了。为什么 emscripten 将主 .js 设置为条目;然后用它来设置工人并再次将自己导入工人?我的意思是,我们不能不这样做就将逻辑分开吗? 你知道为什么吗? 我不确定,但分离他们的 ifdef 条件压缩逻辑可能是一项艰巨的工作。 github.com/emscripten-core/emscripten/blob/1.40.1/src/… 我明白了。我认为这会使代码难以阅读。无论如何,非常感谢。

以上是关于Emscripten 中的“Module”变量机制,同时将 Pthread 编译为 worker的主要内容,如果未能解决你的问题,请参考以下文章

emscripten:在 C 中使用全局变量

Emscripten WebAssembly:导出类“Import #13 module="GOT.func" 错误:模块不是对象或函数”

如何使用 emscripten 生成独立的 WebAssembly

如何在 React / React Native 中使用 Emscripten 编译的 JavaScript

带有 SDL 音频的 Emscripten 工作模型

错误:未知参数:Emscripten 中的“-nostdsysteminc”