@Magneto903

Как скомпилировать этот код c++ в WebAssembly (или почему вывод wasm-файла отличается от вывода c++ программы)?

У меня относительно несложная c++ программа, которая выполняет модифицированный алгоритм Дейкстры с очередью (вершина в очереди может оказаться несколько раз).
https://www.onlinegdb.com/yutiVBNOf
Но я писал эту программу, чтобы ускорить процесс в веб приложении, поэтому я хотел скомпилировать её в WASM-модуль, чтобы потом вызывать из JavaScript'а
Однако, когда я запускаю этот WASM-модуль, он выдает мне не то, что выдавала исходная c++ программа (на тех же входных данных), а совершенно другие (некорректные) данные.

Я тестировал и собирал WASM-модуль при помощи этого сервиса
с++ код, который я пытался скомпилировать в wasm-модуль (левое окно сервиса)

#include <iostream>

using namespace std;


int v[1000];
float cache_1[1000];
int cache_2[1000];
int s[5000];
int e[5000];
float l[5000];
int graph_1[1000][20];
float graph_2[1000][20];

float queue_1[1000];
int queue_2[1000];
int queue_ids[1000];

int links_1[1000][20];
float links_2[1000][20];


void init_arr() {
  for (int i=0; i < 1000; ++i) {
    v[i] = -2;
    cache_1[i] = -2.0;
    cache_2[i] = -2;
  }
  for (int i=0; i < 5000; i++) {
    s[i] = -2;
    e[i] = -2;
    l[i] = -2.0;
  }
  for (int i=0; i < 1000; i++) {
    for (int j=0; j < 20; j++) {
      graph_1[i][j] = -2;
      graph_2[i][j] = -2.0;
    }
  }
}

void add_point(int i, int point) {
  v[i] = point;
}

void add_edge(int i, int s_p, int e_p, float edge_len) {
  s[i] = s_p;
  e[i] = e_p;
  l[i] = edge_len;
}

void fill_graph() {
  for (int i=0; i < 5000; i++) {
    int start = s[i];
    int end = e[i];
    float dist = l[i];
    if (start == -2) {
      break;
    }
    // найдём место, куда вставить
    int j = 0;
    while (graph_1[start][j] > -2) {
      j++;
    }
    graph_1[start][j] = end;
    graph_2[start][j] = dist;
    j=0;
    while (graph_1[end][j] > -2) {
      j++;
    }
    graph_1[end][j] = start;
    graph_2[end][j] = dist;
  }
}

void get_dists(int a) {
  
  int L=0;
  for (int i=0; i<1000; i++) {
    if (graph_1[i][0] > -2) {
      L++;
    } else {
      break;
    }
  }
  
  for (int i=0; i < 1000; i++) {
    for (int j=0; j < 20; j++) {
      links_1[i][j] = -2;
      links_2[i][j] = -2.0;
    }
  }
  
  int i = L;
  
  while (--i >= 0) {
    
    cache_1[i] = 100000.0;
    cache_2[i] = i;
    for (int j=0; j < 20; j++) {
      
      links_1[i][j] = graph_1[i][j];
      links_2[i][j] = graph_2[i][j];
    }
  }
  
  
  float root_1 = cache_1[a];
  int root_2 = cache_2[a];
  
  cache_1[a] = 0;
  cache_2[a] = -1;
  

  root_1 = 0.0;
  root_2 = -1;
  
  
  
  for (int j=0; j < 1000; j++) {
    queue_1[j] = -2.0;
    queue_ids[j] = -2;
  }
  
  queue_1[0] = root_1;
  queue_ids[0] = 0;
  
  i=0;
  int len = 1;
  
  
  while (i < len) {
    float node_1 = queue_1[i];
    int node_id = queue_ids[i];
    
    int node_verts_len = 0;
    
    for (int j=0; j < 20; j++) {
      if (links_1[node_id][j] > -2) {
        node_verts_len++;
      }
    }
    
    int j = node_verts_len-1;
    while (j >= 0) {
      
      int link_vert = links_1[node_id][j];
      float link_dist = links_2[node_id][j];

      float c1 = cache_1[link_vert];
      int c2 = cache_2[link_vert];
      
      float d = node_1 + link_dist;
      
      if (d < c1) {
        cache_1[link_vert] = d;
        cache_2[link_vert] = node_id;
        
        queue_1[len] = d;
        queue_ids[len] = link_vert;
        len+=1;
      }
      j--;
    }
    i++;
  }
}


float get_cache_1(int i) {
  return cache_1[i];
}

int get_cache_2(int i) {
  return cache_2[i];
}



int main() {
    
    init_arr();
    
    add_point(0, 0);
    add_point(1, 1);
    add_point(2, 2);
    add_point(3, 3);
    add_point(4, 4);
    
    add_edge(0, 0, 2, 1);
    add_edge(1, 0, 1, 1);
    add_edge(2, 1, 2, 1);
    add_edge(3, 2, 3, 1);
    add_edge(4, 2, 4, 1);
    add_edge(5, 3, 4, 1);
    
    fill_graph();
    
    get_dists(0);
    
    
    for (int i=0; i < 5; i++) {
        //cout << i << " " << get_cache_1(i) << " " << get_cache_2(i) << endl;
    }
    
  return 0;
}



JS-код, который вызывает wasm-модуль (правое окно сервиса)

var wasmModule = new WebAssembly.Module(wasmCode);
var wasmInstance = new WebAssembly.Instance(wasmModule, wasmImports);
var wasm_module = wasmInstance.exports

wasm_module.main();

for (var i=0; i < 5; i++) {
  var c1 = wasm_module['_Z11get_cache_1i'](i);
  var c2 = wasm_module['_Z11get_cache_2i'](i);
  
  log(i+" " +c1 + " " + c2)
}



Как мне всё-таки добиться, чтобы wasm-модуль работал правильно?
Сейчас он выдает:
0 0 -1
1 100000 1
2 100000 2
3 100000 3
4 100000 4

А должен выдавать это:
0 0 -1                                                                                                                                        
1 1 0                                                                                                                                         
2 1 0                                                                                                                                         
3 2 2                                                                                                                                         
4 2 2
  • Вопрос задан
  • 179 просмотров
Решения вопроса 1
@none7
Действительно проблема в WasmFiddle. Он генерирует код, который нуждается в функции memcpy(импорт), но не реализует её в импорте. Добавил простенькую функцию:
void* memcpy(char* dst, char* src, int count) {
  while(count--) *dst++ = *src++;
}

И чуть изменил JS-код
var wasmModule = new WebAssembly.Module(wasmCode);
var wasmInstance = new WebAssembly.Instance(wasmModule, wasmImports);
var wasm_module = wasmInstance.exports

wasm_module.main();

for (var i=0; i < 5; i++) {
  var c1 = wasm_module.get_cache_1(i);
  var c2 = wasm_module.get_cache_2(i);
  
  log(i+" " +c1 + " " + c2);
}

Выводит в точности то, что Вы ожидаете.
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы