@@ -147,33 +147,56 @@ function calculateSpeedup(mergedCostAccuracy) {
147147}
148148
149149function paretoUnion ( curve1 , curve2 ) {
150- const combined = [ ] ;
150+ const curve1Length = curve1 . length ;
151+ const curve2Length = curve2 . length ;
152+ const output = new Array ( curve1Length + curve2Length ) ;
153+ let outputLength = 0 ;
151154 let index1 = 0 ;
152155 let index2 = 0 ;
153156
154- while ( index1 < curve1 . length && index2 < curve2 . length ) {
155- const [ cost1 , error1 ] = curve1 [ index1 ] ;
156- const [ cost2 , error2 ] = curve2 [ index2 ] ;
157+ while ( index1 < curve1Length && index2 < curve2Length ) {
158+ const point1 = curve1 [ index1 ] ;
159+ const point2 = curve2 [ index2 ] ;
160+ const cost1 = point1 [ 0 ] ;
161+ const error1 = point1 [ 1 ] ;
162+ const cost2 = point2 [ 0 ] ;
163+ const error2 = point2 [ 1 ] ;
157164 if ( cost1 === cost2 && error1 === error2 ) {
158- combined . push ( curve1 [ index1 ] ) ;
165+ output [ outputLength ] = point1 ;
166+ outputLength += 1 ;
159167 index1 += 1 ;
160168 index2 += 1 ;
161169 } else if ( cost1 <= cost2 && error1 <= error2 ) {
162170 index2 += 1 ;
163171 } else if ( cost1 >= cost2 && error1 >= error2 ) {
164172 index1 += 1 ;
165173 } else if ( error1 < error2 ) {
166- combined . push ( curve1 [ index1 ] ) ;
174+ output [ outputLength ] = point1 ;
175+ outputLength += 1 ;
167176 index1 += 1 ;
168177 } else {
169- combined . push ( curve2 [ index2 ] ) ;
178+ output [ outputLength ] = point2 ;
179+ outputLength += 1 ;
170180 index2 += 1 ;
171181 }
172182 }
173183
174- return combined . concat ( curve1 . slice ( index1 ) , curve2 . slice ( index2 ) ) ;
184+ while ( index1 < curve1Length ) {
185+ output [ outputLength ] = curve1 [ index1 ] ;
186+ outputLength += 1 ;
187+ index1 += 1 ;
188+ }
189+ while ( index2 < curve2Length ) {
190+ output [ outputLength ] = curve2 [ index2 ] ;
191+ outputLength += 1 ;
192+ index2 += 1 ;
193+ }
194+ output . length = outputLength ;
195+ return output ;
175196}
176197
198+ const PARETO_BLOCK_SIZE = 32 ;
199+
177200function paretoConvex ( points ) {
178201 const convex = [ ] ;
179202
@@ -204,18 +227,56 @@ function paretoShift([cost0, error0], frontier) {
204227 return frontier . map ( ( [ cost , error ] ) => [ cost0 + cost , error0 + error ] ) ;
205228}
206229
207- function paretoCombine ( frontiers ) {
208- const minimizedFrontiers = frontiers . map ( ( frontier ) => paretoMinimize ( frontier ) ) ;
209- return minimizedFrontiers . reduce ( ( combined , frontier ) => {
210- if ( combined . length === 0 ) {
211- return frontier ;
230+ function paretoUnionBalanced ( curves ) {
231+ let level = curves ;
232+ while ( level . length > 1 ) {
233+ const nextLevel = [ ] ;
234+ for ( let index = 0 ; index < level . length ; index += 2 ) {
235+ if ( index + 1 >= level . length ) {
236+ nextLevel . push ( level [ index ] ) ;
237+ } else {
238+ nextLevel . push ( paretoUnion ( level [ index ] , level [ index + 1 ] ) ) ;
239+ }
212240 }
241+ level = nextLevel ;
242+ }
243+ return level [ 0 ] || [ ] ;
244+ }
213245
214- return combined . reduce ( ( combinedFrontier , point ) => {
215- const points = paretoMinimize ( paretoShift ( point , frontier ) ) ;
216- return paretoUnion ( points , combinedFrontier ) ;
217- } , [ ] ) ;
218- } , [ ] ) ;
246+ // Merge shifted frontiers in small balanced batches. This keeps the
247+ // large-large unions that are fast for JS without retaining every
248+ // shifted frontier at once.
249+ function paretoCombineTwo ( combined , frontier ) {
250+ if ( combined . length === 0 ) {
251+ return frontier ;
252+ }
253+
254+ let combinedFrontier = [ ] ;
255+ for ( let index = 0 ; index < combined . length ; index += PARETO_BLOCK_SIZE ) {
256+ const shiftedFrontiers = [ ] ;
257+ const limit = Math . min ( index + PARETO_BLOCK_SIZE , combined . length ) ;
258+ for ( let pointIndex = index ; pointIndex < limit ; pointIndex += 1 ) {
259+ shiftedFrontiers . push ( paretoShift ( combined [ pointIndex ] , frontier ) ) ;
260+ }
261+ combinedFrontier = paretoUnion ( paretoUnionBalanced ( shiftedFrontiers ) , combinedFrontier ) ;
262+ }
263+ return paretoConvex ( combinedFrontier ) ;
264+ }
265+
266+ function paretoCombine ( frontiers ) {
267+ let level = frontiers . map ( ( frontier ) => paretoConvex ( paretoMinimize ( frontier ) ) ) ;
268+ while ( level . length > 1 ) {
269+ const nextLevel = [ ] ;
270+ for ( let index = 0 ; index < level . length ; index += 2 ) {
271+ if ( index + 1 >= level . length ) {
272+ nextLevel . push ( level [ index ] ) ;
273+ } else {
274+ nextLevel . push ( paretoCombineTwo ( level [ index ] , level [ index + 1 ] ) ) ;
275+ }
276+ }
277+ level = nextLevel ;
278+ }
279+ return level [ 0 ] || [ ] ;
219280}
220281
221282function calculateMergedCostAccuracy ( jsonData ) {
0 commit comments