Skip to content

Commit 5800036

Browse files
committed
perf: pre-allocate records array and use for-loops in FuseIndex.create
Three changes to reduce overhead during index construction: 1. Pre-allocate `records` with `new Array(docs.length)` and write by index instead of growing via `push()`. For large collections the engine was repeatedly reallocating the backing store. 2. Replace `forEach` with plain `for` loops in `create()` and `_createObjectRecord()` to avoid closure allocation per iteration. 3. Extract `_createStringRecord` / `_createObjectRecord` so `create()` can write directly into the pre-allocated array while `add()` still uses the push-based `_addString` / `_addObject` wrappers.
1 parent 5517a9b commit 5800036

12 files changed

Lines changed: 188 additions & 90 deletions

dist/fuse.basic.cjs

Lines changed: 30 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -272,18 +272,25 @@ class FuseIndex {
272272
return;
273273
}
274274
this.isCreated = true;
275+
const len = this.docs.length;
276+
this.records = new Array(len);
277+
let recordCount = 0;
275278

276279
// List is Array<String>
277280
if (isString(this.docs[0])) {
278-
this.docs.forEach((doc, docIndex) => {
279-
this._addString(doc, docIndex);
280-
});
281+
for (let i = 0; i < len; i++) {
282+
const record = this._createStringRecord(this.docs[i], i);
283+
if (record) {
284+
this.records[recordCount++] = record;
285+
}
286+
}
281287
} else {
282288
// List is Array<Object>
283-
this.docs.forEach((doc, docIndex) => {
284-
this._addObject(doc, docIndex);
285-
});
289+
for (let i = 0; i < len; i++) {
290+
this.records[recordCount++] = this._createObjectRecord(this.docs[i], i);
291+
}
286292
}
293+
this.records.length = recordCount;
287294
this.norm.clear();
288295
}
289296
// Adds a doc to the end of the index
@@ -319,27 +326,36 @@ class FuseIndex {
319326
return this.records.length;
320327
}
321328
_addString(doc, docIndex) {
329+
const record = this._createStringRecord(doc, docIndex);
330+
if (record) {
331+
this.records.push(record);
332+
}
333+
}
334+
_addObject(doc, docIndex) {
335+
this.records.push(this._createObjectRecord(doc, docIndex));
336+
}
337+
_createStringRecord(doc, docIndex) {
322338
if (!isDefined(doc) || isBlank(doc)) {
323-
return;
339+
return null;
324340
}
325-
const record = {
341+
return {
326342
v: doc,
327343
i: docIndex,
328344
n: this.norm.get(doc)
329345
};
330-
this.records.push(record);
331346
}
332-
_addObject(doc, docIndex) {
347+
_createObjectRecord(doc, docIndex) {
333348
const record = {
334349
i: docIndex,
335350
$: {}
336351
};
337352

338353
// Iterate over every key (i.e, path), and fetch the value at that key
339-
this.keys.forEach((key, keyIndex) => {
354+
for (let keyIndex = 0, keyLen = this.keys.length; keyIndex < keyLen; keyIndex++) {
355+
const key = this.keys[keyIndex];
340356
const value = key.getFn ? key.getFn(doc) : this.getFn(doc, key.path);
341357
if (!isDefined(value)) {
342-
return;
358+
continue;
343359
}
344360
if (isArray(value)) {
345361
const subRecords = [];
@@ -379,8 +395,8 @@ class FuseIndex {
379395
};
380396
record.$[keyIndex] = subRecord;
381397
}
382-
});
383-
this.records.push(record);
398+
}
399+
return record;
384400
}
385401
toJSON() {
386402
return {

dist/fuse.basic.min.cjs

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

dist/fuse.basic.min.mjs

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

dist/fuse.basic.mjs

Lines changed: 30 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -270,18 +270,25 @@ class FuseIndex {
270270
return;
271271
}
272272
this.isCreated = true;
273+
const len = this.docs.length;
274+
this.records = new Array(len);
275+
let recordCount = 0;
273276

274277
// List is Array<String>
275278
if (isString(this.docs[0])) {
276-
this.docs.forEach((doc, docIndex) => {
277-
this._addString(doc, docIndex);
278-
});
279+
for (let i = 0; i < len; i++) {
280+
const record = this._createStringRecord(this.docs[i], i);
281+
if (record) {
282+
this.records[recordCount++] = record;
283+
}
284+
}
279285
} else {
280286
// List is Array<Object>
281-
this.docs.forEach((doc, docIndex) => {
282-
this._addObject(doc, docIndex);
283-
});
287+
for (let i = 0; i < len; i++) {
288+
this.records[recordCount++] = this._createObjectRecord(this.docs[i], i);
289+
}
284290
}
291+
this.records.length = recordCount;
285292
this.norm.clear();
286293
}
287294
// Adds a doc to the end of the index
@@ -317,27 +324,36 @@ class FuseIndex {
317324
return this.records.length;
318325
}
319326
_addString(doc, docIndex) {
327+
const record = this._createStringRecord(doc, docIndex);
328+
if (record) {
329+
this.records.push(record);
330+
}
331+
}
332+
_addObject(doc, docIndex) {
333+
this.records.push(this._createObjectRecord(doc, docIndex));
334+
}
335+
_createStringRecord(doc, docIndex) {
320336
if (!isDefined(doc) || isBlank(doc)) {
321-
return;
337+
return null;
322338
}
323-
const record = {
339+
return {
324340
v: doc,
325341
i: docIndex,
326342
n: this.norm.get(doc)
327343
};
328-
this.records.push(record);
329344
}
330-
_addObject(doc, docIndex) {
345+
_createObjectRecord(doc, docIndex) {
331346
const record = {
332347
i: docIndex,
333348
$: {}
334349
};
335350

336351
// Iterate over every key (i.e, path), and fetch the value at that key
337-
this.keys.forEach((key, keyIndex) => {
352+
for (let keyIndex = 0, keyLen = this.keys.length; keyIndex < keyLen; keyIndex++) {
353+
const key = this.keys[keyIndex];
338354
const value = key.getFn ? key.getFn(doc) : this.getFn(doc, key.path);
339355
if (!isDefined(value)) {
340-
return;
356+
continue;
341357
}
342358
if (isArray(value)) {
343359
const subRecords = [];
@@ -377,8 +393,8 @@ class FuseIndex {
377393
};
378394
record.$[keyIndex] = subRecord;
379395
}
380-
});
381-
this.records.push(record);
396+
}
397+
return record;
382398
}
383399
toJSON() {
384400
return {

dist/fuse.cjs

Lines changed: 30 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -269,18 +269,25 @@ class FuseIndex {
269269
return;
270270
}
271271
this.isCreated = true;
272+
const len = this.docs.length;
273+
this.records = new Array(len);
274+
let recordCount = 0;
272275

273276
// List is Array<String>
274277
if (isString(this.docs[0])) {
275-
this.docs.forEach((doc, docIndex) => {
276-
this._addString(doc, docIndex);
277-
});
278+
for (let i = 0; i < len; i++) {
279+
const record = this._createStringRecord(this.docs[i], i);
280+
if (record) {
281+
this.records[recordCount++] = record;
282+
}
283+
}
278284
} else {
279285
// List is Array<Object>
280-
this.docs.forEach((doc, docIndex) => {
281-
this._addObject(doc, docIndex);
282-
});
286+
for (let i = 0; i < len; i++) {
287+
this.records[recordCount++] = this._createObjectRecord(this.docs[i], i);
288+
}
283289
}
290+
this.records.length = recordCount;
284291
this.norm.clear();
285292
}
286293
// Adds a doc to the end of the index
@@ -316,27 +323,36 @@ class FuseIndex {
316323
return this.records.length;
317324
}
318325
_addString(doc, docIndex) {
326+
const record = this._createStringRecord(doc, docIndex);
327+
if (record) {
328+
this.records.push(record);
329+
}
330+
}
331+
_addObject(doc, docIndex) {
332+
this.records.push(this._createObjectRecord(doc, docIndex));
333+
}
334+
_createStringRecord(doc, docIndex) {
319335
if (!isDefined(doc) || isBlank(doc)) {
320-
return;
336+
return null;
321337
}
322-
const record = {
338+
return {
323339
v: doc,
324340
i: docIndex,
325341
n: this.norm.get(doc)
326342
};
327-
this.records.push(record);
328343
}
329-
_addObject(doc, docIndex) {
344+
_createObjectRecord(doc, docIndex) {
330345
const record = {
331346
i: docIndex,
332347
$: {}
333348
};
334349

335350
// Iterate over every key (i.e, path), and fetch the value at that key
336-
this.keys.forEach((key, keyIndex) => {
351+
for (let keyIndex = 0, keyLen = this.keys.length; keyIndex < keyLen; keyIndex++) {
352+
const key = this.keys[keyIndex];
337353
const value = key.getFn ? key.getFn(doc) : this.getFn(doc, key.path);
338354
if (!isDefined(value)) {
339-
return;
355+
continue;
340356
}
341357
if (isArray(value)) {
342358
const subRecords = [];
@@ -376,8 +392,8 @@ class FuseIndex {
376392
};
377393
record.$[keyIndex] = subRecord;
378394
}
379-
});
380-
this.records.push(record);
395+
}
396+
return record;
381397
}
382398
toJSON() {
383399
return {

dist/fuse.d.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,8 @@ declare class FuseIndex<T = any> {
193193
size(): number;
194194
_addString(doc: string, docIndex: number): void;
195195
_addObject(doc: any, docIndex: number): void;
196+
_createStringRecord(doc: string, docIndex: number): IndexRecord | null;
197+
_createObjectRecord(doc: any, docIndex: number): IndexRecord;
196198
toJSON(): {
197199
keys: ReadonlyArray<Omit<KeyObject, 'getFn'>>;
198200
records: IndexRecord[];

dist/fuse.min.cjs

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

dist/fuse.min.mjs

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

dist/fuse.mjs

Lines changed: 30 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -267,18 +267,25 @@ class FuseIndex {
267267
return;
268268
}
269269
this.isCreated = true;
270+
const len = this.docs.length;
271+
this.records = new Array(len);
272+
let recordCount = 0;
270273

271274
// List is Array<String>
272275
if (isString(this.docs[0])) {
273-
this.docs.forEach((doc, docIndex) => {
274-
this._addString(doc, docIndex);
275-
});
276+
for (let i = 0; i < len; i++) {
277+
const record = this._createStringRecord(this.docs[i], i);
278+
if (record) {
279+
this.records[recordCount++] = record;
280+
}
281+
}
276282
} else {
277283
// List is Array<Object>
278-
this.docs.forEach((doc, docIndex) => {
279-
this._addObject(doc, docIndex);
280-
});
284+
for (let i = 0; i < len; i++) {
285+
this.records[recordCount++] = this._createObjectRecord(this.docs[i], i);
286+
}
281287
}
288+
this.records.length = recordCount;
282289
this.norm.clear();
283290
}
284291
// Adds a doc to the end of the index
@@ -314,27 +321,36 @@ class FuseIndex {
314321
return this.records.length;
315322
}
316323
_addString(doc, docIndex) {
324+
const record = this._createStringRecord(doc, docIndex);
325+
if (record) {
326+
this.records.push(record);
327+
}
328+
}
329+
_addObject(doc, docIndex) {
330+
this.records.push(this._createObjectRecord(doc, docIndex));
331+
}
332+
_createStringRecord(doc, docIndex) {
317333
if (!isDefined(doc) || isBlank(doc)) {
318-
return;
334+
return null;
319335
}
320-
const record = {
336+
return {
321337
v: doc,
322338
i: docIndex,
323339
n: this.norm.get(doc)
324340
};
325-
this.records.push(record);
326341
}
327-
_addObject(doc, docIndex) {
342+
_createObjectRecord(doc, docIndex) {
328343
const record = {
329344
i: docIndex,
330345
$: {}
331346
};
332347

333348
// Iterate over every key (i.e, path), and fetch the value at that key
334-
this.keys.forEach((key, keyIndex) => {
349+
for (let keyIndex = 0, keyLen = this.keys.length; keyIndex < keyLen; keyIndex++) {
350+
const key = this.keys[keyIndex];
335351
const value = key.getFn ? key.getFn(doc) : this.getFn(doc, key.path);
336352
if (!isDefined(value)) {
337-
return;
353+
continue;
338354
}
339355
if (isArray(value)) {
340356
const subRecords = [];
@@ -374,8 +390,8 @@ class FuseIndex {
374390
};
375391
record.$[keyIndex] = subRecord;
376392
}
377-
});
378-
this.records.push(record);
393+
}
394+
return record;
379395
}
380396
toJSON() {
381397
return {

dist/fuse.worker.min.mjs

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)