Skip to content

Commit 1332407

Browse files
committed
Optimize the pareto combination code by 10k×
1 parent dcb7a1e commit 1332407

1 file changed

Lines changed: 79 additions & 18 deletions

File tree

src/reports/resources/report-page.js

Lines changed: 79 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -147,33 +147,56 @@ function calculateSpeedup(mergedCostAccuracy) {
147147
}
148148

149149
function 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+
177200
function 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

221282
function calculateMergedCostAccuracy(jsonData) {

0 commit comments

Comments
 (0)