Skip to content

Commit a95e2f9

Browse files
authored
Rollup merge of #122247 - notriddle:notriddle/search-unbox-limit, r=GuillaumeGomez
rustdoc-search: depth limit `T<U>` -> `U` unboxing Profiler output: https://notriddle.com/rustdoc-html-demo-9/search-unbox-limit/ (the only significant change is that one of the `rust` tests went from 378416ms to 16ms). This is a performance enhancement aimed at a problem I found while using type-driven search on the Rust compiler. It is caused by [`Interner`], a trait with 41 associated types, many of which recurse back to `Self` again. This caused search.js to struggle. It eventually terminates, after about 10 minutes of turning my PC into a space header, but it's doing `41!` unifications and that's too slow. [`Interner`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/trait.Interner.html
2 parents 7997ef4 + fa5b9f0 commit a95e2f9

File tree

4 files changed

+366
-31
lines changed

4 files changed

+366
-31
lines changed

src/librustdoc/html/static/js/search.js

+107-31
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,13 @@ const longItemTypes = [
8181
const TY_GENERIC = itemTypes.indexOf("generic");
8282
const ROOT_PATH = typeof window !== "undefined" ? window.rootPath : "../";
8383

84+
// Hard limit on how deep to recurse into generics when doing type-driven search.
85+
// This needs limited, partially because
86+
// a search for `Ty` shouldn't match `WithInfcx<ParamEnvAnd<Vec<ConstTy<Interner<Ty=Ty>>>>>`,
87+
// but mostly because this is the simplest and most principled way to limit the number
88+
// of permutations we need to check.
89+
const UNBOXING_LIMIT = 5;
90+
8491
// In the search display, allows to switch between tabs.
8592
function printTab(nb) {
8693
let iter = 0;
@@ -1458,10 +1465,23 @@ function initSearch(rawSearchIndex) {
14581465
* @param {Map<number,number>|null} mgensIn
14591466
* - Map functions generics to query generics (never modified).
14601467
* @param {null|Map<number,number> -> bool} solutionCb - Called for each `mgens` solution.
1468+
* @param {number} unboxingDepth
1469+
* - Limit checks that Ty matches Vec<Ty>,
1470+
* but not Vec<ParamEnvAnd<WithInfcx<ConstTy<Interner<Ty=Ty>>>>>
14611471
*
14621472
* @return {boolean} - Returns true if a match, false otherwise.
14631473
*/
1464-
function unifyFunctionTypes(fnTypesIn, queryElems, whereClause, mgensIn, solutionCb) {
1474+
function unifyFunctionTypes(
1475+
fnTypesIn,
1476+
queryElems,
1477+
whereClause,
1478+
mgensIn,
1479+
solutionCb,
1480+
unboxingDepth
1481+
) {
1482+
if (unboxingDepth >= UNBOXING_LIMIT) {
1483+
return false;
1484+
}
14651485
/**
14661486
* @type Map<integer, integer>|null
14671487
*/
@@ -1480,7 +1500,7 @@ function initSearch(rawSearchIndex) {
14801500
&& queryElems[0].bindings.size === 0) {
14811501
const queryElem = queryElems[0];
14821502
for (const fnType of fnTypesIn) {
1483-
if (!unifyFunctionTypeIsMatchCandidate(fnType, queryElem, whereClause, mgens)) {
1503+
if (!unifyFunctionTypeIsMatchCandidate(fnType, queryElem, mgens)) {
14841504
continue;
14851505
}
14861506
if (fnType.id < 0 && queryElem.id < 0) {
@@ -1499,7 +1519,13 @@ function initSearch(rawSearchIndex) {
14991519
}
15001520
}
15011521
for (const fnType of fnTypesIn) {
1502-
if (!unifyFunctionTypeIsUnboxCandidate(fnType, queryElem, whereClause, mgens)) {
1522+
if (!unifyFunctionTypeIsUnboxCandidate(
1523+
fnType,
1524+
queryElem,
1525+
whereClause,
1526+
mgens,
1527+
unboxingDepth + 1
1528+
)) {
15031529
continue;
15041530
}
15051531
if (fnType.id < 0) {
@@ -1514,7 +1540,8 @@ function initSearch(rawSearchIndex) {
15141540
queryElems,
15151541
whereClause,
15161542
mgensScratch,
1517-
solutionCb
1543+
solutionCb,
1544+
unboxingDepth + 1
15181545
)) {
15191546
return true;
15201547
}
@@ -1523,7 +1550,8 @@ function initSearch(rawSearchIndex) {
15231550
queryElems,
15241551
whereClause,
15251552
mgens ? new Map(mgens) : null,
1526-
solutionCb
1553+
solutionCb,
1554+
unboxingDepth + 1
15271555
)) {
15281556
return true;
15291557
}
@@ -1559,7 +1587,7 @@ function initSearch(rawSearchIndex) {
15591587
let queryElemsTmp = null;
15601588
for (let i = flast; i >= 0; i -= 1) {
15611589
const fnType = fnTypes[i];
1562-
if (!unifyFunctionTypeIsMatchCandidate(fnType, queryElem, whereClause, mgens)) {
1590+
if (!unifyFunctionTypeIsMatchCandidate(fnType, queryElem, mgens)) {
15631591
continue;
15641592
}
15651593
let mgensScratch;
@@ -1596,7 +1624,8 @@ function initSearch(rawSearchIndex) {
15961624
fnType,
15971625
queryElem,
15981626
whereClause,
1599-
mgensScratch
1627+
mgensScratch,
1628+
unboxingDepth
16001629
);
16011630
if (!solution) {
16021631
return false;
@@ -1608,14 +1637,16 @@ function initSearch(rawSearchIndex) {
16081637
queryElem.generics,
16091638
whereClause,
16101639
simplifiedMgens,
1611-
solutionCb
1640+
solutionCb,
1641+
unboxingDepth
16121642
);
16131643
if (passesUnification) {
16141644
return true;
16151645
}
16161646
}
16171647
return false;
1618-
}
1648+
},
1649+
unboxingDepth
16191650
);
16201651
if (passesUnification) {
16211652
return true;
@@ -1627,7 +1658,13 @@ function initSearch(rawSearchIndex) {
16271658
}
16281659
for (let i = flast; i >= 0; i -= 1) {
16291660
const fnType = fnTypes[i];
1630-
if (!unifyFunctionTypeIsUnboxCandidate(fnType, queryElem, whereClause, mgens)) {
1661+
if (!unifyFunctionTypeIsUnboxCandidate(
1662+
fnType,
1663+
queryElem,
1664+
whereClause,
1665+
mgens,
1666+
unboxingDepth + 1
1667+
)) {
16311668
continue;
16321669
}
16331670
let mgensScratch;
@@ -1651,7 +1688,8 @@ function initSearch(rawSearchIndex) {
16511688
queryElems,
16521689
whereClause,
16531690
mgensScratch,
1654-
solutionCb
1691+
solutionCb,
1692+
unboxingDepth + 1
16551693
);
16561694
if (passesUnification) {
16571695
return true;
@@ -1670,11 +1708,10 @@ function initSearch(rawSearchIndex) {
16701708
*
16711709
* @param {FunctionType} fnType
16721710
* @param {QueryElement} queryElem
1673-
* @param {[FunctionSearchType]} whereClause - Trait bounds for generic items.
16741711
* @param {Map<number,number>|null} mgensIn - Map functions generics to query generics.
16751712
* @returns {boolean}
16761713
*/
1677-
function unifyFunctionTypeIsMatchCandidate(fnType, queryElem, whereClause, mgensIn) {
1714+
function unifyFunctionTypeIsMatchCandidate(fnType, queryElem, mgensIn) {
16781715
// type filters look like `trait:Read` or `enum:Result`
16791716
if (!typePassesFilter(queryElem.typeFilter, fnType.ty)) {
16801717
return false;
@@ -1775,9 +1812,16 @@ function initSearch(rawSearchIndex) {
17751812
* @param {[FunctionType]} whereClause - Trait bounds for generic items.
17761813
* @param {Map<number,number>} mgensIn - Map functions generics to query generics.
17771814
* Never modified.
1815+
* @param {number} unboxingDepth
17781816
* @returns {false|{mgens: [Map<number,number>], simplifiedGenerics: [FunctionType]}}
17791817
*/
1780-
function unifyFunctionTypeCheckBindings(fnType, queryElem, whereClause, mgensIn) {
1818+
function unifyFunctionTypeCheckBindings(
1819+
fnType,
1820+
queryElem,
1821+
whereClause,
1822+
mgensIn,
1823+
unboxingDepth
1824+
) {
17811825
if (fnType.bindings.size < queryElem.bindings.size) {
17821826
return false;
17831827
}
@@ -1804,7 +1848,8 @@ function initSearch(rawSearchIndex) {
18041848
// return `false` makes unifyFunctionTypes return the full set of
18051849
// possible solutions
18061850
return false;
1807-
}
1851+
},
1852+
unboxingDepth
18081853
);
18091854
return newSolutions;
18101855
});
@@ -1834,9 +1879,19 @@ function initSearch(rawSearchIndex) {
18341879
* @param {QueryElement} queryElem
18351880
* @param {[FunctionType]} whereClause - Trait bounds for generic items.
18361881
* @param {Map<number,number>|null} mgens - Map functions generics to query generics.
1882+
* @param {number} unboxingDepth
18371883
* @returns {boolean}
18381884
*/
1839-
function unifyFunctionTypeIsUnboxCandidate(fnType, queryElem, whereClause, mgens) {
1885+
function unifyFunctionTypeIsUnboxCandidate(
1886+
fnType,
1887+
queryElem,
1888+
whereClause,
1889+
mgens,
1890+
unboxingDepth
1891+
) {
1892+
if (unboxingDepth >= UNBOXING_LIMIT) {
1893+
return false;
1894+
}
18401895
if (fnType.id < 0 && queryElem.id >= 0) {
18411896
if (!whereClause) {
18421897
return false;
@@ -1858,14 +1913,21 @@ function initSearch(rawSearchIndex) {
18581913
whereClause[(-fnType.id) - 1],
18591914
queryElem,
18601915
whereClause,
1861-
mgensTmp
1916+
mgensTmp,
1917+
unboxingDepth
18621918
);
18631919
} else if (fnType.generics.length > 0 || fnType.bindings.size > 0) {
18641920
const simplifiedGenerics = [
18651921
...fnType.generics,
18661922
...Array.from(fnType.bindings.values()).flat(),
18671923
];
1868-
return checkIfInList(simplifiedGenerics, queryElem, whereClause, mgens);
1924+
return checkIfInList(
1925+
simplifiedGenerics,
1926+
queryElem,
1927+
whereClause,
1928+
mgens,
1929+
unboxingDepth
1930+
);
18691931
}
18701932
return false;
18711933
}
@@ -1877,13 +1939,14 @@ function initSearch(rawSearchIndex) {
18771939
* @param {Array<FunctionType>} list
18781940
* @param {QueryElement} elem - The element from the parsed query.
18791941
* @param {[FunctionType]} whereClause - Trait bounds for generic items.
1880-
* @param {Map<number,number>|null} mgens - Map functions generics to query generics.
1942+
* @param {Map<number,number>|null} mgens - Map functions generics to query generics.
1943+
* @param {number} unboxingDepth
18811944
*
18821945
* @return {boolean} - Returns true if found, false otherwise.
18831946
*/
1884-
function checkIfInList(list, elem, whereClause, mgens) {
1947+
function checkIfInList(list, elem, whereClause, mgens, unboxingDepth) {
18851948
for (const entry of list) {
1886-
if (checkType(entry, elem, whereClause, mgens)) {
1949+
if (checkType(entry, elem, whereClause, mgens, unboxingDepth)) {
18871950
return true;
18881951
}
18891952
}
@@ -1897,14 +1960,23 @@ function initSearch(rawSearchIndex) {
18971960
* @param {Row} row
18981961
* @param {QueryElement} elem - The element from the parsed query.
18991962
* @param {[FunctionType]} whereClause - Trait bounds for generic items.
1900-
* @param {Map<number,number>|null} mgens - Map functions generics to query generics.
1963+
* @param {Map<number,number>|null} mgens - Map functions generics to query generics.
19011964
*
19021965
* @return {boolean} - Returns true if the type matches, false otherwise.
19031966
*/
1904-
function checkType(row, elem, whereClause, mgens) {
1967+
function checkType(row, elem, whereClause, mgens, unboxingDepth) {
1968+
if (unboxingDepth >= UNBOXING_LIMIT) {
1969+
return false;
1970+
}
19051971
if (row.bindings.size === 0 && elem.bindings.size === 0) {
1906-
if (elem.id < 0) {
1907-
return row.id < 0 || checkIfInList(row.generics, elem, whereClause, mgens);
1972+
if (elem.id < 0 && mgens === null) {
1973+
return row.id < 0 || checkIfInList(
1974+
row.generics,
1975+
elem,
1976+
whereClause,
1977+
mgens,
1978+
unboxingDepth + 1
1979+
);
19081980
}
19091981
if (row.id > 0 && elem.id > 0 && elem.pathWithoutLast.length === 0 &&
19101982
typePassesFilter(elem.typeFilter, row.ty) && elem.generics.length === 0 &&
@@ -1916,11 +1988,12 @@ function initSearch(rawSearchIndex) {
19161988
row.generics,
19171989
elem,
19181990
whereClause,
1919-
mgens
1991+
mgens,
1992+
unboxingDepth
19201993
);
19211994
}
19221995
}
1923-
return unifyFunctionTypes([row], [elem], whereClause, mgens);
1996+
return unifyFunctionTypes([row], [elem], whereClause, mgens, null, unboxingDepth);
19241997
}
19251998

19261999
/**
@@ -2135,9 +2208,9 @@ function initSearch(rawSearchIndex) {
21352208
);
21362209
if (tfpDist !== null) {
21372210
const in_args = row.type && row.type.inputs
2138-
&& checkIfInList(row.type.inputs, elem, row.type.where_clause);
2211+
&& checkIfInList(row.type.inputs, elem, row.type.where_clause, null, 0);
21392212
const returned = row.type && row.type.output
2140-
&& checkIfInList(row.type.output, elem, row.type.where_clause);
2213+
&& checkIfInList(row.type.output, elem, row.type.where_clause, null, 0);
21412214
if (in_args) {
21422215
results_in_args.max_dist = Math.max(results_in_args.max_dist || 0, tfpDist);
21432216
const maxDist = results_in_args.size < MAX_RESULTS ?
@@ -2223,9 +2296,12 @@ function initSearch(rawSearchIndex) {
22232296
row.type.output,
22242297
parsedQuery.returned,
22252298
row.type.where_clause,
2226-
mgens
2299+
mgens,
2300+
null,
2301+
0 // unboxing depth
22272302
);
2228-
}
2303+
},
2304+
0 // unboxing depth
22292305
)) {
22302306
return;
22312307
}

0 commit comments

Comments
 (0)