-
Notifications
You must be signed in to change notification settings - Fork 12.8k
Use more explicit operations in core helpers (and other nits) #58873
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
@typescript-bot perf test this |
@DanielRosenwasser Here they are:
tscComparison Report - baseline..pr
System info unknown
Hosts
Scenarios
tsserverComparison Report - baseline..pr
System info unknown
Hosts
Scenarios
startupComparison Report - baseline..pr
System info unknown
Hosts
Scenarios
Developer Information: |
@typescript-bot perf test this faster predictable [email protected] |
Starting jobs; this comment will be updated as builds start and complete.
|
@jakebailey Here they are:
tscComparison Report - baseline..pr
System info unknown
Hosts
Scenarios
Developer Information: |
src/compiler/core.ts
Outdated
/** @internal */ | ||
/** @internal * |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The heck is happening here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I dunno how that happened, but it is pretty funny that this is never used.
@typescript-bot perf test this |
// NOTE: arrayFrom means that if the callback mutates the underlying collection, | ||
// we won't have an accurate set of values |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// NOTE: arrayFrom means that if the callback mutates the underlying collection, | |
// we won't have an accurate set of values | |
// NOTE: arrayFrom prevents `action` from modifying the map while we iterate |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To be honest, I'm not sure if the copy was intentional.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nothing fails when it's removed, huh
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Neither behavior is really easy to make guarantees about without extra book-keeping.
Right now if you add a new value and it's added into an array under the hood, you will witness a new value if you're in the middle of iterating, but not if you've already finished iterating over all values with a given key.
On the other hand, if the multimap stores a single value, you'll never see any added values as you iterate.
@DanielRosenwasser Here they are:
tscComparison Report - baseline..pr
System info unknown
Hosts
Scenarios
tsserverComparison Report - baseline..pr
System info unknown
Hosts
Scenarios
startupComparison Report - baseline..pr
System info unknown
Hosts
Scenarios
Developer Information: |
@@ -2145,6 +2120,14 @@ export function compareTextSpans(a: Partial<TextSpan> | undefined, b: Partial<Te | |||
return compareValues(a?.start, b?.start) || compareValues(a?.length, b?.length); | |||
} | |||
|
|||
/** @internal */ | |||
export function maxBy<T>(arr: readonly T[], init: number, mapper: (x: T) => number): number { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess this could always be a reduceMapped
instead.
export function arraysEqual<T>(a: readonly T[], b: readonly T[], equalityComparer: EqualityComparer<T> = equateValues): boolean { | ||
return a.length === b.length && a.every((x, i) => equalityComparer(x, b[i])); | ||
} | ||
|
||
/** @internal */ | ||
export function indexOfAnyCharCode(text: string, charCodes: readonly number[], start?: number): number { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If this function is ever called in a hot path it would probably be more efficient to convert charCodes
to a Set
first to reduce the O(text*codes)
to O(text)
if (!some(array2)) return array1; | ||
if (!some(array1)) return array2; | ||
if (array2 === undefined || array2.length === 0) return array1; | ||
if (array1 === undefined || array1.length === 0) return array2; | ||
return [...array1, ...array2]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if indexed array access is significantly faster via avoiding iterator semantics, using array1.concat(array2)
is probably faster for the same reason.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if (result ?? !v) { | ||
result ??= array.slice(0, i); | ||
if (v) { | ||
result.push(v); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This might be more readable as:
if (result ?? !v) { | |
result ??= array.slice(0, i); | |
if (v) { | |
result.push(v); | |
} | |
} | |
if (v) { | |
// If result has been initialized it is looking to collect truthy values separately. | |
// Otherwise it will be initialized when we hit our first falsy value. | |
result?.push(v); | |
} else { | |
// If this is our first falsy value, copy over the current stretch of truthy values. | |
result ??= array.slice(0, i); | |
} |
if (!multiMap.has(hash)) return false; | ||
const candidates = multiMap.get(hash)!; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this utility function expressly support undefined
being a meaningful value in the set? Otherwise this would be more efficient to call get
and do a check against undefined
instead of calling into the map twice.
@@ -305,7 +306,7 @@ function createTabularErrorsDisplay(filesInError: (ReportFileInError | undefined | |||
|
|||
const numberLength = (num: number) => Math.log(num) * Math.LOG10E + 1; | |||
const fileToErrorCount = distinctFiles.map(file => ([file, countWhere(filesInError, fileInError => fileInError!.fileName === file!.fileName)] as const)); | |||
const maxErrors = fileToErrorCount.reduce((acc, value) => Math.max(acc, value[1] || 0), 0); | |||
const maxErrors = maxBy(fileToErrorCount, 0, value => value[1]); | |||
|
|||
const headerRow = Diagnostics.Errors_Files.message; | |||
const leftColumnHeadingLength = headerRow.split(" ")[0].length; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
const leftColumnHeadingLength = headerRow.split(" ")[0].length; | |
const leftColumnFirstSpace = headerRow.indexOf(" "); | |
const leftColumnHeadingLength = leftColumnFirstSpace < 0 ? headerRow.length : leftColumnFirstSpace; |
Though I assume this line isn't a hot path.
This PR takes care of a few nits in the codebase, including:
??
over||
when possible.for
loops incore.ts
for all iteration over arrays.undefined
oncore.ts
helpers.Many of these are rooted in the need for core utilities to be faster, and therefore use fewer implicit operations like iterator usage, truthiness, etc.
There are some other things I noticed, but this seemed like a reasonable first pass.