如何将 C++11 代码编译为 webassembly?
How do I compile C++11 code to webassembly?
我一直在尝试将以下代码移植到 webassembly:
#include "gmtl/gmtl/Vec.h"
#include "gmtl/gmtl/VecOps.h"
#include "gmtl/gmtl.h"
#include <vector>
#include <random>
#include <iostream>
#include <chrono>
#include <map>
#include <sstream>
#include <ctime>
using namespace std;
using namespace gmtl;
class Settings{
public:
Settings(float _repulsion, float _epsilon, float _inner_distance, float _attraction, float _friction, float _gravity, float _min_start_pos, float _max_start_pos){
repulsion = _repulsion;
epsilon = _epsilon;
inner_distance = _inner_distance;
attraction = _attraction;
friction = _friction;
gravity = _gravity;
min_start_pos = _min_start_pos;
max_start_pos = _max_start_pos;
}
float repulsion;
float epsilon;
float inner_distance;
float attraction;
float friction;
float gravity;
float min_start_pos;
float max_start_pos;
};
class Randomator {
private:
float min;
float max;
public:
Randomator(){
srand(time(NULL));
}
float get(){
return rand() / (RAND_MAX + 1.0f);
}
};
class Vertex {
public:
Vertex(int vertex_id){
Randomator ra;
position = gmtl::Vec3f(ra.get(), ra.get(), ra.get());
id = vertex_id;
}
int id;
gmtl::Vec3f position;
gmtl::Vec3f velocity;
gmtl::Vec3f acceleration;
gmtl::Vec3f repulsion_forces;
gmtl::Vec3f attraction_forces;
static gmtl::Vec3f pairwise_repulsion(const gmtl::Vec3f& one, const gmtl::Vec3f& other, const Settings& settings){
gmtl::Vec3f diff = one - other;
// gmtl::Vec3f diff = *(this->position) - *(other->position);
float abs_diff = length(diff);
return (settings.repulsion /
((settings.epsilon + abs_diff)*(settings.epsilon + abs_diff)) *
(diff / abs_diff));
}
bool operator==(const Vertex& other){
return other.id == id;
}
string toString(){
stringstream ss;
ss << "Vertex " << id;
return ss.str();
}
};
class Edge {
public:
Edge(int edge_id, Vertex* _source, Vertex* _target){
source = _source;
target = _target;
id = edge_id;
};
int id;
Vertex* source;
Vertex* target;
bool directed = false;
string toString(){
stringstream ss;
ss << "Edge " << id;
return ss.str();
}
bool operator==(const Edge& other){
return id == other.id;
}
};
class BarnesHutNode3 {
public:
vector<Vertex> inners; // should probably be a pointer
map<string, BarnesHutNode3*> outers;
gmtl::Vec3f center_sum;
int count;
Settings settings;
BarnesHutNode3(const Settings& _settings) : settings(_settings){
count = 0;
}
gmtl::Vec3f center(){
return this->center_sum / (float)this->count;
}
void place_inner(Vertex& vertex){
this->inners.push_back(vertex);
this->center_sum += vertex.position;
}
void place_outer(Vertex& vertex){
string octant = this->get_octant(vertex.position);
this->outers[octant] = new BarnesHutNode3(settings);
this->outers[octant]->insert(vertex);
}
void insert(Vertex& vertex){
if(this->inners.size() == 0){
this->place_inner(vertex);
}else{
gmtl::Vec3f center = this->center();
gmtl::Vec3f pos = vertex.position;
float distance = sqrt((center[0] - pos[0])*(center[0] - pos[0]) +
(center[1] - pos[1])*(center[1] - pos[1]) +
(center[2] - pos[2])*(center[2] - pos[2]));
if(distance <= settings.inner_distance){
this->place_inner(vertex);
}else{
this->place_outer(vertex);
}
}
this->count++;
}
string get_octant(gmtl::Vec3f& position){
gmtl::Vec3f center = this->center();
string x = center[0] < position[0] ? "l" : "r";
string y = center[1] < position[1] ? "u" : "d";
string z = center[2] < position[2] ? "i" : "o";
return x+y+z;
}
void estimate(Vertex& vertex, gmtl::Vec3f& force, gmtl::Vec3f (*force_fn)(const gmtl::Vec3f& p1, const gmtl::Vec3f& p2, const Settings& settings), const Settings& settings){
if(find(this->inners.begin(), this->inners.end(), vertex) != this->inners.end()){ // todo: make better, maintain a set or something
for(auto i=0; i<this->inners.size(); i++){
if(this->inners[i].id != vertex.id){
gmtl::Vec3f f = force_fn(vertex.position, this->inners[i].position, settings);
force += f;
}
}
}else{
gmtl::Vec3f f = force_fn(vertex.position, this->center(), settings) * (float)this->inners.size();
force += f;
}
for(auto &it : this->outers){
this->outers[it.first]->estimate(vertex, force, force_fn, settings);
}
}
string toString(){
return "BarnesHutNode3";
}
unsigned int size(){
return this->count;
}
};
class Graph {
public:
Graph(const Settings& _settings) : settings(_settings){
vertex_id = 0;
edge_id = 0;
};
void add_vertex(const Vertex& vertex){
V.push_back(vertex);
}
void add_edge(const Edge& edge){
E.push_back(edge);
}
void remove_vertex(Vertex vertex){
V.erase(find(V.begin(), V.end(), vertex));
}
void remove_edge(Edge edge){
E.erase(find(E.begin(), E.end(), edge));
}
int vertex_id = 0;
int edge_id = 0;
vector<Vertex> V;
vector<Edge> E;
Settings settings;
void layout(){
// calculate repulsions
BarnesHutNode3 tree(settings);
for(Vertex& vertex : this->V){
tree.insert(vertex);
}
for(Vertex& vertex : this->V){
vertex.repulsion_forces = gmtl::Vec3f();
tree.estimate(
vertex,
vertex.repulsion_forces,
&Vertex::pairwise_repulsion,
settings);
}
// calculate attractions
for(Edge edge : this->E){
gmtl::Vec3f attraction = (edge.source->position - edge.target->position) * (-1 * settings.attraction);
if(edge.directed){
gmtl::Vec3f sp = edge.source->position;
gmtl::Vec3f tp = edge.target->position;
float distance = sqrt((sp[0] - tp[0])*(sp[0] - tp[0]) +
(sp[1] - tp[1])*(sp[1] - tp[1]) +
(sp[2] - tp[2])*(sp[2] - tp[2]));
gmtl::Vec3f gravity = gmtl::Vec3f(0.0f, settings.gravity/distance, 0.0f);
edge.source->attraction_forces -= attraction;
edge.target->attraction_forces += attraction;
}
}
// update vertices
for(Vertex& vertex : this->V){
gmtl::Vec3f friction = vertex.velocity * settings.friction;
vertex.acceleration += vertex.repulsion_forces - vertex.attraction_forces - friction;
vertex.velocity += vertex.acceleration;
vertex.position += vertex.velocity;
}
}
};
gmtl::Vec3f avg_position(const Graph&);
vector<gmtl::Vec3f> average_positions(int, int, int, const Settings&);
class Experiment {
public:
Experiment(float& _variable, const std::vector<float>& _values, const Settings& settings){
variable = &_variable;
values = _values;
for(auto value : values){
*(this->variable) = value;
this->histories.push_back(average_positions(50, 10, 30, settings));
}
}
float* variable;
std::vector<float> values;
std::vector<std::vector<gmtl::Vec3f> > histories;
static float length(gmtl::Vec3f v){
return sqrt(v[0]*v[0] + v[1]*v[1] + v[2]*v[2]);
}
static gmtl::Vec3f avg_position(const Graph& graph){
gmtl::Vec3f position;
for(auto vertex : graph.V){
position += vertex.position;
}
return position / (float)graph.V.size();
}
static std::vector<gmtl::Vec3f> average_positions(int iterations, int vertices, int edges, const Settings& settings){
vector<gmtl::Vec3f> history;
Graph h(settings);
for(int i=0; i<vertices; i++){
h.add_vertex(Vertex(i));
}
for(int i=0; i<edges; i++){
h.add_edge(Edge(i, &h.V[rand() % h.V.size()], &h.V[rand() % h.V.size()]));
}
for(int i=0; i<iterations; i++){
h.layout();
cout << ".";
history.push_back(avg_position(h));
}
return history;
}
};
class Main {
public:
static int run(int NUM_VERTICES, int NUM_EDGES){
cout << "Welcome to foud.cpp, the meat and bones of social cartography..." << endl;
float _repulsion = 5.0;
float _epsilon = 0.1;
float _inner_distance = 0.36;
float _attraction = 0.0005;
float _friction = 0.60;
float _gravity = 10;
float _min_start_pos = -1.0f;
float _max_start_pos = 1.0f;
srand(time(NULL));
cout << "Creating Randomator ... ";
Randomator ra;
cout << "done." << endl;
cout << "Creating Settings ... ";
Settings settings(
_repulsion,
_epsilon,
_inner_distance,
_attraction,
_friction,
_gravity,
_min_start_pos,
_max_start_pos
);
cout << "done." << endl;
cout << "Creating graph ... ";
Graph graph(settings);
cout << "done." << endl;
cout << "Adding vertices ... ";
for(int i=0; i<NUM_VERTICES; i++){
graph.add_vertex(Vertex(i));
}
cout << "done." << endl;
cout << "Adding edges ... ";
for(int i=0; i<NUM_EDGES; i++){
Vertex source = graph.V[rand() % graph.V.size()];
Vertex temp = graph.V[rand() % graph.V.size()];
while(graph.V.size() && temp.id != source.id){
temp = graph.V[rand() % graph.V.size()];
}
Vertex target = temp;
Edge edge(graph.edge_id++, &source, &target);
graph.add_edge(edge);
}
cout << "done." << endl;
cout << "One layout step ... ";
auto start = std::chrono::high_resolution_clock::now();
graph.layout();
auto stop = std::chrono::high_resolution_clock::now();
cout << "done." << endl;
auto dur = stop - start;
cout << NUM_VERTICES << " vertices and " << NUM_EDGES << " edges took " << (double)dur.count() << "ms" << endl;
return 0;
}
};
int main(int argc, char** argv){
int v = 10000;
int e = v*3;
Main::run(v, e);
}
我知道代码并不完美,但它使用 clang++ -std=c++11 fourd.cpp
进行编译,但我找不到有关如何将 clang 编译为 webassembly 的文档。
我已经尝试深入研究 google 搜索词,但找不到如何从 c++11 编译 webassembly。我已经安装了 clang 和 nodejs,并且已经拉出了 emsdk。
最好使用 emscripten 工具链从 C++ 编译到 WebAssembly。
所以假设 emcc 在你的路径上你可以编译你的代码
emcc -std=c++11 fourd.cpp -o fourd.html
这将创建一个 html
、js
和 wasm
文件。
如果你只对wasm
文件感兴趣你也可以写
emcc -std=c++11 fourd.cpp -o fourd.wasm
但是接下来需要手动加载wasm文件。所以最后一个选项是:
emcc -std=c++11 fourd.cpp -o fourd.js
为您提供一个 js 文件,您可以将其包含在 html
文件中,并且 js
文件会自动加载 wasm
文件。
还值得查看 emscripten 教程,以便为您提供概述:emscripten tutorial
我知道clang工具链有一些选项(实际上是修改过的)可以像https://github.com/yurydelendik/wasmception一样编译成wasm,但实际上我不知道它们有多成熟。
目前可靠的方法是使用emscripten。在底层,它从 C++ 编译为 asm.js,然后将结果转换为 wasm。这并不总能提供最佳性能,但至少两个阶段都非常稳定。
以下是如何使用 emscripten 发出 wasm 模块的教程:https://emscripten.org/docs/compiling/WebAssembly.html
我一直在尝试将以下代码移植到 webassembly:
#include "gmtl/gmtl/Vec.h"
#include "gmtl/gmtl/VecOps.h"
#include "gmtl/gmtl.h"
#include <vector>
#include <random>
#include <iostream>
#include <chrono>
#include <map>
#include <sstream>
#include <ctime>
using namespace std;
using namespace gmtl;
class Settings{
public:
Settings(float _repulsion, float _epsilon, float _inner_distance, float _attraction, float _friction, float _gravity, float _min_start_pos, float _max_start_pos){
repulsion = _repulsion;
epsilon = _epsilon;
inner_distance = _inner_distance;
attraction = _attraction;
friction = _friction;
gravity = _gravity;
min_start_pos = _min_start_pos;
max_start_pos = _max_start_pos;
}
float repulsion;
float epsilon;
float inner_distance;
float attraction;
float friction;
float gravity;
float min_start_pos;
float max_start_pos;
};
class Randomator {
private:
float min;
float max;
public:
Randomator(){
srand(time(NULL));
}
float get(){
return rand() / (RAND_MAX + 1.0f);
}
};
class Vertex {
public:
Vertex(int vertex_id){
Randomator ra;
position = gmtl::Vec3f(ra.get(), ra.get(), ra.get());
id = vertex_id;
}
int id;
gmtl::Vec3f position;
gmtl::Vec3f velocity;
gmtl::Vec3f acceleration;
gmtl::Vec3f repulsion_forces;
gmtl::Vec3f attraction_forces;
static gmtl::Vec3f pairwise_repulsion(const gmtl::Vec3f& one, const gmtl::Vec3f& other, const Settings& settings){
gmtl::Vec3f diff = one - other;
// gmtl::Vec3f diff = *(this->position) - *(other->position);
float abs_diff = length(diff);
return (settings.repulsion /
((settings.epsilon + abs_diff)*(settings.epsilon + abs_diff)) *
(diff / abs_diff));
}
bool operator==(const Vertex& other){
return other.id == id;
}
string toString(){
stringstream ss;
ss << "Vertex " << id;
return ss.str();
}
};
class Edge {
public:
Edge(int edge_id, Vertex* _source, Vertex* _target){
source = _source;
target = _target;
id = edge_id;
};
int id;
Vertex* source;
Vertex* target;
bool directed = false;
string toString(){
stringstream ss;
ss << "Edge " << id;
return ss.str();
}
bool operator==(const Edge& other){
return id == other.id;
}
};
class BarnesHutNode3 {
public:
vector<Vertex> inners; // should probably be a pointer
map<string, BarnesHutNode3*> outers;
gmtl::Vec3f center_sum;
int count;
Settings settings;
BarnesHutNode3(const Settings& _settings) : settings(_settings){
count = 0;
}
gmtl::Vec3f center(){
return this->center_sum / (float)this->count;
}
void place_inner(Vertex& vertex){
this->inners.push_back(vertex);
this->center_sum += vertex.position;
}
void place_outer(Vertex& vertex){
string octant = this->get_octant(vertex.position);
this->outers[octant] = new BarnesHutNode3(settings);
this->outers[octant]->insert(vertex);
}
void insert(Vertex& vertex){
if(this->inners.size() == 0){
this->place_inner(vertex);
}else{
gmtl::Vec3f center = this->center();
gmtl::Vec3f pos = vertex.position;
float distance = sqrt((center[0] - pos[0])*(center[0] - pos[0]) +
(center[1] - pos[1])*(center[1] - pos[1]) +
(center[2] - pos[2])*(center[2] - pos[2]));
if(distance <= settings.inner_distance){
this->place_inner(vertex);
}else{
this->place_outer(vertex);
}
}
this->count++;
}
string get_octant(gmtl::Vec3f& position){
gmtl::Vec3f center = this->center();
string x = center[0] < position[0] ? "l" : "r";
string y = center[1] < position[1] ? "u" : "d";
string z = center[2] < position[2] ? "i" : "o";
return x+y+z;
}
void estimate(Vertex& vertex, gmtl::Vec3f& force, gmtl::Vec3f (*force_fn)(const gmtl::Vec3f& p1, const gmtl::Vec3f& p2, const Settings& settings), const Settings& settings){
if(find(this->inners.begin(), this->inners.end(), vertex) != this->inners.end()){ // todo: make better, maintain a set or something
for(auto i=0; i<this->inners.size(); i++){
if(this->inners[i].id != vertex.id){
gmtl::Vec3f f = force_fn(vertex.position, this->inners[i].position, settings);
force += f;
}
}
}else{
gmtl::Vec3f f = force_fn(vertex.position, this->center(), settings) * (float)this->inners.size();
force += f;
}
for(auto &it : this->outers){
this->outers[it.first]->estimate(vertex, force, force_fn, settings);
}
}
string toString(){
return "BarnesHutNode3";
}
unsigned int size(){
return this->count;
}
};
class Graph {
public:
Graph(const Settings& _settings) : settings(_settings){
vertex_id = 0;
edge_id = 0;
};
void add_vertex(const Vertex& vertex){
V.push_back(vertex);
}
void add_edge(const Edge& edge){
E.push_back(edge);
}
void remove_vertex(Vertex vertex){
V.erase(find(V.begin(), V.end(), vertex));
}
void remove_edge(Edge edge){
E.erase(find(E.begin(), E.end(), edge));
}
int vertex_id = 0;
int edge_id = 0;
vector<Vertex> V;
vector<Edge> E;
Settings settings;
void layout(){
// calculate repulsions
BarnesHutNode3 tree(settings);
for(Vertex& vertex : this->V){
tree.insert(vertex);
}
for(Vertex& vertex : this->V){
vertex.repulsion_forces = gmtl::Vec3f();
tree.estimate(
vertex,
vertex.repulsion_forces,
&Vertex::pairwise_repulsion,
settings);
}
// calculate attractions
for(Edge edge : this->E){
gmtl::Vec3f attraction = (edge.source->position - edge.target->position) * (-1 * settings.attraction);
if(edge.directed){
gmtl::Vec3f sp = edge.source->position;
gmtl::Vec3f tp = edge.target->position;
float distance = sqrt((sp[0] - tp[0])*(sp[0] - tp[0]) +
(sp[1] - tp[1])*(sp[1] - tp[1]) +
(sp[2] - tp[2])*(sp[2] - tp[2]));
gmtl::Vec3f gravity = gmtl::Vec3f(0.0f, settings.gravity/distance, 0.0f);
edge.source->attraction_forces -= attraction;
edge.target->attraction_forces += attraction;
}
}
// update vertices
for(Vertex& vertex : this->V){
gmtl::Vec3f friction = vertex.velocity * settings.friction;
vertex.acceleration += vertex.repulsion_forces - vertex.attraction_forces - friction;
vertex.velocity += vertex.acceleration;
vertex.position += vertex.velocity;
}
}
};
gmtl::Vec3f avg_position(const Graph&);
vector<gmtl::Vec3f> average_positions(int, int, int, const Settings&);
class Experiment {
public:
Experiment(float& _variable, const std::vector<float>& _values, const Settings& settings){
variable = &_variable;
values = _values;
for(auto value : values){
*(this->variable) = value;
this->histories.push_back(average_positions(50, 10, 30, settings));
}
}
float* variable;
std::vector<float> values;
std::vector<std::vector<gmtl::Vec3f> > histories;
static float length(gmtl::Vec3f v){
return sqrt(v[0]*v[0] + v[1]*v[1] + v[2]*v[2]);
}
static gmtl::Vec3f avg_position(const Graph& graph){
gmtl::Vec3f position;
for(auto vertex : graph.V){
position += vertex.position;
}
return position / (float)graph.V.size();
}
static std::vector<gmtl::Vec3f> average_positions(int iterations, int vertices, int edges, const Settings& settings){
vector<gmtl::Vec3f> history;
Graph h(settings);
for(int i=0; i<vertices; i++){
h.add_vertex(Vertex(i));
}
for(int i=0; i<edges; i++){
h.add_edge(Edge(i, &h.V[rand() % h.V.size()], &h.V[rand() % h.V.size()]));
}
for(int i=0; i<iterations; i++){
h.layout();
cout << ".";
history.push_back(avg_position(h));
}
return history;
}
};
class Main {
public:
static int run(int NUM_VERTICES, int NUM_EDGES){
cout << "Welcome to foud.cpp, the meat and bones of social cartography..." << endl;
float _repulsion = 5.0;
float _epsilon = 0.1;
float _inner_distance = 0.36;
float _attraction = 0.0005;
float _friction = 0.60;
float _gravity = 10;
float _min_start_pos = -1.0f;
float _max_start_pos = 1.0f;
srand(time(NULL));
cout << "Creating Randomator ... ";
Randomator ra;
cout << "done." << endl;
cout << "Creating Settings ... ";
Settings settings(
_repulsion,
_epsilon,
_inner_distance,
_attraction,
_friction,
_gravity,
_min_start_pos,
_max_start_pos
);
cout << "done." << endl;
cout << "Creating graph ... ";
Graph graph(settings);
cout << "done." << endl;
cout << "Adding vertices ... ";
for(int i=0; i<NUM_VERTICES; i++){
graph.add_vertex(Vertex(i));
}
cout << "done." << endl;
cout << "Adding edges ... ";
for(int i=0; i<NUM_EDGES; i++){
Vertex source = graph.V[rand() % graph.V.size()];
Vertex temp = graph.V[rand() % graph.V.size()];
while(graph.V.size() && temp.id != source.id){
temp = graph.V[rand() % graph.V.size()];
}
Vertex target = temp;
Edge edge(graph.edge_id++, &source, &target);
graph.add_edge(edge);
}
cout << "done." << endl;
cout << "One layout step ... ";
auto start = std::chrono::high_resolution_clock::now();
graph.layout();
auto stop = std::chrono::high_resolution_clock::now();
cout << "done." << endl;
auto dur = stop - start;
cout << NUM_VERTICES << " vertices and " << NUM_EDGES << " edges took " << (double)dur.count() << "ms" << endl;
return 0;
}
};
int main(int argc, char** argv){
int v = 10000;
int e = v*3;
Main::run(v, e);
}
我知道代码并不完美,但它使用 clang++ -std=c++11 fourd.cpp
进行编译,但我找不到有关如何将 clang 编译为 webassembly 的文档。
我已经尝试深入研究 google 搜索词,但找不到如何从 c++11 编译 webassembly。我已经安装了 clang 和 nodejs,并且已经拉出了 emsdk。
最好使用 emscripten 工具链从 C++ 编译到 WebAssembly。
所以假设 emcc 在你的路径上你可以编译你的代码
emcc -std=c++11 fourd.cpp -o fourd.html
这将创建一个 html
、js
和 wasm
文件。
如果你只对wasm
文件感兴趣你也可以写
emcc -std=c++11 fourd.cpp -o fourd.wasm
但是接下来需要手动加载wasm文件。所以最后一个选项是:
emcc -std=c++11 fourd.cpp -o fourd.js
为您提供一个 js 文件,您可以将其包含在 html
文件中,并且 js
文件会自动加载 wasm
文件。
还值得查看 emscripten 教程,以便为您提供概述:emscripten tutorial
我知道clang工具链有一些选项(实际上是修改过的)可以像https://github.com/yurydelendik/wasmception一样编译成wasm,但实际上我不知道它们有多成熟。
目前可靠的方法是使用emscripten。在底层,它从 C++ 编译为 asm.js,然后将结果转换为 wasm。这并不总能提供最佳性能,但至少两个阶段都非常稳定。 以下是如何使用 emscripten 发出 wasm 模块的教程:https://emscripten.org/docs/compiling/WebAssembly.html