Skip to main content

vs RxJS


SYSTEM CONFIGURATION:

  • CPU: Intel(R) Core(TM) i7-8750H CPU @ 2.20GHz (6 cores / 12 threads)
  • Memory: 32Go
  • OS: Ubuntu 20.04 LTS
  • Browser: Chrome 109
  • Date: 2023-01-26
  • @lirx/core: 1.2.0
  • rxjs: 7.5.6

Benchmark 1

This first benchmark compares the 3 most common and used pipes: map, filter and distinctUntilChanged.

rxjs@lirx/coreratio
time1250ms247ms5x faster
size12.02kb0.50kb24x smaller
(gzipped)4.27kb0.29kb14.7x smaller

As you may see, @lirx/core strongly outperforms RxJS in both execution time and bundle size. And due to its capabilities, you may use it in any of your projects with almost zero footprint. Let's note however, that RxJS did an amazing job this past years, as they went from 20x slower a few years ago at 5x slower for the same test code.

Expand the code
import { from as fromRXJS } from 'rxjs';
import { fromArray, distinct$$$, map$$$, pipe$$, filter$$$ } from '@lirx/core';
import { distinctUntilChanged, filter, map } from 'rxjs/operators';

function performancesExample() {
const values = Array.from({ length: 1e5 }, (v: any, index: number) => index);

const withRXJS = () => {
let j = 0;

const obs = fromRXJS(values).pipe(
map((value: number) => value * 2),
filter((value: number) => value > 1e4),
distinctUntilChanged()
);

console.time('start');
for (let i = 0; i < 1e2; i++) {
obs.subscribe((value: number) => {
j += value;
});
}
console.timeEnd('start');
console.log('j', j);
};

const withLiRXCore = () => {
let j = 0;

const subscribe = pipe$$(fromArray(values), [
map$$$<number, number>((value: number) => value * 2),
filter$$$<number>((value: number) => value > 1e4),
distinct$$$<number>(),
]);

console.time('start');
for (let i = 0; i < 1e2; i++) {
subscribe((value: number) => {
j += value;
});
}
console.timeEnd('start');
console.log('j', j);
};

/* RxJS */

// withRXJS();

// time:
// 1250.388916015625 ms

// size:
// dist/assets/index.412ad1ea.js 12.02 KiB / gzip: 4.27 KiB

/* @lirx/core */

withLiRXCore();

// time:
// 247.414794921875 ms
// => 5x faster

// size:
// dist/assets/index.6d7778f7.js 0.50 KiB / gzip: 0.29 KiB
// => 24x / 14.7x smaller
}

performancesExample();

Click here to run the code


Benchmark 2

This benchmark focuses more on the notifications, and the performances associated with them.

rxjs@lirx/core
(with notification)
ratio@lirx/core
(without notification)
ratio
time3713ms2379ms1.56x faster1112ms3.3x faster
size14.3kb1.37kb10.4x smaller0.56kb25.5x smaller
(gzipped)4.95kb0.66kb7.5x smaller0.32kb15.4x smaller

This time, RxJS performs pretty well. It has been built for Observables having states. So it is finely tuned for these cases. @lirx/core is the winner, but by little (~30% less time). However, if we rewrite the pipeline to work without Notifications, we may see than @lirx/core takes the lead.

Through this example, we may see that it's pretty important to select the right Observable and pipeline, as it could easily double our performances.

Expand the code
import { of as ofRXJS, forkJoin as forkJoinRXJS } from 'rxjs';
import {
forkJoin,
singleN,
fulfilled$$$,
pipe$$,
defaultNotificationObserver,
combineLatest,
map$$,
single,
} from '@lirx/core';
import { map } from 'rxjs/operators';

function performancesExample() {
const length: number = 1e3;
const iterations: number = 1e4;

const sum = (values: readonly number[]): number => {
let sum: number = 0;
for (let i = 0; i < values.length; i++) {
sum += values[i];
}
return sum;
};

const withRXJS = () => {
let j = 0;

const obs = forkJoinRXJS(
Array.from({ length }, (_, i) => ofRXJS(Math.random()))
).pipe(map(sum));

console.time('start');

for (let i = 0; i < iterations; i++) {
obs.subscribe((value: number) => {
j += value;
});
}

console.timeEnd('start');
console.log('j', j);
};

const withLiRXCoreAndNotifications = () => {
let j = 0;

const subscribe = pipe$$(
forkJoin(
Array.from({ length }, (_, i) => singleN<number>(Math.random()))
),
[
fulfilled$$$((values: number[]) => {
return singleN(sum(values));
}),
]
);

console.time('start');

for (let i = 0; i < iterations; i++) {
subscribe(
defaultNotificationObserver((value: number) => {
j += value;
})
);
}
console.timeEnd('start');
console.log('j', j);
};

const withLiRXCore = () => {
let j = 0;

const subscribe = map$$(
combineLatest(Array.from({ length }, (_, i) => single(Math.random()))),
sum
);

console.time('start');

for (let i = 0; i < iterations; i++) {
subscribe((value: number) => {
j += value;
});
}
console.timeEnd('start');
console.log('j', j);
};

/* RxJS */

// withRXJS();

// time:
// 3713.386962890625 ms

// size:
// dist/assets/index.e05384e3.js 14.30 KiB / gzip: 4.95 KiB

/* @lirx/core */

// withLiRXCoreAndNotifications();

// time:
// 2379.6220703125 ms
// => 1.56x faster

// size:
// dist/assets/index.5ca1e5d4.js 1.37 KiB / gzip: 0.66 KiB
// => 10.4x / 7.5x smaller

withLiRXCore();

// time:
// 1112.876953125 ms
// => 3.3x faster

// size:
// dist/assets/index.d50e09c0.js 0.56 KiB / gzip: 0.32 KiB
// => 25.5x / 15.4x smaller
}

performancesExample();

Click here to run the code