如何将C ++ 11代码编译为Webassembly?

时间:2019-04-03 07:52:38

标签: c++ c++11 clang++ llvm-clang webassembly

我一直试图将以下代码移植到webassembly:

"appAssociation": "AUTO",
"rewrites": [
  {
    "source": "/d/**",
    "dynamicLinks": true
  }
]

我知道代码不是完美的,但是可以使用#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编译为webassembly的文档。

我已经尝试过深入研究Google搜索条件的深度,但是找不到如何从c ++ 11编译Webassembly。我已经安装了clang和nodejs,并且已经安装了emsdk。

2 个答案:

答案 0 :(得分:1)

最好使用emscripten工具链从C ++编译为WebAssembly。

因此,假设emcc在您的路径上,您可以编译代码

emcc -std=c++11 fourd.cpp -o fourd.html

这将创建一个htmljswasm文件。

如果您只对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

答案 1 :(得分:0)

我知道使用clang工具链(实际上已修改)有一些选项可以编译为wasm,例如https://github.com/yurydelendik/wasmception,但实际上我不知道它们有多成熟。

目前可靠的方法是使用emscripten。它在后台将其从C ++编译为asm.js,然后将结果转换为wasm。不一定总能提供最佳性能,但至少两个阶段都非常稳定。 以下是本教程如何使用emscripten发出wasm模块的方法:https://emscripten.org/docs/compiling/WebAssembly.html