СтатьиАрхив статей

Перевод: Мациевский Николай aka sunnybear
Опубликована: 19 августа 2009

Вычисляем при помощи Web Workers

Примечание: ниже находится частичный перевод заметки от John Resig "Computing with JavaScript Web Workers", в ней автор раскрывает особенности встроенного в браузеры механизма «отложенных» вычислений при помощи JavaScript и его будущие перспективы. Мои комментарии далее курсивом.

Последняя спецификация Web Workers, несомненно, является наиболее перспективной из грядущих нововведений в браузерах, которое позволяет исполнять JavaScript-задачи параллельно, не блокируя интерфейс браузера.

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

Имея в виду текущее положение вещей, давайте углубимся в спецификацию Web Workers.

Web Workers

Рекомендация Web Worker частично основывается на уже проделанной работе со стороны команды Google Gears относительно модуля WorkerPool. Это идея существенно выросла и была значительно доработана, чтобы стать рекомендацией.

Worker — это скрипт, который может быть загружен и исполнен в фоновом режиме. Web Workers позволяют легко это сделать, например:

new Worker("worker.js");

В этом примере будет загружен скрипт, расположенный по адресу worker.js, а его выполнение будет произведено в фоновом режиме (скорее всего, браузеры будут использовать встроенные средства операционной системы — нити — для реализации такого поведения).

Однако, есть несколько серьезных подводных камней:

  1. Worker не имеет доступа к DOM. Никакого document, getElementById и т.д. (Наиболее важными исключениями из этого правила будут методы setTimeout, setInterval и XMLHttpRequest.)
  2. У worker нет прямого доступа к родительской странице.

Имея в виду эти ограничения, стоит сразу задаться вопросом: а для чего, собственно, может пригодиться worker и какие задачи он способен решать?

Вы можепте использовать Worker, обмениваясь с ним сообщениями. Все браузеры (которые поддерживают данную спецификацию) позволяют обмениваться строковыми сообщениями (Firefox 3.5 также поддерживает обмен JSON-совместимыми объектами). Сообщение как может быть отослано Worker, так и Worker может ответить родительской странице таким сообщением. Ниже приведен пример обмена сообщениями.

Обмен сообщениями производится при помощи API postMessage следующим образом:

var worker = new Worker("worker.js");
// Ждем сообщений от worker
worker.onmessage = function(e){
// Сообщение от клиента:
    e.data;
};
worker.postMessage("start");

Клиент:

onmessage = function(e){
    if ( e.data === "start" ) {
// Выполняем какие-нибудь вычисления
	done();
    }
};
function done(){
// Отправляем полученный результат на главную страницу
    postMessage("done");
}

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

Прямо сейчас Web Workers присутствуют в Firefox 3.5 и Safari 4. Они также заявлены в последних ночных сборках Chromium. Наверное, большинство тут же подумают: что нам до возможности, которая доступна лишь для небольшой части веб-пользователей (только в двух современных браузерах!), но это не должно быть проблемой. Web Workers позволяют эффективно использовать пользовательские машины для параллельных вычислений. В такой ситуации вы можете создавать две версии своих приложений (одну для старых браузеров, и одну — для запуска через механизм Web Workers при его наличии в браузерах). В новых браузерах это просто будет работать значительно быстрее.

Некоторые занимательные примеры использования данного механизма, которые используют заявленное API.

Расчет освещения (RayTracing)

Расчет освещения

Этот пример использует Canvas для отрисовки рассчитанной сцены. Если включить Web Workers, то заметно, как картинка отрисовывается по частям. Это происходит благодаря разбиению всей работы на части и поручение каждого набора пикселей отдельному Worker. Этот Worker затем падает массив цветов для отрисовки на Canvas, а родительская страница их применяет. (Заметьте, сам по себе Worker ничего не изменяет.)

Отслеживание движения

Отлеживание движения

(Требуется Firefox 3.5. Более подробно об этом примере.) В этом случае применяется несколько технологий: элемент video, элемент canvas и отрисовка кадров видео на сам холст. Все отслеживание движения производится в фоновом режиме при помощи Web Workers (поэтому передача видео не останавливается и не блокируется).

Эмуляция огня

Эмуляция огня

Этот пример пытается ограничить несколько случайных точек, используя алгоритм эмуляции огня (simulated annealing) (более подробная информация). Также здесь приведено анимированное PNG-изображение (работает в Firefox 3.5), которое вращается, пока идет вычисление в фоновом режиме.

Вычисление при помощи JavaScript Web Workers

Недавно закончилось интересное соревнование от Engine Yard. В качестве задания необходимо было подобрать фразу, которая бы давала наиболее близкий к исходному SHA1-хэш, расстояние между хэшами вычислялась по Хэммингу (число отличающихся битов).

Наибольший интерес представляло 2 участника, которые попытались решить эту задачу при помощи JavaScript (1, 2). Их программы запускались в браузере и использовали пользовательский компьютер для проведения вычислений. Скорее всего, никто из них так и не добился впечатляющего результата (если брать во внимание тех кластерных монстров, с которыми пришлось соревноваться), но сам подход вызывает неподдельный интерес.

В обоих случаях (если верить исходному коду) используется почти одна и та же тактика: рассчитываются несколько результатов, разделяемых по таймеру. Скорость подбора варьируется от браузера к браузеру на уровне 1000-1500 расчетов в секунду. Естественно, эти алгоритмы весьма ресурсоемки и полностью «убивают» пользовательский процессор и «замораживают» интерфейс браузера.

По-видимому, это является великолепной возможностью применить Web Workers!

Я взял реализацию от Ray C Morgan, вырезал весь интерфейс и таймеры и разложил вычисления на 4 параллельных потока для Web Workers. Благодаря этому удалось добиться скорости в 4500-9500 расчетов в минуту (в новых браузерах, которые поддерживают механизм Web Workers).

По этим ссылкам можно посмотреть на демонстрацию и скачать исходные коды:

Читать дальше

Все комментарии (habrahabr.ru)