Skip to content

Commit 6b4fa9e

Browse files
committed
perf: optimize RAV allocation lookup from O(n²) to O(n)
Replace Array.find() with Map-based lookups for constant-time allocation retrieval in both TapCollector and GraphTallyCollector. This achieves approximately 5000x performance improvement for datasets with 10k+ RAVs/allocations by eliminating the nested loop that caused quadratic complexity. Changes: - tap-collector.ts: Build allocation map for O(1) lookups - graph-tally-collector.ts: Build allocation map for O(1) lookups Based on: #1146 Original author: madumas Original commit: 59efa95
1 parent 72409ea commit 6b4fa9e

2 files changed

Lines changed: 46 additions & 22 deletions

File tree

packages/indexer-common/src/allocations/graph-tally-collector.ts

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -236,20 +236,30 @@ export class GraphTallyCollector {
236236
})
237237
this.logger.trace(`[TAPv2] RAW DATA`, { ravs, allocations })
238238

239-
const pendingRAVsToProcess = ravs
240-
.map((rav) => {
241-
const signedRav = rav.getSignedRAV()
242-
return {
239+
// Create an object for O(1) allocation lookups instead of O(n) Array.find()
240+
// This optimizes performance from O(n²) to O(n) for large datasets
241+
const allocationMap: { [key: string]: Allocation } = {}
242+
for (let i = 0; i < allocations.length; i++) {
243+
const allocation = allocations[i]
244+
allocationMap[allocation.id.toLowerCase()] = allocation
245+
}
246+
247+
const pendingRAVsToProcess: RavWithAllocation[] = []
248+
for (let i = 0; i < ravs.length; i++) {
249+
const rav = ravs[i]
250+
const signedRav = rav.getSignedRAV()
251+
const allocationId = toAddress(
252+
collectionIdToAllocationId(signedRav.rav.collectionId),
253+
).toLowerCase()
254+
const allocation = allocationMap[allocationId] // O(1) lookup
255+
if (allocation !== undefined) {
256+
pendingRAVsToProcess.push({
243257
rav: signedRav,
244-
allocation: allocations.find(
245-
(a) =>
246-
a.id ===
247-
toAddress(collectionIdToAllocationId(signedRav.rav.collectionId)),
248-
),
258+
allocation: allocation,
249259
payer: rav.payer,
250-
}
251-
})
252-
.filter((rav) => rav.allocation !== undefined) as RavWithAllocation[] // this is safe because we filter out undefined allocations
260+
})
261+
}
262+
}
253263
this.logger.trace(`[TAPv2] Pending RAVs to process`, {
254264
pendingRAVsToProcess: pendingRAVsToProcess.length,
255265
})

packages/indexer-common/src/allocations/tap-collector.ts

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -216,18 +216,32 @@ export class TapCollector {
216216
ravs: ravs.length,
217217
allocations: allocations.length,
218218
})
219-
return ravs
220-
.map((rav) => {
221-
const signedRav = rav.getSignedRAV()
222-
return {
219+
220+
// Create an object for O(1) allocation lookups instead of O(n) Array.find()
221+
// This optimizes performance from O(n²) to O(n) for large datasets
222+
const allocationMap: { [key: string]: Allocation } = {}
223+
for (let i = 0; i < allocations.length; i++) {
224+
const allocation = allocations[i]
225+
allocationMap[allocation.id.toLowerCase()] = allocation
226+
}
227+
228+
const results: RavWithAllocation[] = []
229+
for (let i = 0; i < ravs.length; i++) {
230+
const rav = ravs[i]
231+
const signedRav = rav.getSignedRAV()
232+
const allocationId = toAddress(
233+
signedRav.rav.allocationId.toString(),
234+
).toLowerCase()
235+
const allocation = allocationMap[allocationId] // O(1) lookup
236+
if (allocation !== undefined) {
237+
results.push({
223238
rav: signedRav,
224-
allocation: allocations.find(
225-
(a) => a.id === toAddress(signedRav.rav.allocationId.toString()),
226-
),
239+
allocation: allocation,
227240
sender: rav.senderAddress,
228-
}
229-
})
230-
.filter((rav) => rav.allocation !== undefined) as RavWithAllocation[] // this is safe because we filter out undefined allocations
241+
})
242+
}
243+
}
244+
return results
231245
},
232246
{
233247
onError: (err) =>

0 commit comments

Comments
 (0)