image Привет, Хабр! Обращаем ваше внимание на одну новинку (сдана в типографию), доступную уже сейчас для покупки в электронном виде.

Книга была написана, чтобы помочь вам понять, что такое WebAssembly, как он работает и что с ним можно и нельзя сделать. Она показывает разные варианты сборки модуля WebAssembly в зависимости от ваших потребностей. Мы начинаем с простых примеров и затем переходим к более сложным темам, например к динамическому связыванию, параллельной обработке и отладке.

Книга предназначена для разработчиков, имеющих базовое знание C и C++, JavaScript и HTML. По WebAssembly есть информация в Интернете, однако она частично устарела и обычно не очень подробна и не освещает сложные темы.. В этой книге информация подана в удобочитаемом формате, который поможет как начинающим, так и опытным разработчикам создавать модули WebAssembly и взаимодействовать с ними.

Вашему внимаю предлагаю отрывок из книги.

Создание вашего первого модуля WebAssembly


В текущей главе вы напишете код на C и затем примените набор инструментальных средств Emscripten, чтобы скомпилировать его в модуль WebAssembly. Это позволит нам рассмотреть три подхода к данному набору инструментов, с помощью которых мы можем создавать модули WebAssembly. Чтобы дать вам представление о том, что позволяет сделать этот набор, в WebAssembly с помощью Emscripten были перенесены некоторые объекты, в том числе Unreal Engine 3, SQLite и AutoCAD.

Набор инструментальных средств EMSCRIPTEN

Набор инструментальных средств Emscripten на данный момент самый развитый из доступных инструментариев для компиляции кода на C или C++ в байт-код WebAssembly. Изначально он был создан для транспиляции такого кода в asm.js. Когда началась работа над MVP WebAssembly, Emscripten был выбран, поскольку он использует компилятор LLVM, а рабочая группа WebAssembly уже имела опыт работы с LLVM при разработке Google Native Client (PNaCl). Emscripten по-прежнему подходит для транспиляции кода на C и C++ в asm.js, но вы будете применять его для компиляции написанного вами кода в модули WebAssembly.

Как описано в главе 1, у компиляторов обычно есть фронтенд-часть, которая конвертирует исходный код в промежуточное представление (IR), и бэкенд-часть, предназначенная для конвертации IR в нужный машинный код, как показано на рис. 3.1.

image

Я уже упоминал, что Emscripten задействует компилятор LLVM — инструментальные средства этого компилятора на данный момент лучше всех поддерживают WebAssembly, к тому же он удобен тем, что к нему можно подключить несколько фронтенд- и бэкенд-частей. Компилятор Emscripten использует Clang, похожий на GCC в C++, в качестве компилятора фронтенд-части для конвертации кода на C или C++ в IR LLVM, как показано на рис. 3.2. Затем Emscripten конвертирует IR LLVM в двоичный байт-код, представляющий собой виртуальный набор инструкций, понятных браузерам, поддерживающим WebAssembly. Поначалу это может звучать немного пугающе, но, как вы увидите в данной главе, процесс компиляции кода на C или C++ в модуль WebAssembly — это простая команда в окне консоли.

Прежде чем продолжить, пожалуйста, ознакомьтесь с приложением А, чтобы установить Emscripten, и убедитесь в наличии у вас всех инструментов, которые понадобятся в этой книге. Установив их, можете переходить к следующему разделу.



Модули WEBASSEMBLY

Когда файл WebAssembly загружается браузером, поддерживающим WebAssembly, браузер проводит проверку с целью убедиться, что все в правильном порядке.. Если файл прошел проверку, то браузер скомпилирует байт-код в машинный код устройства, как показано на рис. 3.3.



И двоичный файл WebAssembly, и скомпилированный объект в браузере называются модулями. Хотя вы можете создать и пустой модуль, он не будет особенно полезен, поэтому у большинства модулей есть хотя бы одна функция, производящая некую обработку. Функции модулей могут быть встроенными, импортированными из экспорта других модулей или даже импортированными из JavaScript.

В модулях WebAssembly есть несколько разделов, которые Emscripten наполнит, основываясь на вашем коде на C или C++. C точки зрения внутреннего устройства, разделы начинаются с ID раздела, за которым идет его размер и затем само содержимое. Подробную информацию о разделах модуля можно найти в главе 2.. Все разделы необязательны, поэтому модуль и может оказаться пустым.

