Автор оригинала: Madhu G Nadig.
Назад, когда JavaScript был разработан как Моча На Netscape Communications Corporation Brendan Eich в 1995 году язык был предназначен для добавления Программируемость В Интернете – сделать веб-более интерактивную для пользователя. За прошедшие годы JavaScript приобрел массивную известность и стал одним из самых важных языков дня. Рост полотна взял местами JavaScript, он никогда не был задуман.
Любой, кто написал значительный объем кода в JavaScript, не будет удивлен, узнав, что JavaScript был написан через 10 дней Отказ Как может быть, JavaScript имеет тонкие возможности ошибок, а Много других вещей, которые программисты предпочли бы не иметь Отказ Но это одно из основных программирования (сценариев?) Языки в современном веб-ведоме. Следовательно, крайне важно реализовать лучшие программные технологии программирования и технологий в JavaScript.
Поскольку JavaScript вырос из своих границ, на ходу добавляли лоты и множество функций, которые были не думали изначально. Добавление всех этих непредвиденных функций, от функционального программирования к ООП, были в значительной степени работы вокруг существующих языковых функций. Одна из прохладных функций, добавленных в JS в последние годы, была возможность параллельного вычислительного JavaScript, и это будет сосредоточиться на следующей статье.
Все больше и больше Js на браузере
Вы можете спросить – почему параллелизовать JavaScript? В конце концов, вы используете его в браузере для небольших сценариев масштаба. Конечно, если вы используете JavaScript для OnClick
Обработчик или для простого эффекта анимации, вы делаете не нужна параллелизация. Тем не менее, есть много приложений в тяжелом весе JS в Интернете, которые делают много приводящихся в JavaScript:
Обработка изображения на стороне клиента (в Facebook и Lightroom) написана в JS; Офисные пакеты в браузере, такие как документы Google, записываются в JS; и компоненты Firefox, такие как встроенный PDF Viewer, PDF.js и языковой классификатор, записываются в JS. Фактически, некоторые из этих приложений находятся в виде ASM.js, простой подмножестве JS, который является популярным целевым языком для компиляторов C ++; Игровые двигатели, изначально написанные в C ++, перекомпилируются в JS, чтобы запустить в Интернете как программы ASM.JS.
Сумма JavaScript в веб-странице неуклонно растет в течение многих лет, и причина для параллелизма JS такая же, как причина распараллелизовать любые другие Langauge: закон Мура умирает, а многоядерная архитектура захватывает мир.
JavaScript везде
JavaScript больше не ограничен браузером. Он проходит везде и на что угодно, от серверов до устройств IoT. Многие из этих программ являются тяжелыми весом и могут сильно пользоваться плодами параллельных вычислений. Кампания к JavaScript Все вынужден в путь И нахождение способов параллельного вычислительного JavaScript в средах без браузера теперь важно.
Возможно, самое твердое препятствие для параллельного программирования в JS была его парадигма на основе событий.
JavaScript имеет модель параллелизма на основе «контура события», где почти все ввода/вывода не блокируют. Когда операция была завершена, сообщение оказывается наряду с предоставленной функцией обратного вызова. В какой-то момент в будущем сообщение оказывается нами/обратный вызов.
Независимость вызывающего абонента из ответа позволяет проводить время выполнения JS выполнять другие вещи, ожидая завершения асинхронной операции и их обратных вызовов для запуска.
JavaScript Runtimes содержат очередью сообщений, которая хранит список сообщений, которые будут обработаны и связанные с ними функции обратного вызова. Эти сообщения оказываются в очереди в ответ на внешние события (такие как нажатие мыши или получают ответ на HTTP-запрос), заданное функцию обратного вызова.
Время выполнения имеет одно тему, слушая События , когда один из событий огонь главный нить звонки необходимая функция обратного вызова и работает на заднем плане. Как только событие обрабатывается, он возвращается обратно в основную нить. Таким образом, один Структура событий может обрабатывать любое количество событий, потому что основной цикл событий является не блокировка Отказ Эта же модель реализована независимо от того, где работает JavaScript, в Клиентская сторона или Серверная сторона (или практически в любом месте).
Теперь, если бы мы были параллелизовать JavaScript, имея несколько потоков, то по определению каждый поток будет слушать событие. На проведении указанного события невозможно определить, какой нить или сколько потоков будут обрабатывать событие. Очень возможно, что все темы обрабатывают одно и то же событие, и одно событие обрабатывается N Количество раз, где N это количество потоков. Таким образом, достаточно сказать, что нормальная параллельная параллельная вычисления не будет работать. JavaScript требует чего-то более «JavaScripty» – другая работа в длинном массиве JavaScript Works.
Обычно для достижения любого рода параллельных вычислений с использованием JavaScript вам нужно будет разбить работу в крошечные куски и разделить их выполнение, а также с использованием таймеров. Это как медленный, так и невыпорный.
Благодаря веб-работникам у нас теперь есть лучший способ достичь этого
Параллельные вычисления в JavaScript могут быть достигнуты через Веб-работники API , которые были введены с спецификацией HTML5.
Веб-документ, как определено в World Wide Web Consortium (W3C) и рабочей группой технологии приложений Web Hypertext Technology (Whatwg), является сценарием JavaScript, выполненный с страницы HTML, которая работает на фоновом режиме, независимо от других сценариев пользовательского интерфейса, которые также могут быть выполнены с одной и той же страницы HTML. Веб-процедуры часто могут использовать многоядерные процессоры более эффективно. Веб-работники относительно тяжелые. Ожидается, что они будут долгоживающимися, имеют высокую стоимость производительности запуска, а также высокая стоимость памяти для каждого экземпляра.
Веб-пользователи позволяют пользователю запустить JavaScript параллельно, не препятствуя пользовательскому интерфейсу. А работник Сценарий будет загружен и запущен в Backgroud, полностью независимо от сценариев пользовательского интерфейса. Это означает, что рабочие не имеют никакого доступа к элементам пользовательского интерфейса, например, DOM и общие функции JS, такие как GetElementById
(Вы все еще можете сделать звонки AJAX с помощью веб-работников). Празднование Prime Schood of The Web-Worker API состоит в том, чтобы выполнить вычислительную дорогую задачу на заднем плане, без прерывания или прерывания пользовательского взаимодействия.
С веб-работниками теперь вы можете иметь несколько нитей JS, работающих параллельно. Это позволяет браузеру иметь нормальную работу с выполнением на основе контура события одного основного потока на стороне пользователя, одновременно создавая комнату для нескольких потоков на заднем плане. Каждый веб-работник имеет отдельное очередь сообщений, петлю события и пространство памяти, независимо от исходной резьбы, которая его создала.
Веб-процедуры общаются с основным документом/основным потоком посредством передачи сообщений. Прохождение сообщения выполняется с использованием PostMessage API.
Согласно Scpecification, есть два типа веб-работников: Общие веб-работники и Выделенные веб-работники Отказ
Веб-пользователь по умолчанию является выделенным веб-рабочей.
Выделенный работник доступен только из скрипта, который сначала не породил его, тогда как общие работники могут быть доступны из нескольких сценариев.
Общий веб-работник нуждается в другом конструкторе: SharedWorker
Общий рабочий доступен несколькими сценариями – даже если они обращаются к различным окнам, IFRAMES или даже работникам.
Давайте начнем с работника. Работник может иметь обработчик для OnMessage
Событие, и он связывается с основным потоком с использованием PostMessage API.
onmessage = function(e) { console.log('Message received from main script'); //CPU intensive computations console.log('Posting message back to main script'); postMessage(result); }
Я буду реализовывать Функция верблюда трех горба На заднем плане используя веб-работники и отправьте результат с помощью API PostMessage.
Функция:
В нашей задаче .JS:
onmessage = function(e) { console.log("Message received from main script."); // Implementing three hump camel function var x = e.data[0]; var y = e.data[1]; var result = (2*x*x) - (1.05*x*x*x*x) + (Math.pow(x,6)/6) + (x*y) + (y*y); var workerResult = "Result: " + result; console.log("Posting message back to main script."); postMessage(workerResult); }
На мероприятии основного потока отправляя сообщение OnMessage
и функция выполняется. Работник вычисляет функцию на заднем плане и поместите сообщение в очередь сообщений через PostMessage
функция.
Теперь, приходя в нашу главную тему:
function compute(){ if (window.Worker) { // Check if the Browser supports the Worker api. // Requires script name as input var worker = new Worker("task.js"); worker.postMessage([0.554,2]); // Sending message as an array to the worker console.log('Message posted to worker'); worker.onmessage = function(e) { console.log(e.data); document.getElementById("result").innerHTML = e.data; console.log('Message received from worker'); }; } }
У нас есть одна функция Compute, которая выполняется на кнопке onClick. Сначала мы проверяем, поддерживает ли браузер API веб-работника. Тогда мы создаем работник, используя Работник ()
конструктор. После чего мы публикуем данные рабочему, используя PostMessage ()
функция. Это запускает сценарий работника на заднем плане, поскольку рабочий сценарий ждет нашего сообщения о событии сообщения.
Затем мы ждем события сообщения от работника. Мы обращаемся к этому событию с нашим обратным вызовом и обновите результат.
Результат для заданных параметров:
Параллелизм задачи через веб-работников
Веб-процедуры очень хороши при задании параллелизма. Можно создать множество фоновых работников и запустить несколько задач самостоятельно параллельно. В этом примере я пойду вперед и реализую Quicksort и Mergeort, чтобы работать параллельно друг другу.
Во-первых, серийный код
QuickSort:
function swap(items, firstIndex, secondIndex){ var temp = items[firstIndex]; items[firstIndex] = items[secondIndex]; items[secondIndex] = temp; } function partition(items, left, right) { var pivot = items[Math.floor((right + left) / 2)], i = left, j = right; while (i <= j) { while (items[i] < pivot) { i++; } while (items[j] > pivot) { j--; } if (i <= j) { swap(items, i, j); i++; j--; } } return i; } function quickSort(items, left, right) { var index; if (items.length > 1) { index = partition(items, left, right); if (left < index - 1) { quickSort(items, left, index - 1); } if (index < right) { quickSort(items, index, right); } } return items; }
Сортировка слиянием:
function mergeSort(arr){ if (arr.length < 2) return arr; var middle = parseInt(arr.length / 2); var left = arr.slice(0, middle); var right = arr.slice(middle, arr.length); return merge(mergeSort(left), mergeSort(right)); } function merge(left, right){ var result = []; while (left.length && right.length) { if (left[0] <= right[0]) { result.push(left.shift()); } else { result.push(right.shift()); } } while (left.length) result.push(left.shift()); while (right.length) result.push(right.shift()); return result; }
Теперь, чтобы эти задачи работать параллельно, нам нужно создать рабочий процесс для каждого из них:
onmessage = function(e) { console.log("Message received from main script."); // Implementing three hump camel function var arr = e.data; console.log(arr); result = quickSort(arr, 0, arr.length - 1); postMessage(result); }
onmessage = function(e) { console.log("Message received from main script."); // Implementing three hump camel function var arr = e.data; console.log(arr); result = mergeSort(arr); postMessage(result); }
В наших main.js нам нужно создать и позвонить как рабочим.
function bothsort(){ if (window.Worker) { // Check if the Browser supports the Worker api. // Requires script name as input var worker = new Worker("mergesort.js"); var worker_2 = new Worker("quicksort.js"); array= [] for(i =1000; i > 0; i--){ array.push(Math.round(Math.random()*1000)); } worker.postMessage(array); // Sending message as an array to the worker worker_2.postMessage(array); // Sending message as an array to the worker worker.onmessage = function(e) { console.log(e.data); document.getElementById("result").innerHTML = e.data; console.log('MergeSort Message received from worker'); }; worker_2.onmessage = function(e) { console.log(e.data); document.getElementById("result_2").innerHTML = e.data; console.log('QUickSort Message received from worker'); }; } }
Теперь давайте порекомендуем как последовательные, так и параллельные задачи через window.performance.Не
:
a = performance.now(); bothsort(); b = performance.now(); console.log(b-a); a = performance.now(); mergeSort(array); quickSort(array, 0, array.length-1); b = performance.now(); console.log(b-a);
Ускорение
Как только параллелизация задачи завершена, важно оценить скорость и эффективность новой программы, для параллелизма бессмысленно без более быстрого выполнения.
Speedup (SP) определяется как отношение времени выполнения для последовательного алгоритма (T1) до времени выполнения для параллельного алгоритма с процессорами P (TP). То есть/тп. Идеальные результаты ускорения, когда. Ускорение формально получена из закона Amdahl, который считает, что часть программы, которая является последовательной и порцией, которая параллельна при расчете ускорения.
Вот результаты для серийного и параллельного после многих прогонов:
| Number of Data points | Serial | Parallel | Speedup | |-----------------------|-------------|-------------|---------| | 400 | 9.165000 | 1.855000 | 4.9407 | | 800 | 11.10500 | 1.225000 | 9.0653 | | 1600 | 20.099999 | 3.545000 | 5.6699 | | 3200 | 45.905000 | 4.929999 | 9.3113 | | 6400 | 93.797550 | 5.265000 | 17.8153 | | 12800 | 288.005 | 9.44499 | 30.4298 |
Преимущества параллельной обработки очевидны; со скоростью 4,9407 (4.9x более быстрое время). Мы также можем наблюдать за производительностью, выходящими с графиков на 12800 элементов для сортировки, со скоростью, достигающей массивной стоимости 30,4298.
Одна из самых тяжелых задач на стороне клиента – обработка изображений. В этом разделе я постараюсь включить веб-работников в OpenSource Source JavaScript Image Manipulate Библиотека Pixelify.js – Библиотека преобразует любое изображение в пикселированный в течение времени.
Параллелизуемые регионы
Как обсуждалось ранее, У веб-работников нет доступа к элементам DOM , следовательно, любая часть кода, которая должна получить доступ к дому, по своей природе непараллелизации. Работа вокруг этого состоит в том, чтобы отправлять сообщения основной нитью с каким-то образом кодировкой, которые сообщают основным нитьм, какие действия, чтобы взять в доме, это имеет много накладных расходов, если рабочие должны взаимодействовать с домом на регулярной основе Отказ Кроме того, веб-работники не могут слушать любые сгенерированные пользователю события (например, onclick
), поэтому любое взаимодействие на стороне клиента должна быть в одной резьбовой основной цикле (который тогда, конечно, может позвонить рабочему).
Кроме того, веб-работники используют PostMessage
API для общения с главной нитью, который по существу зарезервирован для обмена сообщениями через строки. Браузеры используют Алгоритм структурированного клона пройти вдоль сложных объектов, файлов и лоб. Однако это не значит, что алгоритм обеспечивает весь мощный механизм. Есть ограничения (и по праву так). Алгоритм структурированного клона не работает со многими структурами:
Объекты ошибки и функции не могут быть продублированы структурированным алгоритмом клона; Попытка сделать это выбросить исключение data_clone_err. Попытка клонируйте узлы DOM, также будут бросать исключение data_clone_err. Некоторые параметры объектов не сохраняются
Pixelify.js использует холст
Элемент HTML5, чтобы сделать манипуляцию изображения. Это означает, что веб-работники не могут получить доступ к холсте, если оно является частью DOM. В случае, если холст
Элемент не является частью DOM, мы все еще не можем пройти холст
объект из главной нити к работнику из-за ограничений PostMessage
API, который не позволит объектам с функцией для передачи. Это ингибирует любые параллелизм, когда холст
Объект участвует. Фактические процедуры/манипуляции, сделанные из холст
должен оставаться в главной ните.
Объектно-ориентированный код
Один из недостатков PostMessage
Параллелизация объектно-ориентированного кода. Вы не можете передать объект функции к работнику, поэтому вы просто не можете пройти объект контекста – это
– от вашего объекта ориентированного кода.
Я должен был использовать работу, чтобы иметь дело с этим вопросом. Во-первых, я отменил контекст это
в согласованные части, которые будут нужны этот работник. Затем после обработки я вернул параметры в основную нить, а затем применил к результатам в основной ните, это будет много ясно, как только мы посмотрим на код.
Из кода Pixelify.js параллелизуемая область из кода является расчет параметров холста:
for (y = 0; y <= this.h + hs; y += this.pixel) { yy = y; if (yy >= this.h) yy = this.h - this.pixel + hs; for (x = 0; x <= this.w + hs; x += this.pixel) { xx = x; if (xx >= this.w) xx = this.w - this.pixel + hs; image_index = (yy * (this.w * 4)) + (xx * 4); r = data[image_index]; g = data[image_index + 1]; b = data[image_index + 2]; a = (this.alpha * data[image_index + 3]) / 255; rgba = 'rgba(' + r +','+ g +','+ b +','+ a + ')'; this[this.clean ? '_contextClean' : '_context'].fillStyle = rgba; this[this.clean ? '_contextClean' : '_context'] .fillRect( (this.x + x) - hs, (this.y + y) - hs, this.pixel, this.pixel ) } }
Из взгляда этого это не большая часть задачи процессора. Это может не быть хорошо конденсировано с веб-работниками. Ну, есть только один способ узнать наверняка. Итак, давайте прыгнем прямо в код.
Выполнение
Первоначально я буду использовать один веб-работник, а затем добавить множественные из них, если результаты кажутся обнадеживающими.
В нашем главном файле библиотеки Pixelify – Parallel.js Давайте настроим веб-работник и отправьте требуемые параметры через сообщение – внутри Pixelate
функция. Это обходной путь с момента прямого прохождения это
Контекст приведет к Data_clone_err
. Отказ
var worker = new Worker("pix.js"); // Sending message as an array to the worker worker.postMessage([this.h , this.pixel, this.w, this.x, this.y, hs, data, this.alpha]); // storing the current context in a variable. var pxo = this;
Теперь давайте послушаем сообщение у работника. На нашем работнике мы отправим сообщение, как только мы вычислили наши ценности.
worker.onmessage = function(e) { console.log(e.data); pxo.replace(); // More to come soon }
В нашем рабочем файле pix.js Мы будем слушать OnMessage
Событие и повторно собрать разделенные параметры, отправленные из основного потока.
onmessage = function(e){ this.h = e.data[0]; this.pixel = e.data[1]; this.w = e.data[2]; this.x = e.data[3]; this.y = e.data[4]; hs = e.data[5]; data = e.data[6]; this.alpha = e.data[7];
Затем мы инициализируем объект результата, именно здесь результат будет храниться и отправлен обратно в основной поток.
result = {rgbas: [], rect: []};
Теперь фактические вычисления
for (y = 0; y <= this.h + hs; y += this.pixel) { yy = y; result.rgbas.push([]) result.rect.push([]) if (yy >= this.h) yy = this.h - this.pixel + hs; for (x = 0; x <= this.w + hs; x += this.pixel) { xx = x; if (xx >= this.w) xx = this.w - this.pixel + hs; image_index = (yy * (this.w * 4)) + (xx * 4); r = data[image_index]; g = data[image_index + 1]; b = data[image_index + 2]; a = (this.alpha * data[image_index + 3]) / 255; rgba = 'rgba(' + r +','+ g +','+ b +','+ a + ')'; result.rgbas[y/this.pixel].push(rgba); result.rect[y/this.pixel].push([(this.x + x) - hs, (this.y + y) - hs, this.pixel, this.pixel]); } } postMessage(result);
Вернувшись в нашу главный файл Pixelity.js, теперь мы должны применить результаты к элементу DOM. К сожалению, нам придется иметь Еще один вложенный для петли Просто чтобы применить результаты к объекту Canvas.
worker.onmessage = function(e) { console.log(pxo._context) for (y = 0; y <= pxo.h + hs; y += pxo.pixel) { for (x = 0; x <= pxo.w + hs; x += pxo.pixel) { pxo[this.clean ? '_contextClean' : '_context'].fillStyle = e.data.rgbas[y/10][x/10]; pxo[this.clean ? '_contextClean' : '_context'] .fillRect( (pxo.x + x) - hs, (pxo.y + y) - hs, pxo.pixel, pxo.pixel ) } } pxo.replace(); };
Остальная часть кода по существу остается прежним.
Полученные результаты
У меня есть со мной изображение Konrad ZUED (невзрачного пионера информатики) с копией его создания, Z1.
После пикселирования:
Ускорение
Ускорение – Abysmal. Фактически, параллельный код намного намного медленнее, чем серийный имполнирование. Для размера изображений в диапазоне от 100 * от 100 до 1024 * 768 параллельная реализация составляет около 5 до 10 раз медленнее, чем последовательная реализация. Даже если у меня есть несколько веб-рабочих, было бы очень трудно каждому улову до серийной производительности, не говоря уже о том, чтобы превзойти его.
Причины плохой параллельной производительности
- Задача не процессор интенсивно
- Как отмечалось ранее, наша параллелизационная область не была интенсивным процессором. Веб-работники были сделаны для интенсивных задач процессоров, следовательно, не имея интенсивную задачу CPU, сделанные веб-работниками менее эффективными.
- Обходные пути
- Мы должны были использовать много обходных путей для библиотеки для работы параллельно. Во-первых,
это
Контекст был разбит, а затем повторно собран работник. - Затем результаты были отмечены и сохранены в объекте. Объект был перезаряжен до основного потока, который затем применяется к результатам для
холст
Объект – это добавил дополнительную вложенную петлю в основной ните – правильное препятствие для производительности.
- Мы должны были использовать много обходных путей для библиотеки для работы параллельно. Во-первых,
- Движение данных
- Много данных перемещены между главной нитью и рабочим процессом – добавление к накладным расходам. Сам объект результата был довольно огромным.
- В верхней части этого изображения двоичных данных также были отправлены как оно через PostMessage.
Веб-работники приносят захватывающую перспективу параллелизма в JavaScript. Принесение параллелизма к программисту JavaScript – большая победа.
Web-работники для интенсивных задач CPU Из нашего примера задач параллелизм мы видели, что веб-работники хорошо работают с интенсивными задачами CPU, с приведенным выше примером достигая ускорения 30x только с двумя веб-работниками. Веб-работники не подходят, когда задача в руке не является интенсивным процессором, как видно из приведенного выше примера манипуляций изображений.
Неспособность работать с DOM – это одно из недостатков Stark, поскольку мы видели из приведенного выше примера. Добавление обходных путей будет препятствовать производительности.
В целом, веб-работники являются удивительным для интенсивной параллелизма процессора. Хотя не очень подходит для задач, которые требуют взаимодействия DOM, они являются очень ценным активом, когда JavaScript не работает на веб-странице.
Вот и все сейчас! Если у вас есть какие-либо комментарии или вопросы, пожалуйста, оставьте их ниже и или найдите меня на Commentor!