Оригинальный адрес:GitHub.com/Microsoft/Вы…
В этой статье представлены основные концепции Napa.js и вы узнаете, как работает Napa.js. О его происхождении и намерении развития вы можете прочитатьэта статья
Введение
Zone
Зона является основной концепцией Napa.js. Это базовая единица для выполнения кода JavaScript. Весь контент, связанный с многопоточностью, неотделим от концепции Зоны. Процесс может содержать несколько зон, и каждая зона состоит из нескольких рабочих процессов JavaScript.
Все воркеры внутри зоны похожи: они загружают один и тот же код и обрабатываются почти одинаковоbroadcast
а такжеexecute
request, вы не можете указать выполнение кода в конкретном воркере. Воркеры в разных зонах совершенно разные: они загружают разный код или загружают один и тот же код, но выполняются с разными политиками, такими как разные размеры стека, разные политики безопасности и т.д. Приложение будет использовать несколько зон для загрузки разных политик.
Есть два типа зон:
- Зона Napa — состоит из нескольких рабочих процессов JavaScript, управляемых Napa.js. Рабочие в зоне Напа поддерживают некоторые API-интерфейсы Node.js.
- Зона узла — виртуальная зона, которая предоставляет цикл событий Node.js с полными возможностями Node.js.
Это разделение позволяет вам использовать зону Napa для тяжелых вычислительных транзакций и зону Node для транзакций ввода-вывода. В то же время зона Node также является дополнением к зоне Napa, которая не может полностью поддерживать Node API.
Следующий код создает зону Napa с 8 рабочими:
var napa = require('napajs');
var zone = napa.zone.create('sample-zone', { workers: 8 });
Следующий код демонстрирует, как получить доступ к зоне Node:
var zone = napa.zone.node;
В зоне можно выполнять два типа операций:
- Широковещательная передача — все рабочие исполняют один и тот же код, изменяют состояние рабочего процесса и возвращают объект-обещание. Однако мы можем судить об успешном или неудачном выполнении только по возвращаемому результату обещания. обычно используется
broadcast
для запуска приложения, предварительной загрузки некоторых данных или изменения настроек приложения. - Execute — выполняется на случайном воркере, не меняет состояние воркера, возвращает промис с данными результата.
execute
Обычно используется для ведения реального бизнеса.
В работе Zone применяется стратегия «первым пришел — первым вышел», ноbroadcast
Сравниватьexecute
более высокий приоритет.
Следующий код демонстрирует использованиеbroadcast
а такжеexecute
Выполните простое задание:
function foo() {
console.log('hi');
}
// This setups function definition of foo in all workers in the zone.
zone.broadcast(foo.toString());
// This execute function foo on an arbitrary worker.
zone.execute(() => { global.foo() });
передача информации
Поскольку V8 не подходит для выполнения кода JavaScript среди нескольких изолятов, каждый изолят управляет своим собственным внутренним стеком. Передача значений между изолятами требует маршаллинга/немаршаллинга Размер полезной нагрузки и сложность объекта определяют эффективность связи. Все изоляты JavaScript относятся к одному и тому же процессу, а нативные объекты могут быть обернуты в объекты JavaScript, исходя из этого мы пытаемся спроектировать эффективный режим передачи данных для Napa.
Для реализации вышеуказанного шаблона вводятся следующие понятия:
передаваемый тип
Переносимые типы — это типы JavaScript, которые можно свободно перемещать в рабочем потоке. включать
- Основные типы JavaScript: null, boolean, число, строка
- Достигнуто
Transportable
Объект интерфейса (класс TypeScript) - массив или объект вышеуказанных типов
- и не определено
Хранить среди рабочих
Store API используется для обмена данными между работниками JavaScript. при исполненииstore.set
, данные маршалируются в JSON и сохраняются в стеке процесса, доступном для всех потоков; при выполненииstore.get
Когда данные дизассемблированы.
Следующий код демонстрирует, как использовать хранилище для обмена данными:
var napa = require('napajs');
var zone = napa.zone.create('zone1');
var store = napa.store.create('store1');
// Set 'key1' in node.
store.set('key1', {
a: 1,
b: "2",
c: napa.memory.crtAllocator // transportable complex type.
};
// Get 'key1' in another thread.
zone.execute(() => {
var store = global.napa.store.get('store1');
console.log(store.get('key1'));
});
Хотя это очень удобно, не рекомендуется использовать store для передачи значений в одной и той же транзакции, потому что это не только передает данные (но также сопровождается другими вещами, такими как блокировка). Кроме того, несмотря на наличие механизма сборки мусора, разработчики должны вручную удалять соответствующий ключ после использования данных.
Установить
воплощать в жизньnpm install napajs
Установить.
После установки системы OSX будет сообщено об ошибке.github issueУ Чжунли тот же вопрос, решение согласно официальномудокументация по сборке, создайте его вручную.В последней версии v0.1.4 указанные выше проблемы исправлены.
Действуйте следующим образом:
-
Установите необходимые зависимости
- Install C++ compilers that support C++14:
xcode-select --install
- Install CMake:
brew install cmake
- Install cmake-js:
npm install -g cmake-js
- Install C++ compilers that support C++14:
-
Сборка через npm
npm install --no-fetch
Пример быстрого старта
Вычислите значение числа пи
Ниже приведен пример вычисления значения π, демонстрирующий, как использовать преимущества многопоточности для выполнения подзадач.
var napa = require("napajs");
// Change this value to control number of napa workers initialized.
const NUMBER_OF_WORKERS = 4;
// Create a napa zone with number_of_workers napa workers.
var zone = napa.zone.create('zone', { workers: NUMBER_OF_WORKERS });
// Estimate the value of π by using a Monte Carlo method
function estimatePI(points) {
var i = points;
var inside = 0;
while (i-- > 0) {
var x = Math.random();
var y = Math.random();
if ((x * x) + (y * y) <= 1) {
inside++;
}
}
return inside / points * 4;
}
function run(points, batches) {
var start = Date.now();
var promises = [];
for (var i = 0; i < batches; i++) {
promises[i] = zone.execute(estimatePI, [points / batches]);
}
return Promise.all(promises).then(values => {
var aggregate = 0;
values.forEach(result => aggregate += result.value);
printResult(points, batches, aggregate / batches, Date.now() - start);
});
}
function printResult(points, batches, pi, ms) {
console.log('\t' + points
+ '\t\t' + batches
+ '\t\t' + NUMBER_OF_WORKERS
+ '\t\t' + ms
+ '\t\t' + pi.toPrecision(7)
+ '\t' + Math.abs(pi - Math.PI).toPrecision(7));
}
console.log();
console.log('\t# of points\t# of batches\t# of workers\tlatency in MS\testimated π\tdeviation');
console.log('\t---------------------------------------------------------------------------------------');
// Run with different # of points and batches in sequence.
run(4000000, 1)
.then(result => run(4000000, 2))
.then(result => run(4000000, 4))
.then(result => run(4000000, 8))
Текущие результаты следующие: при выборе 1 группы, 2 групп и 4 групп подзадач для параллельных вычислений видно, что время выполнения значительно улучшилось. При выборе 8 групп подзадач для параллельных вычислений. вычислений, так как больше нет простаивающих рабочих ресурсов. Нет заметного улучшения времени выполнения.
# of points # of batches # of workers latency in MS estimated π deviation
---------------------------------------------------------------------------------------
40000000 1 4 1015 3.141619 0.00002664641
40000000 2 4 532 3.141348 0.0002450536
40000000 4 4 331 3.141185 0.0004080536
40000000 8 4 326 3.141620 0.00002724641
Рассчитать последовательность Фибоначчи
var napa = require("napajs");
// Change this value to control number of napa workers initialized.
const NUMBER_OF_WORKERS = 4;
// Create a napa zone with number_of_workers napa workers.
var zone = napa.zone.create('zone', { workers: NUMBER_OF_WORKERS });
/*
Fibonacci sequence
n: | 0 1 2 3 4 5 6 7 8 9 10 11 ...
-------------------------------------------------------------------------
NTH Fibonacci: | 0 1 1 2 3 5 8 13 21 34 55 89 ...
*/
function fibonacci(n) {
if (n <= 1) {
return n;
}
var p1 = zone.execute("", "fibonacci", [n - 1]);
var p2 = zone.execute("", "fibonacci", [n - 2]);
// Returning promise to avoid blocking each worker.
return Promise.all([p1, p2]).then(([result1, result2]) => {
return result1.value + result2.value;
});
}
function run(n) {
var start = Date.now();
return zone.execute('', "fibonacci", [n])
.then(result => {
printResult(n, result.value, Date.now() - start);
return result.value;
});
}
function printResult(nth, fibonacci, ms) {
console.log('\t' + nth
+ '\t' + fibonacci
+ '\t\t' + NUMBER_OF_WORKERS
+ '\t\t' + ms);
}
console.log();
console.log('\tNth\tFibonacci\t# of workers\tlatency in MS');
console.log('\t-----------------------------------------------------------');
// Broadcast declaration of 'napa' and 'zone' to napa workers.
zone.broadcast(' \
var napa = require("napajs"); \
var zone = napa.zone.get("zone"); \
');
// Broadcast function declaration of 'fibonacci' to napa workers.
zone.broadcast(fibonacci.toString());
// Run fibonacci evaluation in sequence.
run(10)
.then(result => { run(11)
.then(result => { run(12)
.then(result => { run(13)
.then(result => { run(14)
.then(result => { run(15)
.then(result => { run(16)
}) }) }) }) }) })
Результат операции
Nth Fibonacci # of workers latency in MS
-----------------------------------------------------------
10 55 4 10
11 89 4 13
12 144 4 15
13 233 4 22
14 377 4 31
15 610 4 50
16 987 4 81