Skip to content

Commit bca3841

Browse files
authored
fix: (perf) Remove extra clone operation on .values .keys .Iterator access (#3993)
* Perf(observableset): remove unwanted clone from `entries`, `values` methods * Add performance test * Add changeset * Omit redundant keys variable * More verbose perf test logs
1 parent f5e9472 commit bca3841

File tree

3 files changed

+156
-18
lines changed

3 files changed

+156
-18
lines changed

.changeset/odd-dogs-thank.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"mobx": patch
3+
---
4+
5+
Improve observableset memory footprint and performance

packages/mobx/__tests__/perf/perf.js

Lines changed: 143 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -203,11 +203,11 @@ results of this test:
203203
const ar = observable([0])
204204
const findLastIndexOfZero = computed(function () {
205205
aCalc++
206-
return ar.findLastIndex(x => x === 0);
206+
return ar.findLastIndex(x => x === 0)
207207
})
208208
const lastIndexOfZero = computed(function () {
209209
bCalc++
210-
return ar.lastIndexOf(0);
210+
return ar.lastIndexOf(0)
211211
})
212212
mobx.observe(findLastIndexOfZero, voidObserver, true)
213213
mobx.observe(lastIndexOfZero, voidObserver, true)
@@ -225,9 +225,7 @@ results of this test:
225225

226226
const end = now()
227227

228-
log(
229-
"Array findLastIndex loop - Updated in " + (end - start) + " ms."
230-
)
228+
log("Array findLastIndex loop - Updated in " + (end - start) + " ms.")
231229
t.end()
232230
})
233231

@@ -552,6 +550,146 @@ results of this test:
552550
t.end()
553551
})
554552

553+
test(`${version} - Set: initializing`, function (t) {
554+
gc()
555+
const iterationsCount = 100000
556+
let i
557+
558+
const start = Date.now()
559+
for (i = 0; i < iterationsCount; i++) {
560+
mobx.observable.set()
561+
}
562+
const end = Date.now()
563+
log("Initilizing " + iterationsCount + " maps: " + (end - start) + " ms.")
564+
t.end()
565+
})
566+
567+
test(`${version} - Set: setting and deleting properties`, function (t) {
568+
gc()
569+
const iterationsCount = 1000
570+
const propertiesCount = 10000
571+
const set = mobx.observable.set()
572+
let i
573+
let p
574+
575+
const start = Date.now()
576+
for (i = 0; i < iterationsCount; i++) {
577+
for (p = 0; p < propertiesCount; p++) {
578+
set.add("" + p)
579+
}
580+
for (p = 0; p < propertiesCount; p++) {
581+
set.delete("" + p)
582+
}
583+
}
584+
const end = Date.now()
585+
586+
log(
587+
"Setting and deleting " +
588+
propertiesCount +
589+
" set properties " +
590+
iterationsCount +
591+
" times: " +
592+
(end - start) +
593+
" ms."
594+
)
595+
t.end()
596+
})
597+
598+
test(`${version} - Set: looking up properties`, function (t) {
599+
gc()
600+
const iterationsCount = 1000
601+
const propertiesCount = 10000
602+
const set = mobx.observable.set()
603+
let i
604+
let p
605+
606+
for (p = 0; p < propertiesCount; p++) {
607+
set.add("" + p)
608+
}
609+
610+
const start = Date.now()
611+
for (i = 0; i < iterationsCount; i++) {
612+
for (p = 0; p < propertiesCount; p++) {
613+
set.has("" + p)
614+
}
615+
}
616+
const end = Date.now()
617+
618+
log(
619+
"Looking up " +
620+
propertiesCount +
621+
" set properties " +
622+
iterationsCount +
623+
" times: " +
624+
(end - start) +
625+
" ms."
626+
)
627+
t.end()
628+
})
629+
630+
test(`${version} - Set: iterator helpers`, function (t) {
631+
gc()
632+
const iterationsCount = 1000
633+
const propertiesCount = 10000
634+
const set = mobx.observable.set()
635+
let i
636+
let p
637+
638+
for (p = 0; p < propertiesCount; p++) {
639+
set.add("" + p)
640+
}
641+
642+
const start = Date.now()
643+
for (i = 0; i < iterationsCount; i++) {
644+
set.entries().take(1)
645+
}
646+
const end = Date.now()
647+
648+
log(
649+
"Single take out of" +
650+
propertiesCount +
651+
" set properties " +
652+
iterationsCount +
653+
" times: " +
654+
(end - start) +
655+
" ms."
656+
)
657+
t.end()
658+
})
659+
660+
test(`${version} - Set: conversion to array`, function (t) {
661+
gc()
662+
const iterationsCount = 1000
663+
const propertiesCount = 10000
664+
const set = mobx.observable.set()
665+
let i
666+
let p
667+
668+
for (p = 0; p < propertiesCount; p++) {
669+
set.add("" + p)
670+
}
671+
672+
const start = Date.now()
673+
for (i = 0; i < iterationsCount; i++) {
674+
Array.from(set.keys())
675+
Array.from(set.values())
676+
Array.from(set.entries())
677+
;[...set]
678+
}
679+
const end = Date.now()
680+
681+
log(
682+
"Converting " +
683+
propertiesCount +
684+
" set properties into an array" +
685+
iterationsCount +
686+
" times: " +
687+
(end - start) +
688+
" ms."
689+
)
690+
t.end()
691+
})
692+
555693
test(`${version} - Map: initializing`, function (t) {
556694
gc()
557695
const iterationsCount = 100000

packages/mobx/src/types/observableset.ts

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -211,16 +211,11 @@ export class ObservableSet<T = any> implements Set<T>, IInterceptable<ISetWillCh
211211
}
212212

213213
entries() {
214-
let nextIndex = 0
215-
const keys = Array.from(this.keys())
216-
const values = Array.from(this.values())
214+
const values = this.values()
217215
return makeIterableForSet<[T, T]>({
218216
next() {
219-
const index = nextIndex
220-
nextIndex += 1
221-
return index < values.length
222-
? { value: [keys[index], values[index]], done: false }
223-
: { value: undefined, done: true }
217+
const { value, done } = values.next()
218+
return !done ? { value: [value, value], done } : { value: undefined, done }
224219
}
225220
})
226221
}
@@ -232,13 +227,13 @@ export class ObservableSet<T = any> implements Set<T>, IInterceptable<ISetWillCh
232227
values(): SetIterator<T> {
233228
this.atom_.reportObserved()
234229
const self = this
235-
let nextIndex = 0
236-
const observableValues = Array.from(this.data_.values())
230+
const values = this.data_.values()
237231
return makeIterableForSet({
238232
next() {
239-
return nextIndex < observableValues.length
240-
? { value: self.dehanceValue_(observableValues[nextIndex++]), done: false }
241-
: { value: undefined, done: true }
233+
const { value, done } = values.next()
234+
return !done
235+
? { value: self.dehanceValue_(value), done }
236+
: { value: undefined, done }
242237
}
243238
})
244239
}

0 commit comments

Comments
 (0)