C++ към JavaScript с Emscripten

хащак OpenFest15

Автор Борислав Станимиров

C++ към JavaScript?!?!?!?!

C++ към JavaScript.

Няколко думи за мен

  • Борислав aka Боби aka iboB
  • Предимно С++ програмист
  • Предимно програмист на игри
  • Занимавам се с open source
  • Работя в Chobolabs

Малко общи приказки


  • Интро лекция
  • През погледа на предимно С++ програмист
  • ... за съжаление?
  • А вие какви сте?

И към същинската част...

Още не...

Още общи приказки...

JavaScript


  • Единственият език, който върви във всички браузъри
  • Можем да изпълняваме само JS в браузъра, но можем да пишем на всеки език, стига да го компилираме до JS
  • ...което не е новост
    • 2006: Google Web Toolkit (GWT) - Java към JS
    • 2007: pyjamas - Python към JS
    • 2009: CoffeScript - ъъммм... CoffeScript към JS
    • ... и други повече и по-малко незначителни боклици

asm.js


  • Тъй като JS е много фьешън, авторите на браузъри искат да го оптимизират ...с хиляди човекочасове
  • Още през 2000-те се заражда идеята за подмножество на JS, което работи добре с оптимизациите


And thus, asm js was born

Какво е asm.js?


  • Не е асемблер!
  • (Асемблер е WebAssembly, но за него друг път)
  • Той е (work-in-progress) спецификация за използване на подмножество от JS
  • Основни положения
    • Всички integer променливи са силно типизирани
      • Как? С type inferrence: var|0
    • Няма заделяне на памет
      • Как? С typed array heap и views
  • И някои яки браузъри като FireFox и IE имат специални оптимизации за него. Още те чакаме, Chrome

asm.js код


C функцията strlen:

                
  function strlen(ptr) {
    ptr = ptr|0;
    var curr = 0;
    curr = ptr;
    while (MEM8[curr]|0 != 0) {
      curr = (curr + 1)|0;
    }
    return (curr - ptr)|0;
  }
                
                

LLVM


  • Това е компилаторна инфраструктура
  • Front end
    • Компилатор от даден език до Intermediate Form
    • Такива има за C, C++, Fortran, Go, Rust и др.
  • LLVM
    • Превежда IF на оптмизиран гъзарски IF
  • Back end
    • Превежда IF до желан краен формат
    • ... например binaries за дадена платформа
    • И един back end превежда до asm.js...
emscripten

... най-сетне

Emscripten


  • Формално това е llvm back end към asm.js
  • И... уж... би могло да работи за всеки език, който има front end за llvm
  • ...но
    • Езиците се нуждаят от рънтайм библитоека
    • ...a, Emscripten дава такава само за С и С++
  • Реално е С/С++ компилатор до asm.js
Бързодействието на JavaScript с удобството на C++. Kой не би искал това?

Реални данни


 

- Таблица от Алон Закай, © 2014

Наш пример

JS
for
ms
CPP
for
ms




Getting started


  • Инсталиране
  • Компилиране на програми
    • Абсолютно същото, което бихте направили, за да билдвате с clang
    • $ clang => $ emcc
    • makefiles работят
    • CMake работи

Hello world demo

Смесване на C++ и JS

C/C++ погледнат през JS


  • Module е нашият С/C++ свят, погледнат през JS
  • Дава ни елементарен контрол върху програмата
    • Позволява ни да зададем console I/O
    • Command-line arguments
    • Стартираме ли със зареждането на страницата и т.н.
  • Module.cwrap / Module.ccall ни позволяват да викаме С функции от нашата програма
  • Embind
    • Boost.Python или luabind за JS през Emscripten
    • Нещата показани през embind отиват в Module

JS погледнат от С


  • Наблягам на С, а не С++
  • emscripten.h
  • emscripten_run_script - изпълнява стринг
  • EM_ASM... - макроси за улеснено ползване
  • Всяка глобална JS функция е достъпна в С
    • ...и в С++ с   extern "C"
  • JS exceptions директно прелитат през нашите C/C++ функции. Няма начин да ги видим
  • И като става дума за това. Обратното също е вярно - C++ exceptions са невидими за JS

Браузърът като платформа

