将 Pthread 编译为 worker 时 Emscripten 中的 `Module` 变量机制
The `Module` variable's mechanism in Emscripten while compiling Pthread to workers
在将 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.html
、simple.js
、simple.worker.js
和 simple.wasm
。
在simple.worker.js
中有一个片段:
// 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
调用,代码片段如下所示:
//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.js
的 Module
和 simple.worker.js
的 Module
应该不是一回事。
线程代码:
// 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);
}
一个主程序将自己发送给worker。
// 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
不是共享的,而是独立初始化的。
在将 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.html
、simple.js
、simple.worker.js
和 simple.wasm
。
在simple.worker.js
中有一个片段:
// 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
调用,代码片段如下所示:
//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.js
的 Module
和 simple.worker.js
的 Module
应该不是一回事。
线程代码:
// 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);
}
一个主程序将自己发送给worker。
// 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
不是共享的,而是独立初始化的。