Rollup merge of #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>
This commit is contained in:
commit
f9cadb915f
@ -2717,9 +2717,33 @@ ${item.displayPath}<span class="${type}">${name}</span>\
|
|||||||
* @return {Array<FunctionSearchType>}
|
* @return {Array<FunctionSearchType>}
|
||||||
*/
|
*/
|
||||||
function buildItemSearchTypeAll(types, lowercasePaths) {
|
function buildItemSearchTypeAll(types, lowercasePaths) {
|
||||||
return types.map(type => buildItemSearchType(type, lowercasePaths));
|
return types.length > 0 ?
|
||||||
|
types.map(type => buildItemSearchType(type, lowercasePaths)) :
|
||||||
|
EMPTY_GENERICS_ARRAY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Empty, immutable map used in item search types with no bindings.
|
||||||
|
*
|
||||||
|
* @type {Map<number, Array<FunctionType>>}
|
||||||
|
*/
|
||||||
|
const EMPTY_BINDINGS_MAP = new Map();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Empty, immutable map used in item search types with no bindings.
|
||||||
|
*
|
||||||
|
* @type {Array<FunctionType>}
|
||||||
|
*/
|
||||||
|
const EMPTY_GENERICS_ARRAY = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Object pool for function types with no bindings or generics.
|
||||||
|
* This is reset after loading the index.
|
||||||
|
*
|
||||||
|
* @type {Map<number|null, FunctionType>}
|
||||||
|
*/
|
||||||
|
let TYPES_POOL = new Map();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts a single type.
|
* Converts a single type.
|
||||||
*
|
*
|
||||||
@ -2732,15 +2756,15 @@ ${item.displayPath}<span class="${type}">${name}</span>\
|
|||||||
let pathIndex, generics, bindings;
|
let pathIndex, generics, bindings;
|
||||||
if (typeof type === "number") {
|
if (typeof type === "number") {
|
||||||
pathIndex = type;
|
pathIndex = type;
|
||||||
generics = [];
|
generics = EMPTY_GENERICS_ARRAY;
|
||||||
bindings = new Map();
|
bindings = EMPTY_BINDINGS_MAP;
|
||||||
} else {
|
} else {
|
||||||
pathIndex = type[PATH_INDEX_DATA];
|
pathIndex = type[PATH_INDEX_DATA];
|
||||||
generics = buildItemSearchTypeAll(
|
generics = buildItemSearchTypeAll(
|
||||||
type[GENERICS_DATA],
|
type[GENERICS_DATA],
|
||||||
lowercasePaths
|
lowercasePaths
|
||||||
);
|
);
|
||||||
if (type.length > BINDINGS_DATA) {
|
if (type.length > BINDINGS_DATA && type[BINDINGS_DATA].length > 0) {
|
||||||
bindings = new Map(type[BINDINGS_DATA].map(binding => {
|
bindings = new Map(type[BINDINGS_DATA].map(binding => {
|
||||||
const [assocType, constraints] = binding;
|
const [assocType, constraints] = binding;
|
||||||
// Associated type constructors are represented sloppily in rustdoc's
|
// Associated type constructors are represented sloppily in rustdoc's
|
||||||
@ -2759,38 +2783,83 @@ ${item.displayPath}<span class="${type}">${name}</span>\
|
|||||||
];
|
];
|
||||||
}));
|
}));
|
||||||
} else {
|
} else {
|
||||||
bindings = new Map();
|
bindings = EMPTY_BINDINGS_MAP;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* @type {FunctionType}
|
||||||
|
*/
|
||||||
|
let result;
|
||||||
if (pathIndex < 0) {
|
if (pathIndex < 0) {
|
||||||
// types less than 0 are generic parameters
|
// types less than 0 are generic parameters
|
||||||
// the actual names of generic parameters aren't stored, since they aren't API
|
// the actual names of generic parameters aren't stored, since they aren't API
|
||||||
return {
|
result = {
|
||||||
id: pathIndex,
|
id: pathIndex,
|
||||||
ty: TY_GENERIC,
|
ty: TY_GENERIC,
|
||||||
path: null,
|
path: null,
|
||||||
generics,
|
generics,
|
||||||
bindings,
|
bindings,
|
||||||
};
|
};
|
||||||
}
|
} else if (pathIndex === 0) {
|
||||||
if (pathIndex === 0) {
|
|
||||||
// `0` is used as a sentinel because it's fewer bytes than `null`
|
// `0` is used as a sentinel because it's fewer bytes than `null`
|
||||||
return {
|
result = {
|
||||||
id: null,
|
id: null,
|
||||||
ty: null,
|
ty: null,
|
||||||
path: null,
|
path: null,
|
||||||
generics,
|
generics,
|
||||||
bindings,
|
bindings,
|
||||||
};
|
};
|
||||||
|
} else {
|
||||||
|
const item = lowercasePaths[pathIndex - 1];
|
||||||
|
result = {
|
||||||
|
id: buildTypeMapIndex(item.name, isAssocType),
|
||||||
|
ty: item.ty,
|
||||||
|
path: item.path,
|
||||||
|
generics,
|
||||||
|
bindings,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
const item = lowercasePaths[pathIndex - 1];
|
const cr = TYPES_POOL.get(result.id);
|
||||||
return {
|
if (cr) {
|
||||||
id: buildTypeMapIndex(item.name, isAssocType),
|
// Shallow equality check. Since this function is used
|
||||||
ty: item.ty,
|
// to construct every type object, this should be mostly
|
||||||
path: item.path,
|
// equivalent to a deep equality check, except if there's
|
||||||
generics,
|
// a conflict, we don't keep the old one around, so it's
|
||||||
bindings,
|
// not a fully precise implementation of hashcons.
|
||||||
};
|
if (cr.generics.length === result.generics.length &&
|
||||||
|
cr.generics !== result.generics &&
|
||||||
|
cr.generics.every((x, i) => result.generics[i] === x)
|
||||||
|
) {
|
||||||
|
result.generics = cr.generics;
|
||||||
|
}
|
||||||
|
if (cr.bindings.size === result.bindings.size && cr.bindings !== result.bindings) {
|
||||||
|
let ok = true;
|
||||||
|
for (const [k, v] of cr.bindings.entries()) {
|
||||||
|
const v2 = result.bindings.get(v);
|
||||||
|
if (!v2) {
|
||||||
|
ok = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (v !== v2 && v.length === v2.length && v.every((x, i) => v2[i] === x)) {
|
||||||
|
result.bindings.set(k, v);
|
||||||
|
} else if (v !== v2) {
|
||||||
|
ok = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ok) {
|
||||||
|
result.bindings = cr.bindings;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (cr.ty === result.ty && cr.path === result.path
|
||||||
|
&& cr.bindings === result.bindings && cr.generics === result.generics
|
||||||
|
&& cr.ty === result.ty
|
||||||
|
) {
|
||||||
|
return cr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TYPES_POOL.set(result.id, result);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -2801,7 +2870,7 @@ ${item.displayPath}<span class="${type}">${name}</span>\
|
|||||||
* object-based encoding so that the actual search code is more readable and easier to debug.
|
* object-based encoding so that the actual search code is more readable and easier to debug.
|
||||||
*
|
*
|
||||||
* The raw function search type format is generated using serde in
|
* The raw function search type format is generated using serde in
|
||||||
* librustdoc/html/render/mod.rs: impl Serialize for IndexItemFunctionType
|
* librustdoc/html/render/mod.rs: IndexItemFunctionType::write_to_string
|
||||||
*
|
*
|
||||||
* @param {{
|
* @param {{
|
||||||
* string: string,
|
* string: string,
|
||||||
@ -2970,8 +3039,8 @@ ${item.displayPath}<span class="${type}">${name}</span>\
|
|||||||
const fb = {
|
const fb = {
|
||||||
id: null,
|
id: null,
|
||||||
ty: 0,
|
ty: 0,
|
||||||
generics: [],
|
generics: EMPTY_GENERICS_ARRAY,
|
||||||
bindings: new Map(),
|
bindings: EMPTY_BINDINGS_MAP,
|
||||||
};
|
};
|
||||||
for (const [k, v] of type.bindings.entries()) {
|
for (const [k, v] of type.bindings.entries()) {
|
||||||
fb.id = k;
|
fb.id = k;
|
||||||
@ -3199,6 +3268,8 @@ ${item.displayPath}<span class="${type}">${name}</span>\
|
|||||||
}
|
}
|
||||||
currentIndex += itemTypes.length;
|
currentIndex += itemTypes.length;
|
||||||
}
|
}
|
||||||
|
// Drop the (rather large) hash table used for reusing function items
|
||||||
|
TYPES_POOL = new Map();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Loading…
Reference in New Issue
Block a user