forked from firefox-devtools/profiler
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Speed up filterSamples by taking advantage of sortedness.
On the profile from firefox-devtools#4668 this makes filterSamples 10x faster. It changes the algorithm complexity from N * M to N + M, N being the number of samples and M the number of ranges. Before: https://share.firefox.dev/3Ji8qPD After: https://share.firefox.dev/3CAtmxA
- Loading branch information
Showing
4 changed files
with
210 additions
and
58 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
/* This Source Code Form is subject to the terms of the Mozilla Public | ||
* License, v. 2.0. If a copy of the MPL was not distributed with this | ||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ | ||
|
||
// @flow | ||
|
||
import { canonicalizeRangeSet } from '../../utils/range-set'; | ||
|
||
describe('canonicalizeRangeSet', function () { | ||
it('sorts unsorted ranges', function () { | ||
expect( | ||
canonicalizeRangeSet([ | ||
{ start: 5, end: 6 }, | ||
{ start: 1, end: 2 }, | ||
]) | ||
).toEqual([ | ||
{ start: 1, end: 2 }, | ||
{ start: 5, end: 6 }, | ||
]); | ||
}); | ||
|
||
it('absorbs nested ranges', function () { | ||
expect( | ||
canonicalizeRangeSet([ | ||
{ start: 1, end: 6 }, | ||
{ start: 3, end: 6 }, | ||
]) | ||
).toEqual([{ start: 1, end: 6 }]); | ||
}); | ||
|
||
it('unifies overlapping ranges', function () { | ||
expect( | ||
canonicalizeRangeSet([ | ||
{ start: 1, end: 4 }, | ||
{ start: 3, end: 6 }, | ||
]) | ||
).toEqual([{ start: 1, end: 6 }]); | ||
}); | ||
|
||
it('unifies adjacent ranges', function () { | ||
expect( | ||
canonicalizeRangeSet([ | ||
{ start: 1, end: 3 }, | ||
{ start: 3, end: 6 }, | ||
]) | ||
).toEqual([{ start: 1, end: 6 }]); | ||
}); | ||
|
||
it('removes empty ranges', function () { | ||
expect( | ||
canonicalizeRangeSet([ | ||
{ start: 1, end: 3 }, | ||
{ start: 6, end: 6 }, | ||
]) | ||
).toEqual([{ start: 1, end: 3 }]); | ||
}); | ||
|
||
it('handles this complicated example', function () { | ||
expect( | ||
canonicalizeRangeSet([ | ||
{ start: 1, end: 2.5 }, | ||
{ start: 1.5, end: 2 }, | ||
{ start: 1, end: 2 }, | ||
{ start: 1.7, end: 2.6 }, | ||
{ start: -4, end: -4 }, | ||
{ start: -4, end: -3.8 }, | ||
{ start: -3.5, end: 2.5 }, | ||
{ start: -3.5, end: -3 }, | ||
{ start: -6, end: -4 }, | ||
]) | ||
).toEqual([ | ||
{ start: -6, end: -3.8 }, | ||
{ start: -3.5, end: 2.6 }, | ||
]); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
/* This Source Code Form is subject to the terms of the Mozilla Public | ||
* License, v. 2.0. If a copy of the MPL was not distributed with this | ||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ | ||
// @flow | ||
|
||
import type { StartEndRange } from 'firefox-profiler/types'; | ||
|
||
/** | ||
* Canonicalize the list of ranges, by OR'ing nested and overlapping ranges | ||
* together so that the resulting list of ranges is a flat list of ranges which | ||
* covers the same time values. The resulting list has the following properties: | ||
* | ||
* - Sorted by range.start | ||
* - No empty ranges | ||
* - No overlap | ||
* - Adjacent ranges are collapsed into one | ||
*/ | ||
export function canonicalizeRangeSet(ranges: StartEndRange[]): StartEndRange[] { | ||
if (ranges.length === 0) { | ||
return []; | ||
} | ||
|
||
const sortedRanges = ranges.slice().sort((a, b) => a.start - b.start); | ||
let lastCanonRange = { ...sortedRanges[0] }; | ||
const canonRanges = [lastCanonRange]; | ||
|
||
for (let i = 1; i < sortedRanges.length; i++) { | ||
const range = sortedRanges[i]; | ||
if (range.start >= range.end) { | ||
// Empty or invalid range, skip. | ||
continue; | ||
} | ||
|
||
if (range.end <= lastCanonRange.end) { | ||
// lastCanonRange already covers this range completely. | ||
continue; | ||
} | ||
|
||
if (range.start <= lastCanonRange.end) { | ||
// range's beginning overlaps lastCanonRange's end. Merge the two ranges. | ||
lastCanonRange.end = range.end; | ||
continue; | ||
} | ||
|
||
lastCanonRange = { ...range }; | ||
canonRanges.push(lastCanonRange); | ||
} | ||
|
||
return canonRanges; | ||
} |