EventEmitter против RxJS против кефира

Я хотел сравнить производительность JS EventEmitter и RxJS. Для этого я написал следующий тестовый скрипт:

Тест производительности

import Rx from 'rxjs/Rx';
import Kefir from 'kefir';

import { EventEmitter } from "events";

let Benchmark = require ("benchmark");
let suite = new Benchmark.Suite;

suite
.add('for', () => {
  let numArray = [1,2,3,4,5,6,7,8,9,10];
  let count = 0;
  for (let i = 0; i<numArray.length; i++)
    count += numArray[i];
})
.add('forEach', () => {
  let numArray = [1,2,3,4,5,6,7,8,9,10];
  let count = 0;
  numArray.forEach((num) => { count += num; });
})
.add('eventEmitter', () => {
  let numArray = [1,2,3,4,5,6,7,8,9,10];
  let count = 0;
  let myEmitter = new EventEmitter();
  myEmitter.on('number', (num) => { count += num; });
  numArray.forEach((num) => { myEmitter.emit('number', num); });
})
.add('rxjs', () => {
  let numArray = [1,2,3,4,5,6,7,8,9,10];
  let count = 0;
  let source = Rx.Observable.from(numArray)
    .do((x) => { count += x }, (error) => {}, () => {});
  source.subscribe((x) => {}, (error) => {}, () => {});
})
.add('kefir', () => {
  let numArray = [1,2,3,4,5,6,7,8,9,10];
  let count = 0;
  let stream = Kefir.sequentially(0, numArray);
  count = stream.scan(sum => sum + 1, 0);
})
.on('cycle', function (event) {
  console.log(String(event.target));
})
.on('complete', function () {
  console.log('Slowest is ' + this.filter('slowest').map('name'));
})
.run({'async': true});

Результаты производительности

for x 47,595,916 ops/sec ±1.58% (87 runs sampled)
forEach x 4,428,485 ops/sec ±0.75% (86 runs sampled)
eventEmitter x 1,478,876 ops/sec ±0.61% (86 runs sampled)
rxjs x 547,732 ops/sec ±0.66% (86 runs sampled)
kefir x 496,709 ops/sec ±5.15% (50 runs sampled)
Slowest is kefir

Как вы, ребята, можете видеть, что кефир оказался самым медленным вопреки заявлению, сделанному по этой ссылке.

  1. Я сделал что-то не так при написании теста?
  2. Было бы здорово, если бы кто-нибудь мог объяснить разницу, почему это происходит. Особенно, когда вы сравниваете его с Javascript Event-Emitter.

1 ответ

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

Во-первых, count это не число в кефире, это поток. Попробуйте войти count после звонка scan, И главное, похоже, кефир count Поток никогда не активируется, и вычисления никогда не запускаются! Это должно быть исправлено в первую очередь. Я подозреваю, что то же самое верно для эталонного теста Rx, но вам придется проверить документацию, чтобы увидеть, заблокирует ли создание потока из наблюдаемой ES или нет.

Я считаю, что вы можете реализовать асинхронный тест с этой библиотекой с чем-то похожим (непроверенный код):

suite.add('My async test', function(deferred) {
    let numArray = [1,2,3,4,5,6,7,8,9,10];
    let stream = Kefir.sequentially(0, numArray);
    let countStream = stream.scan(sum => sum + 1, 0);
    countStream.onEnd(() => deferred.resolve());
}, {'defer': true})

Имея это в виду, очень странно, что тест на кефире самый медленный, потому что он не работает. Я подозреваю, что тестовые массивы слишком малы и вычисляются слишком быстро, чтобы получить ценный тест. Скорее всего, тесты на самом деле измеряют время создания потока или в зависимости от того, какой из тестов содержит наибольшее количество мусора / процессов во время выполнения. Фактически, если тест Rx не ожидает завершения теста, он будет выполнять его обработку / очистку от мусора В ТЕЧЕНИЕ теста на кефир! Вы можете смягчить это, подождав, пока тесты Kefir и Rx завершатся в тесте; создать меньше мусора, повторно используя общий глобальный тестовый массив между тестами; и использовать очень, очень большой массив, чтобы убедиться, что итерация - это то, что доминирует во времени, проведенном в тестах.

Наконец, для асинхронных эталонных тестов (Kefir и Rx) необходимо убедиться, что эталонные тесты обрабатывают события одинаково по отношению к циклу событий. Я уверен, что пример Kefir обрабатывает каждое событие в РАЗЛИЧНОМ тике цикла событий и должен будет ждать выполнения любой другой активности браузера (рендеринг / рисование, другие обратные вызовы / тайм-ауты и т. Д.) Между каждым шагом в поток. Рассмотрим вывод следующего кода:

console.log('a')
setTimeout(function() {
    console.log('b')
}, 0);
console.log('c')

Этот код будет всегда печатать a, c, b с небольшой ненулевой задержкой в ​​печати финала b,

Я считаю, что вы можете заставить кефир обрабатывать массив в одном тике с чем-то вроде Kefir.constant([1,2,3...10]).flatten(), Однако я не думаю, что очень полезно сравнивать две потоковые инфраструктуры в синхронном тесте, потому что это не их предназначение.

Наконец, scan операция семантически отличается от forEach / do в других средах, потому что он создает значение для выходного потока на каждом шаге для любых потенциальных слушателей, в то время как другие должны только выполнить этот один блок кода.

Эталонные тесты получить очень сложно, но я надеюсь, что это поможет.

Другие вопросы по тегам