Константен хийп


  • Ако си спомняте как работи asm.js...
  • Размерът трябва да е ясен по време на компилиране (16 MB или -s TOTAL_MEMORY=12345)
  • -s ALLOW_MEMORY_GROWTH=1 - работи в крайни случаи, но е непрактично, бавно и не-asm.js-но
  • Скрити любопитни факти за emscripten heap-а
    • memory corruption на стероиди
    • null pointer dereference не е грешка
    • edge case, в който emscripten build е по-бърз от native build - много заделяния на памет

File I/O


  • Като цяло, работи отлично с local storage
  • Като цяло, обаче, ресурсите, които искаме да ползваме ги няма на local storage
  • preload и embed - може би приемливи решения
  • emscripten_async_wget - може би наложително решение
  • emscripten_wget - може би неработещо решение

Directory I/O

  • Всички С и С++ функции за работа с директории работят на 100% без проблеми
  • За съжаление такива функции не съществуват
                
void ensure_path_exists(string path) { // Our sad solution to this problem
    auto pos = path.rfind('/');
    if (pos != string::npos) {
        path.erase(pos);
        if(path.empty()) return;
        ensure_path_exists(path); // Recurse for parent directories
        static string format =
            "try {\n"
            "var path = '%s';\n"
            "FS.mkdir(path);\n"
            "} catch(e) {}"; // Ignore exception that it exists
        size_t len = format.length() + path.length();
        char* js = new char[len];
        sprintf(js, format.c_str(), path.c_str());
        emscripten_run_script(js);
        delete[] js;
    }
}
                
                

Multi-threading


  • Късият отговор е: Не
  • Дългият отговор е: Еми-и-и... не съвсе-ем...
  • Worker threads с emscripten_call_worker
    • 100% същите ограничения като worker през JS
  • Хак през Worker threads с SharedArrayBuffer
    • "Работи" и за pthreads и за std::thread
    • Не всеки браузър го поддържа (още)
    • Единственото "споделено" нещо е С++ памет
    • Съответно опити да правите друго в thread-a ще водят до краш

Силно платформени неща


  • html5.h + emscripten.h
    • Дават ни emscripten-specific C++ интерфейс към повечето неща, които браузърът може да прави
    • Достъп до елементи от страницата
    • Вход от устройства като клавиатура и мишка
    • Звуци
    • HTTP заявки (с малко бъгове)
  • И това е за платформата...
Ама мрън мрън мрън! Това значи, че за повечето мулти-платформени приложения, които искам да правя, трябва да добавя един тон emscripten-specific код?

-- анонимен писач на мулти-платформени приложения




За щастие, не, анонимни писачо. Някой вече го е направил.

Библиотеки

  • Множество добри хора, са се усетили че това, което браузърът ни дава, много прилича на съществуващи мулти-платформени библиотеки


Да, аз мога да компилирам libpng с emscripten, но браузърът ми МОЖЕ да борави с png. Не може ли да се възползвам от това?
Да, аз мога да напиша WebGL рисуване за моята игра, но WebGL толкова прилича на OpenGL. Не може ли кодът да ми е един?

Enter Emscripten Ports

Emscripten ports

  • Библиотеки, които официално имат поддръжка или версия специално за emscripten
  • В момента това са:


OpenGL, SDL, SDL2, SDL2_image, SDL2_net, SDL2_mixer, SDL2_ttf, FreeType, Vorbis, Ogg, Bullet, zlib, libpng


Учудващо libjpeg, JsonCpp, curl, OpenSSL още не са в горния списък

Това значи, че, ако софтуерът ви ползва само от тези библиотеки, ще можете да го компилирате за emscripten (почти) без промени

3D Tetris demo

Дебъгване


  • Ако имате опит с TypeScript, CoffeeScript и подобни, знаете че браузърите поддържат source map файлове
  • С компилационна настройка -g4, същото важи и тук
  • За съжаление, обаче не можем да гледаме стойностите на променливите


Debugging demo

Край


Въпроси?


Борислав Станимиров / ibob.github.io / @natcbb


Тази презентация е тук: http://ibob.github.io/slides/emscripten/
Презентацията е лицензирана с Creative Commons Признание 3.0
Creative Commons License