Раздел «Старт» указывает на индекс функции, являющейся частью модуля (неимпортированной). Указанная функция будет автоматически вызвана прежде, чем любой экспортированный объект сможет быть вызванным JavaScript. Если включить в код на C или C++ функцию main, то Emscripten установит ее в качестве стартовой функции модуля.

Модуль WebAssembly получает используемую память от хоста в форме ArrayBuffer. В том, что касается модуля, буфер ведет себя как область динамической памяти в C или C++, но каждый раз, когда модуль взаимодействует с памятью, фреймворк WebAssembly проверяет, находится ли запрос в границах массива.

Модули WebAssembly поддерживают только четыре типа данных:

  • 32-битные целые числа;
  • 64-битные целые числа;
  • 32-битные числа с плавающей запятой;
  • 64-битные числа с плавающей запятой.

Булевы значения представлены с помощью 32-битного целого числа, где 0 — false, а значение nonzero — true. Все остальные типы значений, определенные средой хоста, такие как строки, необходимо представлять в линейной памяти модуля.

Файлы WebAssembly содержат двоичный байт-код, предназначенный не для чтения человеком, а для того, чтобы быть максимально эффективным и быстро загружаться, компилироваться и создавать экземпляр модуля. В то же время модули WebAssembly не предназначены быть «черными ящиками», в которых разработчики могут скрыть свой код. WebAssembly был разработан с учетом открытости Интернета, поэтому его двоичный формат имеет эквивалентное представление в текстовом формате. Последний можно увидеть, если зайти в инструменты разработчика в браузере.

Модули WebAssembly имеют несколько преимуществ.

  • Они созданы для того, чтобы быть целевым форматом при компиляции, в отличие от JavaScript. Это позволит в будущем улучшать WebAssembly, не затрагивая JavaScript.
  • Они разработаны переносимыми, то есть их можно использовать не только в браузерах. Сейчас модули WebAssembly можно применять и в Node.js.
  • Файлы WebAssembly используют двоичный формат, поэтому максимально компактны и их можно быстро передавать и загружать.
  • Файлы структурированы таким образом, чтобы валидация происходила за один проход, что уменьшает время загрузки.
  • С помощью последних функций API WebAssembly на JavaScript файл можно скомпилировать в машинный код в процессе загрузки, и он будет готов к использованию сразу после ее завершения.
  • Из-за динамической природы JavaScript код нужно выполнить несколько раз, прежде чем скомпилировать его в машинный код. А байт-код WebAssemblyкомпилируется в машинный код сразу же. В итоге первый вызов функции имеет такую же скорость, как и, например, десятый.

    Благодаря предварительной компиляции компилятор может оптимизировать код еще до того, как он появится в браузере.
  • Код WebAssembly запускается почти так же быстро, как нативный код. Поскольку WebAssembly проверяет, правильно ли работает код, наблюдается небольшое снижение производительности в сравнении с работой чисто нативного кода.

Когда не стоит использовать модуль WebAssembly

Несмотря на то что WebAssembly имеет много преимуществ, он не всегда является нужным вариантом. В некоторых обстоятельствах лучше выбрать JavaScript:

  • если код простой, то дополнительная работа по установке инструментальных средств компилятора и написанию на другом языке будет просто пустой тратой сил и времени;
  • хотя сейчас над этим вопросом идет работа и все может измениться, но в данный момент у модулей WebAssembly нет прямого доступа к DOM или любым веб-API.

ОПРЕДЕЛЕНИЕ

DOM, или объектная модель документа, — это интерфейс, который отображает различные аспекты веб-страницы, предоставляя коду на JavaScript возможность взаимодействовать со страницей.

Параметры вывода EMSCRIPTEN


Модули WebAssembly можно создавать разными способами в зависимости от ваших целей.. Можно дать Emscripten команду сгенерировать файл модуля WebAssembly и, в зависимости от параметров, указанных в командной строке, Emscripten может также включить связующий файл JavaScript и файл HTML.

ОПРЕДЕЛЕНИЕ

Связующий файл JavaScript — это файл JavaScript, генерируемый Emscripten. Его содержимое может варьироваться в зависимости от аргументов командной строки. Данный файл содержит код, который будет автоматически загружать файл WebAssembly и обеспечивать его компиляцию и создание экземпляра модуля в браузере. JavaScript также содержит множество вспомогательных функций, чтобы хосту было проще взаимодействовать с модулем и наоборот.

