mirror of
https://github.com/oxc-project/oxc.git
synced 2026-11-04 10:29:58 +00:00
perf(linter/plugins): use shift over multiply for powers of 2 (#27536)
Follow on after #27393. After that PR, `Comment` is now 24 bytes, whereas `Token` is 16 bytes. In Oxlint plugins code, this change required using multiplication to convert from comment index to position in buffer. But `Token`'s size is still a power of 2, so can still use shift for that (faster). Also, the shift required for these conversions for the tokens/comments/tokens-and-comments buffers were hardcoded in various places. Replace them all with constants - `<< 2` -> `<< TOKEN_SIZE32_SHIFT` - to make is clearer what's going on.
This commit is contained in:
1 parent
0285738b2c
commit
c5f9235e0a
5 files changed
+29
-12
No files matched your search
@@ -12,6 +12,8 @@ import {
|
||||
initComments,
|
||||
initCommentsBuffer,
|
||||
} from "./comments.ts";
|
||||
// oxlint-disable-next-line no-unused-vars -- used in inlined `firstTokenAtOrAfter`
|
||||
import { TOKEN_SIZE32_SHIFT } from "./tokens.ts";
|
||||
import {
|
||||
initTokensAndCommentsBuffer,
|
||||
tokensAndCommentsInt32,
|
||||
|
||||
@@ -267,6 +267,8 @@ const TOKEN_DESERIALIZED_FLAG_OFFSET = TOKEN_SIZE - 1;
|
||||
|
||||
const TOKEN_SIZE_SHIFT = 4;
|
||||
debugAssert(TOKEN_SIZE === 1 << TOKEN_SIZE_SHIFT);
|
||||
export const TOKEN_SIZE32_SHIFT = TOKEN_SIZE_SHIFT - 2;
|
||||
debugAssert(TOKEN_SIZE32 === 1 << TOKEN_SIZE32_SHIFT);
|
||||
|
||||
const KIND_FIELD_OFFSET = 8;
|
||||
const IS_ESCAPED_FIELD_OFFSET = 10;
|
||||
@@ -350,7 +352,7 @@ export function initTokensBuffer(): void {
|
||||
const arrayBuffer = buffer.buffer,
|
||||
absolutePos = buffer.byteOffset + tokensPos;
|
||||
tokensUint8 = new Uint8Array(arrayBuffer, absolutePos, tokensLen << TOKEN_SIZE_SHIFT);
|
||||
tokensInt32 = new Int32Array(arrayBuffer, absolutePos, tokensLen << (TOKEN_SIZE_SHIFT - 2));
|
||||
tokensInt32 = new Int32Array(arrayBuffer, absolutePos, tokensLen << TOKEN_SIZE32_SHIFT);
|
||||
|
||||
// Grow caches if needed. After first few files, caches should have grown large enough to service all files.
|
||||
// Later files will skip this step, and allocations stop.
|
||||
@@ -514,7 +516,7 @@ function debugCheckValidRanges(): void {
|
||||
|
||||
let lastEnd = 0;
|
||||
for (let i = 0; i < tokensLen; i++) {
|
||||
const pos32 = i << 2;
|
||||
const pos32 = i << TOKEN_SIZE32_SHIFT;
|
||||
const start = tokensInt32![pos32];
|
||||
const end = tokensInt32![pos32 + 1];
|
||||
if (end <= start) throw new Error(`Invalid token range: ${start}-${end}`);
|
||||
|
||||
@@ -25,6 +25,7 @@ import {
|
||||
tokensLen,
|
||||
tokensInt32,
|
||||
TOKEN_SIZE32,
|
||||
TOKEN_SIZE32_SHIFT,
|
||||
} from "./tokens.ts";
|
||||
import { EMPTY_INT32_ARRAY } from "../utils/typed_arrays.ts";
|
||||
import { debugAssert, debugAssertIsNonNull } from "../utils/asserts.ts";
|
||||
@@ -196,7 +197,7 @@ function mergeTokensAndComments(tokensInt32: Int32Array, commentsInt32: Int32Arr
|
||||
);
|
||||
return;
|
||||
}
|
||||
tokenStart = tokensInt32[tokenIndex * TOKEN_SIZE32];
|
||||
tokenStart = tokensInt32[tokenIndex << TOKEN_SIZE32_SHIFT];
|
||||
} while (tokenStart < commentStart);
|
||||
|
||||
// Process run of comments
|
||||
@@ -297,7 +298,7 @@ export function getTokenOrCommentEnd(entryIndex: number): number {
|
||||
const pos32 = entryIndex << MERGED_SIZE32_SHIFT;
|
||||
const originalIndex = tokensAndCommentsInt32![pos32 + MERGED_ORIGINAL_INDEX_OFFSET32];
|
||||
return tokensAndCommentsInt32![pos32 + MERGED_TYPE_OFFSET32] === MERGED_TYPE_TOKEN
|
||||
? tokensInt32![originalIndex * TOKEN_SIZE32 + 1]
|
||||
? tokensInt32![(originalIndex << TOKEN_SIZE32_SHIFT) + 1]
|
||||
: commentsInt32![originalIndex * COMMENT_SIZE32 + 1];
|
||||
}
|
||||
|
||||
|
||||
@@ -2,13 +2,21 @@
|
||||
* `SourceCode` methods related to tokens.
|
||||
*/
|
||||
|
||||
import { cachedTokens, tokensInt32, tokensLen, initTokensBuffer, getToken } from "./tokens.ts";
|
||||
import {
|
||||
cachedTokens,
|
||||
tokensInt32,
|
||||
tokensLen,
|
||||
initTokensBuffer,
|
||||
getToken,
|
||||
TOKEN_SIZE32_SHIFT,
|
||||
} from "./tokens.ts";
|
||||
import {
|
||||
tokensAndCommentsInt32,
|
||||
tokensAndCommentsLen,
|
||||
getTokenOrComment,
|
||||
getTokenOrCommentEnd,
|
||||
initTokensAndCommentsBuffer,
|
||||
MERGED_SIZE32_SHIFT,
|
||||
} from "./tokens_and_comments.ts";
|
||||
import { debugAssertIsNonNull } from "../utils/asserts.ts";
|
||||
|
||||
@@ -1290,7 +1298,7 @@ export function getTokenByRangeStart<Options extends RangeOptions | null | undef
|
||||
// This makes it safe to use `>> 1` for division by 2 (which is faster than `>>> 1`).
|
||||
for (let lo = 0, hi = len; lo < hi;) {
|
||||
const mid = (lo + hi) >> 1;
|
||||
const tokenStart = int32[mid << 2];
|
||||
const tokenStart = int32[mid << TOKEN_SIZE32_SHIFT];
|
||||
if (tokenStart < offset) {
|
||||
lo = mid + 1;
|
||||
} else if (tokenStart > offset) {
|
||||
@@ -1357,7 +1365,7 @@ export function isSpaceBetween(first: NodeOrToken, second: NodeOrToken): boolean
|
||||
let index = firstTokenAtOrAfter(tokensAndCommentsInt32, rangeStart, 0, tokensAndCommentsLen);
|
||||
|
||||
for (let lastTokenEnd = rangeStart; index < tokensAndCommentsLen; index++) {
|
||||
const tokenStart = tokensAndCommentsInt32[index << 2];
|
||||
const tokenStart = tokensAndCommentsInt32[index << MERGED_SIZE32_SHIFT];
|
||||
// The first token of the later node should undergo the check in the second branch
|
||||
if (tokenStart > rangeEnd) break;
|
||||
if (tokenStart !== lastTokenEnd) return true;
|
||||
@@ -1413,7 +1421,7 @@ export function isSpaceBetweenTokens(first: NodeOrToken, second: NodeOrToken): b
|
||||
let index = firstTokenAtOrAfter(tokensAndCommentsInt32, rangeStart, 0, tokensAndCommentsLen);
|
||||
|
||||
for (let lastTokenEnd = rangeStart; index < tokensAndCommentsLen; index++) {
|
||||
const tokenStart = tokensAndCommentsInt32[index << 2];
|
||||
const tokenStart = tokensAndCommentsInt32[index << MERGED_SIZE32_SHIFT];
|
||||
|
||||
// The first token of the later node should undergo the check in the second branch
|
||||
if (tokenStart > rangeEnd) break;
|
||||
@@ -1471,7 +1479,7 @@ function getEntry(index: number, includeComments: boolean): TokenOrComment {
|
||||
* @returns Start offset in source text
|
||||
*/
|
||||
function entryStart(index: number, int32: Int32Array): number {
|
||||
return int32[index << 2];
|
||||
return int32[index << TOKEN_SIZE32_SHIFT];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1483,7 +1491,9 @@ function entryStart(index: number, int32: Int32Array): number {
|
||||
* @returns End offset in source text
|
||||
*/
|
||||
function entryEnd(index: number, includeComments: boolean): number {
|
||||
return includeComments === true ? getTokenOrCommentEnd(index) : tokensInt32![(index << 2) + 1];
|
||||
return includeComments === true
|
||||
? getTokenOrCommentEnd(index)
|
||||
: tokensInt32![(index << TOKEN_SIZE32_SHIFT) + 1];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1550,7 +1560,7 @@ export function firstTokenAtOrAfter(
|
||||
): number {
|
||||
for (let endIndex = length; startIndex < endIndex;) {
|
||||
const mid = (startIndex + endIndex) >> 1;
|
||||
if (int32[mid << 2] < offset) {
|
||||
if (int32[mid << TOKEN_SIZE32_SHIFT] < offset) {
|
||||
startIndex = mid + 1;
|
||||
} else {
|
||||
endIndex = mid;
|
||||
|
||||
@@ -30,6 +30,8 @@ const { fnParams, returnParamIndex, fnBodySource } = extractInlinedFunction(
|
||||
*
|
||||
* The function is inlined into all call sites in `FILES` list above.
|
||||
*
|
||||
* `TOKEN_SIZE32_SHIFT` constant must be in scope at all call sites.
|
||||
*
|
||||
* ```ts
|
||||
* // Original code
|
||||
* const index = firstTokenAtOrAfter(int32, rangeStart, searchFromIndex, length);
|
||||
@@ -38,7 +40,7 @@ const { fnParams, returnParamIndex, fnBodySource } = extractInlinedFunction(
|
||||
* let index = searchFromIndex;
|
||||
* for (let endIndex = length; index < endIndex; ) {
|
||||
* const mid = (index + endIndex) >> 1;
|
||||
* if (int32[mid << 2] < rangeStart) {
|
||||
* if (int32[mid << TOKEN_SIZE32_SHIFT] < rangeStart) {
|
||||
* index = mid + 1;
|
||||
* } else {
|
||||
* endIndex = mid;
|
||||
|
||||
Reference in new issue
Block a user