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/core | ratio | |
---|---|---|---|
time | 1250ms | 247ms | 5x faster |
size | 12.02kb | 0.50kb | 24x smaller |
(gzipped) | 4.27kb | 0.29kb | 14.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();
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 | |
---|---|---|---|---|---|
time | 3713ms | 2379ms | 1.56x faster | 1112ms | 3.3x faster |
size | 14.3kb | 1.37kb | 10.4x smaller | 0.56kb | 25.5x smaller |
(gzipped) | 4.95kb | 0.66kb | 7.5x smaller | 0.32kb | 15.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();