Создать модуль с помощью Emscripten позволяют следующие три подхода.

  • Дать Emscripten команду сгенерировать модуль WebAssembly, связующий файл JavaScript и файл шаблона HTML.

Emscripten, создающий HTML-файл, — это нетипично для производственной системы, но это полезно, если вы изучаете WebAssembly и хотите сосредоточиться на компиляции C или C++, прежде чем погрузиться в детали того, что требуется для загрузки и создания экземпляра модуля. Вдобавок данный метод полезен, если вы хотите поэкспериментировать с фрагментами кода, чтобы отладить что-то или создать прототип. C помощью этого подхода вы можете просто написать код на C или C++, скомпилировать его и затем открыть сгенерированный HTML-файл в браузере, чтобы увидеть результаты.

  • Дать Emscripten команду сгенерировать модуль WebAssembly и связующий файл JavaScript.

В производственной системе обычно используется этот подход, поскольку можно добавить сгенерированный файл JavaScript к новой или уже существующей HTML-странице, просто включив в нее ссылку на данный файл. Этот файл JavaScript автоматически загрузится и создаст экземпляр модуля при загрузке HTML-страницы. У него также есть несколько вспомогательных функций, упрощающих взаимодействие между модулем и вашим JavaScript.

И этот подход, и подход с шаблоном HTML будут включать в себя объекты любой стандартной библиотеки C, если ваш код будет их использовать. Если ваш код не задействует функцию стандартной библиотеки C, но вам нужно включить ее в модуль, то можете использовать параметры, чтобы Emscripten включил нужные вам функции.

  • Дать Emscripten команду сгенерировать только модуль WebAssembly.

Этот подход нужен для динамического связывания двух или более модулей в среде выполнения, но подойдет и для создания минималистического модуля без поддержки стандартных библиотек C и связующего файла JavaScript.

ОПРЕДЕЛЕНИЕ

Более подробно я изложу данную информацию в главах 7 и 8, но сейчас вам нужно знать, что динамическое связывание модулей WebAssembly — это процесс соединения двух или более модулей в среде выполнения, где нераспознанные символы в одном модуле (например, функция) разрешаются в символы, существующие в другом.

Если вашему коду нужно передавать между модулем и JavaScript что-то кроме целых чисел и чисел с плавающей запятой, то ему понадобится управление памятью. Если у вас нет эквивалента из стандартной библиотеки для функций malloc и free, то я не рекомендую этот подход для данного сценария. Линейная память модуля в действительности представляет собой буфер на массиве, переданный модулю при создании экземпляра, поэтому проблемы с памятью не повлияют на браузер или ОС, но могут привести к ошибкам, которые будет сложно отследить.

Кроме случаев динамического связывания, этот подход полезен для изучения того, как вручную загрузить, скомпилировать и создать экземпляр модуля с помощью API WebAssembly на JavaScript, — повторить то, что может сделать за вас связующий код Emscripten. Знание того, что делают функции API WebAssembly на JavaScript, упростит понимание ряда примеров, которые можно найти в Сети.

Поскольку Emscripten не единственный доступный компилятор, который может создавать модули WebAssembly (например, такой компилятор есть и у Rust), то в будущем вы можете решить использовать сторонний модуль, не имеющий кода для загрузки. В какой-то момент вам может потребоваться вручную загрузить и создать экземпляр модуля.

Компиляция C или C++ с помощью EMSCRIPTEN и шаблона HTML


Допустим, вас попросили написать код, который будет определять, какие простые числа существуют в определенном диапазоне чисел. Вы могли написать его на JavaScript, но прочитали, что одна из главных областей, где WebAssembly прекрасно проявляет себя, — это вычисления, и потому решили использовать его для данного проекта.

Вам нужно будет интегрировать проект в существующий сайт, но сначала вы хотите создать модуль WebAssembly, чтобы убедиться, что все работает должным образом. Вы напишете код на C, а затем компилируете его в модуль WebAssembly с помощью Emscripten. Очень удобно, что, как показано на рис. 3.4, Emscripten может генерировать JavaScript, необходимый для загрузки и компиляции модуля WebAssembly, а также создавать HTML-файлы из шаблонов.

Первое, что вам нужно сделать, — создать папку, в которой будут храниться ваши файлы: WebAssembly\Chapter 3\3.4 html_template\.

