簡體   English   中英

EventEmitter vs RxJS vs Kefir

[英]EventEmitter vs RxJS vs Kefir

我想比較 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

正如您所看到的,與此鏈接中的聲明相反,Kefir 的速度最慢。

  1. 我在編寫測試時做錯了什么嗎?
  2. 如果有人能解釋為什么會發生這種差異,那就太好了。 特別是當您將它與 javascript 事件發射器進行比較時。

我知道您不久前問過這個問題,但我認為讓未來的讀者了解我在查看基准測試時注意到的一些問題可能會有所幫助。

首先, count不是 Kefir 中的數字,它是一個流。 在調用scan后嘗試記錄count 重要的是,似乎 Kefir count流從未被激活並且計算從未運行! 這需要先修復。 我懷疑 Rx 基准測試也是如此,但是您必須檢查文檔以查看從 ES observable 創建流是否會阻塞。

我相信您可以使用類似於(未經測試的代碼)的內容使用該庫實現異步測試:

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})

考慮到這一點,很奇怪 Kefir 的基准測試是最慢的,因為它不起作用。 我懷疑測試數組太小,計算速度太快,無法獲得有價值的基准。 很可能測試實際上是在測量流構建時間,或者無論哪個基准恰好在運行時由運行時收集/處理的垃圾最多。 事實上,如果 Rx 基准測試不等待測試完成,它將在 Kefir 測試期間進行處理/垃圾清理! 您可以通過等待 Kefir 和 Rx 測試在基准測試中完成來緩解這種情況; 通過在基准測試之間重用通用的全局測試數組來減少垃圾; 並使用一個非常非常大的數組來確保迭代是在基准測試中花費的時間的主要因素。

最后,對於異步基准測試(Kefir 和 Rx),您需要確保基准測試以與事件循環相同的方式處理事件。 我確定 Kefir 示例在事件循環的不同刻度中處理每個事件,並且必須等待瀏覽器的任何其他活動(渲染/繪畫、其他回調/超時等)在每個步驟之間完成溪流。 考慮以下代碼的輸出:

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

此代碼將始終打印acb並在打印最終b有輕微的非零延遲。

我相信你可以讓開菲爾在同一個滴答聲中處理數組,比如Kefir.constant([1,2,3...10]).flatten() 但是,我認為在同步基准測試中比較兩個流框架不是很有用,因為這不是它們的預期目的。

最后, scan操作在語義上與其他框架中的forEach / do不同,因為它在每個步驟為任何潛在偵聽器的輸出流生成一個值,而其他人只需運行該代碼塊。

基准很難做到正確,但我希望這會有所幫助。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM