Skip to content

Commit f9cadb9

Browse files
Rollup merge of rust-lang#119756 - notriddle:notriddle/reuse-map, r=GuillaumeGomez
rustdoc-search: reuse individual types in function signatures Because `search.js` never mutates the function signature after loading it, they can be safely and easily reused across functions. This change doesn't change the format of the search index. It only changes `search.js`. Profiler output: https://notriddle.com/rustdoc-html-demo-9/fn-signature-opti2/index.html <table><tr><th>benchmark<th>before<th>after <tr><th>arti<td> ``` user: 002.228 s sys: 000.315 s wall: 001.663 s child_RSS_high: 315668 KiB group_mem_high: 285948 KiB ``` <td> ``` user: 001.805 s sys: 000.231 s wall: 001.398 s child_RSS_high: 235864 KiB group_mem_high: 203056 KiB ``` <tr><th>cortex-m<td> ``` user: 000.143 s sys: 000.035 s wall: 000.140 s child_RSS_high: 59168 KiB group_mem_high: 23000 KiB ``` <td> ``` user: 000.138 s sys: 000.031 s wall: 000.133 s child_RSS_high: 58944 KiB group_mem_high: 22220 KiB ``` <tr><th>sqlx<td> ``` user: 000.792 s sys: 000.115 s wall: 000.536 s child_RSS_high: 156716 KiB group_mem_high: 122948 KiB ``` <td> ``` user: 000.824 s sys: 000.084 s wall: 000.535 s child_RSS_high: 136668 KiB group_mem_high: 101792 KiB ``` <tr><th>stm32f4<td> ``` user: 006.665 s sys: 003.533 s wall: 008.624 s child_RSS_high: 1037660 KiB group_mem_high: 1022516 KiB ``` <td> ``` user: 005.997 s sys: 003.185 s wall: 007.987 s child_RSS_high: 832068 KiB group_mem_high: 810908 KiB ``` <tr><th>stm32f4xx-hal<td> ``` user: 000.317 s sys: 000.051 s wall: 000.203 s child_RSS_high: 77060 KiB group_mem_high: 41776 KiB ``` <td> ``` user: 000.287 s sys: 000.046 s wall: 000.180 s child_RSS_high: 75216 KiB group_mem_high: 39200 KiB ``` <tr><th>ripgrep<td> ``` user: 000.463 s sys: 000.063 s wall: 000.295 s child_RSS_high: 101288 KiB group_mem_high: 66364 KiB ``` <td> ``` user: 000.472 s sys: 000.036 s wall: 000.247 s child_RSS_high: 82708 KiB group_mem_high: 47056 KiB ``` </tr></table>
2 parents b0aa3d8 + c3cd657 commit f9cadb9

File tree

1 file changed

+91
-20
lines changed

1 file changed

+91
-20
lines changed

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

+91-20
Original file line numberDiff line numberDiff line change
@@ -2717,9 +2717,33 @@ ${item.displayPath}<span class="${type}">${name}</span>\
27172717
* @return {Array<FunctionSearchType>}
27182718
*/
27192719
function buildItemSearchTypeAll(types, lowercasePaths) {
2720-
return types.map(type => buildItemSearchType(type, lowercasePaths));
2720+
return types.length > 0 ?
2721+
types.map(type => buildItemSearchType(type, lowercasePaths)) :
2722+
EMPTY_GENERICS_ARRAY;
27212723
}
27222724

2725+
/**
2726+
* Empty, immutable map used in item search types with no bindings.
2727+
*
2728+
* @type {Map<number, Array<FunctionType>>}
2729+
*/
2730+
const EMPTY_BINDINGS_MAP = new Map();
2731+
2732+
/**
2733+
* Empty, immutable map used in item search types with no bindings.
2734+
*
2735+
* @type {Array<FunctionType>}
2736+
*/
2737+
const EMPTY_GENERICS_ARRAY = [];
2738+
2739+
/**
2740+
* Object pool for function types with no bindings or generics.
2741+
* This is reset after loading the index.
2742+
*
2743+
* @type {Map<number|null, FunctionType>}
2744+
*/
2745+
let TYPES_POOL = new Map();
2746+
27232747
/**
27242748
* Converts a single type.
27252749
*
@@ -2732,15 +2756,15 @@ ${item.displayPath}<span class="${type}">${name}</span>\
27322756
let pathIndex, generics, bindings;
27332757
if (typeof type === "number") {
27342758
pathIndex = type;
2735-
generics = [];
2736-
bindings = new Map();
2759+
generics = EMPTY_GENERICS_ARRAY;
2760+
bindings = EMPTY_BINDINGS_MAP;
27372761
} else {
27382762
pathIndex = type[PATH_INDEX_DATA];
27392763
generics = buildItemSearchTypeAll(
27402764
type[GENERICS_DATA],
27412765
lowercasePaths
27422766
);
2743-
if (type.length > BINDINGS_DATA) {
2767+
if (type.length > BINDINGS_DATA && type[BINDINGS_DATA].length > 0) {
27442768
bindings = new Map(type[BINDINGS_DATA].map(binding => {
27452769
const [assocType, constraints] = binding;
27462770
// Associated type constructors are represented sloppily in rustdoc's
@@ -2759,38 +2783,83 @@ ${item.displayPath}<span class="${type}">${name}</span>\
27592783
];
27602784
}));
27612785
} else {
2762-
bindings = new Map();
2786+
bindings = EMPTY_BINDINGS_MAP;
27632787
}
27642788
}
2789+
/**
2790+
* @type {FunctionType}
2791+
*/
2792+
let result;
27652793
if (pathIndex < 0) {
27662794
// types less than 0 are generic parameters
27672795
// the actual names of generic parameters aren't stored, since they aren't API
2768-
return {
2796+
result = {
27692797
id: pathIndex,
27702798
ty: TY_GENERIC,
27712799
path: null,
27722800
generics,
27732801
bindings,
27742802
};
2775-
}
2776-
if (pathIndex === 0) {
2803+
} else if (pathIndex === 0) {
27772804
// `0` is used as a sentinel because it's fewer bytes than `null`
2778-
return {
2805+
result = {
27792806
id: null,
27802807
ty: null,
27812808
path: null,
27822809
generics,
27832810
bindings,
27842811
};
2812+
} else {
2813+
const item = lowercasePaths[pathIndex - 1];
2814+
result = {
2815+
id: buildTypeMapIndex(item.name, isAssocType),
2816+
ty: item.ty,
2817+
path: item.path,
2818+
generics,
2819+
bindings,
2820+
};
27852821
}
2786-
const item = lowercasePaths[pathIndex - 1];
2787-
return {
2788-
id: buildTypeMapIndex(item.name, isAssocType),
2789-
ty: item.ty,
2790-
path: item.path,
2791-
generics,
2792-
bindings,
2793-
};
2822+
const cr = TYPES_POOL.get(result.id);
2823+
if (cr) {
2824+
// Shallow equality check. Since this function is used
2825+
// to construct every type object, this should be mostly
2826+
// equivalent to a deep equality check, except if there's
2827+
// a conflict, we don't keep the old one around, so it's
2828+
// not a fully precise implementation of hashcons.
2829+
if (cr.generics.length === result.generics.length &&
2830+
cr.generics !== result.generics &&
2831+
cr.generics.every((x, i) => result.generics[i] === x)
2832+
) {
2833+
result.generics = cr.generics;
2834+
}
2835+
if (cr.bindings.size === result.bindings.size && cr.bindings !== result.bindings) {
2836+
let ok = true;
2837+
for (const [k, v] of cr.bindings.entries()) {
2838+
const v2 = result.bindings.get(v);
2839+
if (!v2) {
2840+
ok = false;
2841+
break;
2842+
}
2843+
if (v !== v2 && v.length === v2.length && v.every((x, i) => v2[i] === x)) {
2844+
result.bindings.set(k, v);
2845+
} else if (v !== v2) {
2846+
ok = false;
2847+
break;
2848+
}
2849+
}
2850+
if (ok) {
2851+
result.bindings = cr.bindings;
2852+
}
2853+
}
2854+
if (cr.ty === result.ty && cr.path === result.path
2855+
&& cr.bindings === result.bindings && cr.generics === result.generics
2856+
&& cr.ty === result.ty
2857+
) {
2858+
return cr;
2859+
}
2860+
}
2861+
TYPES_POOL.set(result.id, result);
2862+
return result;
27942863
}
27952864

27962865
/**
@@ -2801,7 +2870,7 @@ ${item.displayPath}<span class="${type}">${name}</span>\
28012870
* object-based encoding so that the actual search code is more readable and easier to debug.
28022871
*
28032872
* The raw function search type format is generated using serde in
2804-
* librustdoc/html/render/mod.rs: impl Serialize for IndexItemFunctionType
2873+
* librustdoc/html/render/mod.rs: IndexItemFunctionType::write_to_string
28052874
*
28062875
* @param {{
28072876
* string: string,
@@ -2970,8 +3039,8 @@ ${item.displayPath}<span class="${type}">${name}</span>\
29703039
const fb = {
29713040
id: null,
29723041
ty: 0,
2973-
generics: [],
2974-
bindings: new Map(),
3042+
generics: EMPTY_GENERICS_ARRAY,
3043+
bindings: EMPTY_BINDINGS_MAP,
29753044
};
29763045
for (const [k, v] of type.bindings.entries()) {
29773046
fb.id = k;
@@ -3199,6 +3268,8 @@ ${item.displayPath}<span class="${type}">${name}</span>\
31993268
}
32003269
currentIndex += itemTypes.length;
32013270
}
3271+
// Drop the (rather large) hash table used for reusing function items
3272+
TYPES_POOL = new Map();
32023273
}
32033274

32043275
/**

0 commit comments

Comments
 (0)