ПРИМЕЧАНИЕ

В этой книге используются обозначения Windows для разделителей файлов. Пользователям *nix нужно заменить символы \ на /.



Как показано на рис. 3.5, первый шаг процесса — создание кода на C или C++. Создайте файл calculate_primes.c и откройте его. Первое, что вам нужно будет сделать, — включить заголовочный файл для стандартной библиотеки C, стандартной библиотеки ввода-вывода C и библиотеки Emscripten:

#include <stdlib.h>
#include <stdio.h>
#include <emscripten.h>



Следующий шаг — написать вспомогательную функцию IsPrime, которая будет принимать в качестве параметра целочисленное значение, которое вы будете проверять, чтобы понять, простое ли это число. Если да, то функция будет возвращать 1. Если нет, то будет возвращать 0 (ноль).

Простое число — это любое число, которое может делиться без остатка только на 1 и на само себя. За исключением 2, четные числа никогда не являются простыми, поэтому функция может их пропускать. Кроме того, поскольку проверка любого числа выше, чем квадратный корень из него, будет лишней, ваш код может пропускать и эти числа, что сделает код чуть более эффективным.. Основываясь на этом, вы можете создать в файле calculate_primes.c следующую функцию:



Теперь, когда у вас есть функция, которая может определить, является ли значение простым числом, нужно написать код, чтобы пройти в цикле по диапазону чисел, вызвать функцию IsPrime и вывести значение, если это простое число.. Код для этих действий не требует никакого взаимодействия с JavaScript, поэтому включите его в функцию main. Видя функцию main в вашем коде на C или C++, Emscripten укажет ее в качестве стартовой функции модуля. Когда модуль загрузится и скомпилируется, фреймворк WebAssembly автоматически вызовет стартовую функцию.

Вы используете функцию printf в вашей функции main, чтобы передать строки в код Emscripten на JavaScript. Затем данный код отобразит полученные строки в текстовом поле на веб-странице и в окне консоли инструментов разработчика в браузере. В главе 4 вы напишете код, в котором модуль будет взаимодействовать с кодом на JavaScript, что позволит вам лучше разобраться в том, как устроено это взаимодействие.

После функции IsPrime можно написать код, показанный в листинге 3.1, чтобы пройти в цикле от 3 до 100 000 и найти среди этих чисел простые.

На рис. 3.6 показан следующий шаг этого процесса, когда вы командуете компилятору Emscripten конвертировать ваш код на C в модуль WebAssembly. В данном случае вам также нужно, чтобы Emscripten создал связующий файл на JavaScript и файл шаблона HTML.

Для компиляции кода на C в модуль WebAssembly нужно использовать окно консоли, чтобы запустить команду emcc, которая является компилятором Emscripten.. Вместо того чтобы указывать путь к файлам, которые вы хотите скомпилировать, проще перейти в папку WebAssembly\Chapter 3\3.4 html_template\. Откройте окно консоли и перейдите туда.



Команда emcc принимает некоторое количество параметров и флагов.. Хотя их порядок не имеет значения, входные файлы следует указывать в начале. В этом случае нужно поместить calculate_primes.c после emcc.

По умолчанию, если вы не указываете имя выходного файла, Emscripten не будет генерировать HTML-файл, а вместо этого сгенерирует файл WebAssembly a.out.wasm и файл JavaScript a.out.js. Чтобы указать выходной файл, нужно использовать параметр -о (дефис и строчная «о»), а затем желаемое название файла. Чтобы Emscripten создал шаблон HTML, нужно указать название файла с расширением .html.

Запустите следующую команду, чтобы сгенерировать модуль WebAssembly, который связывает файл JavaScript и шаблон HTML. Обратите внимание: это может занять пару минут, если вы впервые запускаете компилятор Emscripten, поскольку он также будет создавать общие ресурсы, которые сможет использовать повторно. Они будут кэшированы, поэтому последующие компиляции будут проходить гораздо быстрее:
emcc calculate_primes.c -o html_template.html

Оформить предзаказ бумажной книги по специальной цене можно на нашем сайте

Комментарии (2)


  1. raamid
    27.09.2021 21:18
    +1

    Скажите пожалуйста, есть ли в данной книге описание работы с другими инструментами, кроме Emscripten? Например WABT или Binaryen.


    1. ph_piter Автор
      28.09.2021 11:31

      WABT есть