fix(subject): Faster subscriptions management in Subject#7240
fix(subject): Faster subscriptions management in Subject#7240benlesh merged 2 commits intoReactiveX:masterfrom
Conversation
The current implementation of the Subject was backing itself on an array of observers. When one observer unsubscribed itself, it was performing a linear scan on the array of observers and dropping the item from the array of observers. The algorithm complexity of this operation was O(n) with n being the number of observers connected to the Subject.
It caused code like:
```js
const allSubscriptions = [];
const source = new Subject();
for (let index = 0 ; index !== 1_000_000 ; ++index) {
allSubscriptions.push(source.subscribe()); // rather quick
}
for (const subscription of allSubscriptions) {
subscription.unsubscribe(); // taking very long
}
```
The proposed approach consists into changing our backing collection (an array) into a Map. But we lose somehow the set capability on subject.observers (might possibly be patched in some way).
Following that change the command `cross-env TS_NODE_PROJECT=tsconfig.mocha.json mocha --config spec/support/.mocharc.js "spec/**/Subje*-spec.ts"` passed to 452ms from 10s.
|
By the way I started to run some benchmarks against the new code and the master I took my branch from. Here is the code I used// @ts-check
const { Bench } = require('tinybench');
const { Subject: SubjectMaster } = require('./dist-master/cjs');
const { Subject: SubjectPr } = require('./dist/cjs');
async function run() {
const numIterations = 10_000;
const bench = new Bench({ warmupIterations: Math.ceil(numIterations / 20), iterations: numIterations });
bench.add('[master] Creating Subject', () => {
new SubjectMaster();
});
bench.add('[pr] Creating Subject', () => {
new SubjectPr();
});
bench.add('[master] 0 subscribed, 1 next', () => {
const source = new SubjectMaster();
source.next(1);
});
bench.add('[pr] 0 subscribed, 1 next', () => {
const source = new SubjectPr();
source.next(1);
});
bench.add('[master] 0 subscribed, 100 next', () => {
const source = new SubjectMaster();
for (let i = 0; i !== 100; ++i) {
source.next(i);
}
});
bench.add('[pr] 0 subscribed, 100 next', () => {
const source = new SubjectPr();
for (let i = 0; i !== 100; ++i) {
source.next(i);
}
});
bench.add('[master] 1 subscribed, 1 next', () => {
const source = new SubjectMaster();
const subscription = source.asObservable().subscribe();
source.next(1);
subscription.unsubscribe();
});
bench.add('[pr] 1 subscribed, 1 next', () => {
const source = new SubjectPr();
const subscription = source.asObservable().subscribe();
source.next(1);
subscription.unsubscribe();
});
bench.add('[master] 1 subscribed, 100 next', () => {
const source = new SubjectMaster();
const subscription = source.asObservable().subscribe();
for (let i = 0; i !== 100; ++i) {
source.next(i);
}
subscription.unsubscribe();
});
bench.add('[pr] 1 subscribed, 100 next', () => {
const source = new SubjectPr();
const subscription = source.asObservable().subscribe();
for (let i = 0; i !== 100; ++i) {
source.next(i);
}
subscription.unsubscribe();
});
bench.add('[master] 3 subscribed, 1 next', () => {
const source = new SubjectMaster();
const s1 = source.asObservable().subscribe();
const s2 = source.asObservable().subscribe();
const s3 = source.asObservable().subscribe();
source.next(1);
s1.unsubscribe();
s2.unsubscribe();
s3.unsubscribe();
});
bench.add('[pr] 3 subscribed, 1 next', () => {
const source = new SubjectPr();
const s1 = source.asObservable().subscribe();
const s2 = source.asObservable().subscribe();
const s3 = source.asObservable().subscribe();
source.next(1);
s1.unsubscribe();
s2.unsubscribe();
s3.unsubscribe();
});
bench.add('[master] 3 subscribed, 100 next', () => {
const source = new SubjectMaster();
const s1 = source.asObservable().subscribe();
const s2 = source.asObservable().subscribe();
const s3 = source.asObservable().subscribe();
for (let i = 0; i !== 100; ++i) {
source.next(i);
}
s1.unsubscribe();
s2.unsubscribe();
s3.unsubscribe();
});
bench.add('[pr] 3 subscribed, 100 next', () => {
const source = new SubjectPr();
const s1 = source.asObservable().subscribe();
const s2 = source.asObservable().subscribe();
const s3 = source.asObservable().subscribe();
for (let i = 0; i !== 100; ++i) {
source.next(i);
}
s1.unsubscribe();
s2.unsubscribe();
s3.unsubscribe();
});
bench.add('[master] 10 subscribed, 1 next', () => {
const source = new SubjectMaster();
const s = [];
for (let i = 0; i !== 10; ++i) {
s.push(source.asObservable().subscribe());
}
source.next(1);
for (const e of s) {
e.unsubscribe();
}
});
bench.add('[pr] 10 subscribed, 1 next', () => {
const source = new SubjectPr();
const s = [];
for (let i = 0; i !== 10; ++i) {
s.push(source.asObservable().subscribe());
}
source.next(1);
for (const e of s) {
e.unsubscribe();
}
});
bench.add('[master] 10 subscribed, 100 next', () => {
const source = new SubjectMaster();
const s = [];
for (let i = 0; i !== 10; ++i) {
s.push(source.asObservable().subscribe());
}
for (let i = 0; i !== 100; ++i) {
source.next(i);
}
for (const e of s) {
e.unsubscribe();
}
});
bench.add('[pr] 10 subscribed, 100 next', () => {
const source = new SubjectPr();
const s = [];
for (let i = 0; i !== 10; ++i) {
s.push(source.asObservable().subscribe());
}
for (let i = 0; i !== 100; ++i) {
source.next(i);
}
for (const e of s) {
e.unsubscribe();
}
});
bench.add('[master] 100 subscribed, 1 next', () => {
const source = new SubjectMaster();
const s = [];
for (let i = 0; i !== 100; ++i) {
s.push(source.asObservable().subscribe());
}
source.next(1);
for (const e of s) {
e.unsubscribe();
}
});
bench.add('[pr] 100 subscribed, 1 next', () => {
const source = new SubjectPr();
const s = [];
for (let i = 0; i !== 100; ++i) {
s.push(source.asObservable().subscribe());
}
source.next(1);
for (const e of s) {
e.unsubscribe();
}
});
bench.add('[master] 100 subscribed, 100 next', () => {
const source = new SubjectMaster();
const s = [];
for (let i = 0; i !== 100; ++i) {
s.push(source.asObservable().subscribe());
}
for (let i = 0; i !== 100; ++i) {
source.next(i);
}
for (const e of s) {
e.unsubscribe();
}
});
bench.add('[pr] 100 subscribed, 100 next', () => {
const source = new SubjectPr();
const s = [];
for (let i = 0; i !== 100; ++i) {
s.push(source.asObservable().subscribe());
}
for (let i = 0; i !== 100; ++i) {
source.next(i);
}
for (const e of s) {
e.unsubscribe();
}
});
bench.add('[master] 1000 subscribed, 1 next', () => {
const source = new SubjectMaster();
const s = [];
for (let i = 0; i !== 1000; ++i) {
s.push(source.asObservable().subscribe());
}
source.next(1);
for (const e of s) {
e.unsubscribe();
}
});
bench.add('[pr] 1000 subscribed, 1 next', () => {
const source = new SubjectPr();
const s = [];
for (let i = 0; i !== 1000; ++i) {
s.push(source.asObservable().subscribe());
}
source.next(1);
for (const e of s) {
e.unsubscribe();
}
});
bench.add('[master] 1000 subscribed, 100 next', () => {
const source = new SubjectMaster();
const s = [];
for (let i = 0; i !== 1000; ++i) {
s.push(source.asObservable().subscribe());
}
for (let i = 0; i !== 100; ++i) {
source.next(i);
}
for (const e of s) {
e.unsubscribe();
}
});
bench.add('[pr] 1000 subscribed, 100 next', () => {
const source = new SubjectPr();
const s = [];
for (let i = 0; i !== 1000; ++i) {
s.push(source.asObservable().subscribe());
}
for (let i = 0; i !== 100; ++i) {
source.next(i);
}
for (const e of s) {
e.unsubscribe();
}
});
await bench.warmup();
await bench.run();
console.table(
bench.tasks.map(({ name, result }) => {
return {
Name: name,
Mean: result?.mean,
P75: result?.p75,
P99: result?.p99,
RME: result?.rme,
};
})
);
}
run();Here are the results for the current version of the code: ┌─────────┬──────────────────────────────────────┬────────────────────────┬────────────────────────┬───────────────────────┬────────────────────┐
│ (index) │ Name │ Mean │ P75 │ P99 │ RME │
├─────────┼──────────────────────────────────────┼────────────────────────┼────────────────────────┼───────────────────────┼────────────────────┤
│ 0 │ '[master] Creating Subject' │ 0.00015643814199720028 │ 0.00019999966025352478 │ 0.0003000004217028618 │ 11.589776920801196 │
│ 1 │ '[pr] Creating Subject' │ 0.00017207796397903874 │ 0.00019999966025352478 │ 0.0005000000819563866 │ 0.8590171745396832 │
│ 2 │ '[master] 0 subscribed, 1 next' │ 0.00020557814425434853 │ 0.0002000005915760994 │ 0.0005000000819563866 │ 1.3377641081389615 │
│ 3 │ '[pr] 0 subscribed, 1 next' │ 0.00018017834274192457 │ 0.0002000005915760994 │ 0.0005000000819563866 │ 0.7602265034322395 │
│ 4 │ '[master] 0 subscribed, 100 next' │ 0.0030202487889903437 │ 0.0029999995604157448 │ 0.005300000309944153 │ 0.590282723735076 │
│ 5 │ '[pr] 0 subscribed, 100 next' │ 0.0004527702810480123 │ 0.0005000000819563866 │ 0.0008999994024634361 │ 0.5861990278579561 │
│ 6 │ '[master] 1 subscribed, 1 next' │ 0.0008216253886610409 │ 0.0007999995723366737 │ 0.001600000075995922 │ 0.6719833246454169 │
│ 7 │ '[pr] 1 subscribed, 1 next' │ 0.000856679151685021 │ 0.0008000005036592484 │ 0.0015000002458691597 │ 0.7766441850042467 │
│ 8 │ '[master] 1 subscribed, 100 next' │ 0.004788513404096101 │ 0.004699999466538429 │ 0.008899999782443047 │ 0.7056875628905762 │
│ 9 │ '[pr] 1 subscribed, 100 next' │ 0.0014018038008829956 │ 0.0013999994844198227 │ 0.0022999998182058334 │ 0.8684742543095227 │
│ 10 │ '[master] 3 subscribed, 1 next' │ 0.0018978834599352103 │ 0.0017999997362494469 │ 0.0034000007435679436 │ 3.032935309575753 │
│ 11 │ '[pr] 3 subscribed, 1 next' │ 0.0017642026010910529 │ 0.0016999999061226845 │ 0.003200000151991844 │ 0.5353075708343223 │
│ 12 │ '[master] 3 subscribed, 100 next' │ 0.00802172429488669 │ 0.007799999788403511 │ 0.01489999983459711 │ 0.5660223830939138 │
│ 13 │ '[pr] 3 subscribed, 100 next' │ 0.003131604704897833 │ 0.0030000004917383194 │ 0.005499999970197678 │ 1.134493567559593 │
│ 14 │ '[master] 10 subscribed, 1 next' │ 0.004826806852278755 │ 0.004499000497162342 │ 0.008899999782443047 │ 0.6422339682792549 │
│ 15 │ '[pr] 10 subscribed, 1 next' │ 0.004576598058776545 │ 0.0044999998062849045 │ 0.0074000004678964615 │ 1.3101714281276127 │
│ 16 │ '[master] 10 subscribed, 100 next' │ 0.020022055576250558 │ 0.01850000023841858 │ 0.03910000063478947 │ 0.6652153397296293 │
│ 17 │ '[pr] 10 subscribed, 100 next' │ 0.009184353557200084 │ 0.008299999870359898 │ 0.015599999576807022 │ 2.183817921324249 │
│ 18 │ '[master] 100 subscribed, 1 next' │ 0.04320903015868642 │ 0.038200000301003456 │ 0.10059900023043156 │ 1.2233526482136148 │
│ 19 │ '[pr] 100 subscribed, 1 next' │ 0.03559318009971064 │ 0.03399999998509884 │ 0.07059899996966124 │ 0.7623208783019286 │
│ 20 │ '[master] 100 subscribed, 100 next' │ 0.17524525809325278 │ 0.1589989997446537 │ 0.36219900008291006 │ 0.6542612434974838 │
│ 21 │ '[pr] 100 subscribed, 100 next' │ 0.07490052990522236 │ 0.06799900066107512 │ 0.15750000067055225 │ 0.8771896860089285 │
│ 22 │ '[master] 1000 subscribed, 1 next' │ 1.5466663203015925 │ 1.3917950000613928 │ 3.0632890006527305 │ 1.1953427047583378 │
│ 23 │ '[pr] 1000 subscribed, 1 next' │ 0.4354844354032539 │ 0.3509990004822612 │ 1.4115949999541044 │ 1.3006120132927175 │
│ 24 │ '[master] 1000 subscribed, 100 next' │ 2.8816406472022646 │ 3.048589000478387 │ 5.759379000402987 │ 0.58972696843935 │
│ 25 │ '[pr] 1000 subscribed, 100 next' │ 1.3074916874016635 │ 1.176095999777317 │ 2.6631909999996424 │ 0.7865946049284476 │
└─────────┴──────────────────────────────────────┴────────────────────────┴────────────────────────┴───────────────────────┴────────────────────┘For previous versionIt used ┌─────────┬──────────────────────────────────────┬────────────────────────┬───────────────────────┬───────────────────────┬────────────────────┐
│ (index) │ Name │ Mean │ P75 │ P99 │ RME │
├─────────┼──────────────────────────────────────┼────────────────────────┼───────────────────────┼───────────────────────┼────────────────────┤
│ 0 │ '[master] Creating Subject' │ 0.00012953836437439974 │ 0.000100000761449337 │ 0.0003000004217028618 │ 0.8079907019422605 │
│ 1 │ '[pr] Creating Subject' │ 0.00017635711767006987 │ 0.0002000005915760994 │ 0.0004000002518296242 │ 0.8289423819308911 │
│ 2 │ '[master] 0 subscribed, 1 next' │ 0.00020459377653131344 │ 0.0002000005915760994 │ 0.0005000000819563866 │ 1.1378771813981523 │
│ 3 │ '[pr] 0 subscribed, 1 next' │ 0.00020221618217370962 │ 0.0002000005915760994 │ 0.0005000000819563866 │ 0.6810397390682086 │
│ 4 │ '[master] 0 subscribed, 100 next' │ 0.0029463214406694736 │ 0.0028999997302889824 │ 0.005300000309944153 │ 0.5257643196869795 │
│ 5 │ '[pr] 0 subscribed, 100 next' │ 0.002991678455293176 │ 0.0028999997302889824 │ 0.006000000052154064 │ 0.5867012451076267 │
│ 6 │ '[master] 1 subscribed, 1 next' │ 0.0007954250431398286 │ 0.000700000673532486 │ 0.001600000075995922 │ 0.8766148496955309 │
│ 7 │ '[pr] 1 subscribed, 1 next' │ 0.0008213868146737953 │ 0.000700000673532486 │ 0.0016999999061226845 │ 1.1956179944223218 │
│ 8 │ '[master] 1 subscribed, 100 next' │ 0.004908873666542814 │ 0.004700000397861004 │ 0.009000000543892384 │ 0.590427156123814 │
│ 9 │ '[pr] 1 subscribed, 100 next' │ 0.004821027538623853 │ 0.004699999466538429 │ 0.008899999782443047 │ 1.346060178011175 │
│ 10 │ '[master] 3 subscribed, 1 next' │ 0.0017776441528556996 │ 0.001700000837445259 │ 0.0032999999821186066 │ 0.5591522908763634 │
│ 11 │ '[pr] 3 subscribed, 1 next' │ 0.002093957002878406 │ 0.002199999988079071 │ 0.0035999994724988937 │ 3.92953070123866 │
│ 12 │ '[master] 3 subscribed, 100 next' │ 0.008743048678321383 │ 0.008100000210106373 │ 0.015899999998509884 │ 0.6343904943811376 │
│ 13 │ '[pr] 3 subscribed, 100 next' │ 0.008495988327560293 │ 0.007900000549852848 │ 0.015999999828636646 │ 0.8790439256253967 │
│ 14 │ '[master] 10 subscribed, 1 next' │ 0.0043729048539165325 │ 0.00430000014603138 │ 0.008100000210106373 │ 0.9333949527437183 │
│ 15 │ '[pr] 10 subscribed, 1 next' │ 0.0045709372217430525 │ 0.0044999998062849045 │ 0.008200000040233135 │ 0.5809235637555371 │
│ 16 │ '[master] 10 subscribed, 100 next' │ 0.021834660001977563 │ 0.01919999998062849 │ 0.042500000447034836 │ 0.7986149010500317 │
│ 17 │ '[pr] 10 subscribed, 100 next' │ 0.019986633528925612 │ 0.019100000150501728 │ 0.03870000038295984 │ 0.609131840525482 │
│ 18 │ '[master] 100 subscribed, 1 next' │ 0.03534182979824568 │ 0.03379999939352274 │ 0.07440000027418137 │ 0.7415500120208155 │
│ 19 │ '[pr] 100 subscribed, 1 next' │ 0.0387508219938843 │ 0.0353990001603961 │ 0.075399000197649 │ 0.8391257949669215 │
│ 20 │ '[master] 100 subscribed, 100 next' │ 0.201847207995411 │ 0.19289899989962578 │ 0.4039989998564124 │ 0.5170851608490207 │
│ 21 │ '[pr] 100 subscribed, 100 next' │ 0.16869005630528555 │ 0.1540999999269843 │ 0.41039799991995096 │ 0.6719182298208819 │
│ 22 │ '[master] 1000 subscribed, 1 next' │ 1.5724196430016308 │ 1.404395000077784 │ 2.982989000156522 │ 1.3230435037243793 │
│ 23 │ '[pr] 1000 subscribed, 1 next' │ 0.38491055010296404 │ 0.34489900059998035 │ 0.7981970002874732 │ 0.7066891146691681 │
│ 24 │ '[master] 1000 subscribed, 100 next' │ 3.4955092910996637 │ 3.423287999816239 │ 12.234055999666452 │ 1.1369695485258002 │
│ 25 │ '[pr] 1000 subscribed, 100 next' │ 1.7436433385007084 │ 1.9780930001288652 │ 3.649686999619007 │ 0.534574044984933 │
└─────────┴──────────────────────────────────────┴────────────────────────┴───────────────────────┴───────────────────────┴────────────────────┘For previous previous versionIt used ┌─────────┬──────────────────────────────────────┬────────────────────────┬────────────────────────┬───────────────────────┬────────────────────┐
│ (index) │ Name │ Mean │ P75 │ P99 │ RME │
├─────────┼──────────────────────────────────────┼────────────────────────┼────────────────────────┼───────────────────────┼────────────────────┤
│ 0 │ '[master] Creating Subject' │ 0.00013438917905651826 │ 0.000100000761449337 │ 0.0003000004217028618 │ 1.3808905613853422 │
│ 1 │ '[pr] Creating Subject' │ 0.0001629036470950004 │ 0.00019999966025352478 │ 0.0004000002518296242 │ 1.0150111866666365 │
│ 2 │ '[master] 0 subscribed, 1 next' │ 0.00020070787559535244 │ 0.0002000005915760994 │ 0.0004000002518296242 │ 1.1623744271141512 │
│ 3 │ '[pr] 0 subscribed, 1 next' │ 0.00022203701635951922 │ 0.0002000005915760994 │ 0.0005000000819563866 │ 0.6444068421817747 │
│ 4 │ '[master] 0 subscribed, 100 next' │ 0.002925753821364004 │ 0.0028999997302889824 │ 0.004700000397861004 │ 0.6675482695040803 │
│ 5 │ '[pr] 0 subscribed, 100 next' │ 0.0029455148154576153 │ 0.00279999990016222 │ 0.005699999630451202 │ 0.7283875789421631 │
│ 6 │ '[master] 1 subscribed, 1 next' │ 0.0008836313455785351 │ 0.0008999994024634361 │ 0.0016999999061226845 │ 2.307640350615159 │
│ 7 │ '[pr] 1 subscribed, 1 next' │ 0.0007251301898595778 │ 0.000700000673532486 │ 0.0014000004157423973 │ 0.5369440230363686 │
│ 8 │ '[master] 1 subscribed, 100 next' │ 0.004941166842718795 │ 0.0046000005677342415 │ 0.009399999864399433 │ 0.6682103605826338 │
│ 9 │ '[pr] 1 subscribed, 100 next' │ 0.005783039856998958 │ 0.006799999624490738 │ 0.010099999606609344 │ 2.760054332032325 │
│ 10 │ '[master] 3 subscribed, 1 next' │ 0.0016769644515204616 │ 0.001600000075995922 │ 0.0027000000700354576 │ 1.227012134545868 │
│ 11 │ '[pr] 3 subscribed, 1 next' │ 0.0016556261053327526 │ 0.001600000075995922 │ 0.0029999995604157448 │ 0.5915214577550268 │
│ 12 │ '[master] 3 subscribed, 100 next' │ 0.008435009342731267 │ 0.007799999788403511 │ 0.015899999998509884 │ 0.7330348607559893 │
│ 13 │ '[pr] 3 subscribed, 100 next' │ 0.008118471813221768 │ 0.007699999958276749 │ 0.015999999828636646 │ 0.6762544367799905 │
│ 14 │ '[master] 10 subscribed, 1 next' │ 0.004619361542797714 │ 0.00430000014603138 │ 0.008700000122189522 │ 0.6865332120986498 │
│ 15 │ '[pr] 10 subscribed, 1 next' │ 0.004598094030228484 │ 0.004500000737607479 │ 0.007698999717831612 │ 0.6325973043805078 │
│ 16 │ '[master] 10 subscribed, 100 next' │ 0.022130750062305478 │ 0.018799999728798866 │ 0.04339999984949827 │ 2.150985307759586 │
│ 17 │ '[pr] 10 subscribed, 100 next' │ 0.019448945272817215 │ 0.018399999476969242 │ 0.03890000004321337 │ 0.7046440921562903 │
│ 18 │ '[master] 100 subscribed, 1 next' │ 0.03653645173544088 │ 0.034199999645352364 │ 0.07739999983459711 │ 0.8187488838632786 │
│ 19 │ '[pr] 100 subscribed, 1 next' │ 0.036315523351770795 │ 0.034500000067055225 │ 0.07210000045597553 │ 0.7989402616875521 │
│ 20 │ '[master] 100 subscribed, 100 next' │ 0.2089092620057985 │ 0.19649900030344725 │ 0.4164990000426769 │ 0.5958587469091308 │
│ 21 │ '[pr] 100 subscribed, 100 next' │ 0.1770285663041286 │ 0.15689999982714653 │ 0.43609900027513504 │ 0.9649566330973336 │
│ 22 │ '[master] 1000 subscribed, 1 next' │ 1.5781902391046285 │ 1.4167949995025992 │ 3.3108860002830625 │ 0.9724061547552825 │
│ 23 │ '[pr] 1000 subscribed, 1 next' │ 0.39690413890369236 │ 0.35139899980276823 │ 0.8345969999209046 │ 0.7682173831587366 │
│ 24 │ '[master] 1000 subscribed, 100 next' │ 3.2799664519059473 │ 3.4474869994446635 │ 6.460476000793278 │ 0.4891545002124059 │
│ 25 │ '[pr] 1000 subscribed, 100 next' │ 1.9804286305020564 │ 2.026992999948561 │ 6.734376000240445 │ 1.2118338955923968 │
└─────────┴──────────────────────────────────────┴────────────────────────┴────────────────────────┴───────────────────────┴────────────────────┘All these measurements have been done on a codespace of GitHub against 10k runs. I'll try to issue one against even more runs. |
|
Same benchmark but with 1,000,000 iterations: ┌─────────┬─────────────────────────────────────┬────────────────────────┬────────────────────────┬────────────────────────┬─────────────────────┐
│ (index) │ Name │ Mean │ P75 │ P99 │ RME │
├─────────┼─────────────────────────────────────┼────────────────────────┼────────────────────────┼────────────────────────┼─────────────────────┤
│ 0 │ '[master] Creating Subject' │ 0.00013759328511377957 │ 0.00019999966025352478 │ 0.00039999932050704956 │ 0.33501543849907456 │
│ 1 │ '[pr] Creating Subject' │ 0.00022728897428750432 │ 0.00029999949038028717 │ 0.0005000000819563866 │ 4.775231779343227 │
│ 2 │ '[master] 0 subscribed, 1 next' │ 0.000341275965122363 │ 0.0004000002518296242 │ 0.000599999912083149 │ 1.114983178579931 │
│ 3 │ '[pr] 0 subscribed, 1 next' │ 0.00019064141631205663 │ 0.0002000005915760994 │ 0.0005000000819563866 │ 4.427243915247421 │
│ 4 │ '[master] 0 subscribed, 100 next' │ 0.003445461113855243 │ 0.0032989997416734695 │ 0.006000000052154064 │ 1.8685515250963507 │
│ 5 │ '[pr] 0 subscribed, 100 next' │ 0.00044625133478185317 │ 0.000499999150633812 │ 0.0009000003337860107 │ 0.6086487209661724 │
│ 6 │ '[master] 1 subscribed, 1 next' │ 0.0008947599909277633 │ 0.0010000001639127731 │ 0.0017999997362494469 │ 0.8469281728715339 │
│ 7 │ '[pr] 1 subscribed, 1 next' │ 0.0007666839421559125 │ 0.0007999995723366737 │ 0.0015000002458691597 │ 0.462126717617466 │
│ 8 │ '[master] 1 subscribed, 100 next' │ 0.004968361307883635 │ 0.004700000397861004 │ 0.009399999864399433 │ 0.3859075153627072 │
│ 9 │ '[pr] 1 subscribed, 100 next' │ 0.0013845615506991745 │ 0.001300000585615635 │ 0.0027000000700354576 │ 0.8215533645812437 │
│ 10 │ '[master] 3 subscribed, 1 next' │ 0.0018689319399669765 │ 0.0017999997362494469 │ 0.0034999996423721313 │ 0.7049844808800837 │
│ 11 │ '[pr] 3 subscribed, 1 next' │ 0.0016349651360698045 │ 0.001600000075995922 │ 0.003100000321865082 │ 0.3527873535513193 │
│ 12 │ '[master] 3 subscribed, 100 next' │ 0.008423075634939596 │ 0.007800000719726086 │ 0.01619999948889017 │ 0.29118011818976947 │
│ 13 │ '[pr] 3 subscribed, 100 next' │ 0.0030171170289674774 │ 0.002900000661611557 │ 0.005400000140070915 │ 0.47993844836563104 │
│ 14 │ '[master] 10 subscribed, 1 next' │ 0.004465225901249796 │ 0.004200000315904617 │ 0.008200000040233135 │ 0.5787421444467985 │
│ 15 │ '[pr] 10 subscribed, 1 next' │ 0.004932518482254818 │ 0.004800000227987766 │ 0.008999999612569809 │ 0.19471452904506512 │
│ 16 │ '[master] 10 subscribed, 100 next' │ 0.02027628469098825 │ 0.018200000748038292 │ 0.040300000458955765 │ 0.5120671472684313 │
│ 17 │ '[pr] 10 subscribed, 100 next' │ 0.008474460474032908 │ 0.007999999448657036 │ 0.01510000042617321 │ 0.2584698024940223 │
│ 18 │ '[master] 100 subscribed, 1 next' │ 0.0368288599881772 │ 0.03259999956935644 │ 0.08150000032037497 │ 0.4215064656065205 │
│ 19 │ '[pr] 100 subscribed, 1 next' │ 0.038937690858443265 │ 0.03500000014901161 │ 0.07730000000447035 │ 0.2287692086159684 │
│ 20 │ '[master] 100 subscribed, 100 next' │ 0.17048554184314701 │ 0.15249900054186583 │ 0.37639900017529726 │ 0.159418273229607 │
│ 21 │ '[pr] 100 subscribed, 100 next' │ 0.07705720384817198 │ 0.06880000047385693 │ 0.1596990004181862 │ 0.22773070779270208 │
└─────────┴─────────────────────────────────────┴────────────────────────┴────────────────────────┴────────────────────────┴─────────────────────┘(before (mean) / after / (before divided by after))
|
+ Loops over indices of the observers array rather than creating a new iterator and using for-of for each emission + Removes unnecessary dirty boolean in favor of clearing the snapshot, which frees up memory a little + Centralizes observers list teardown and ensures it's called during `unsubscribe()`.
|
@benlesh do you want me to re-run the benchmarks following your changes? |
|
@dubzzz I re-ran it, with some alterations. The more common case is that something will |
|
Thank you, @dubzzz |
|
Hi @benlesh, Just wondering if there is a chance to have this fix included into one stable release soon. Thanks in advance for your time |
Description:
The current implementation of the Subject was backing itself on an array of observers. When one observer unsubscribed itself, it was performing a linear scan on the array of observers and dropping the item from the array of observers. The algorithm complexity of this operation was O(n) with n being the number of observers connected to the Subject.
It caused code like:
The proposed approach consists into changing our backing collection (an array) into a Map. But we lose somehow the set capability on subject.observers (might possibly be patched in some way).
The command
cross-env TS_NODE_PROJECT=tsconfig.mocha.json mocha --config spec/support/.mocharc.js "spec/**/Subje*-spec.ts"with the new test added:BREAKING CHANGE:
Subject.observers has been impacted. If needed we can probably make it backward compatible in some ways. Here is what changed:
Related issue (if exists):