Skip to content

Commit

Permalink
rustdoc-search: intern function search types
Browse files Browse the repository at this point in the history
This takes advantage of more reuse opportunities.
Along with the empty object commit, they
bringing memory usage down about 20% over the original.

stm32f4 before

```
user: 006.577 s
sys:  003.464 s
wall: 008.471 s
child_RSS_high:    1038760 KiB
group_mem_high:    1024416 KiB
```

After

```
user: 005.654 s
sys:  003.249 s
wall: 007.855 s
child_RSS_high:     830624 KiB
group_mem_high:     809264 KiB
```

(830624-1038760)/1038760 = -20.03%
  • Loading branch information
notriddle committed Jan 8, 2024
1 parent e61be1b commit 1600cc9
Showing 1 changed file with 72 additions and 17 deletions.
89 changes: 72 additions & 17 deletions src/librustdoc/html/static/js/search.js
Original file line number Diff line number Diff line change
Expand Up @@ -2725,17 +2725,25 @@ ${item.displayPath}<span class="${type}">${name}</span>\
/**
* Empty, immutable map used in item search types with no bindings.
*
* @type {Map<integer, Array<Functiontype>>}
* @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>}
* @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.
*
Expand Down Expand Up @@ -2778,35 +2786,80 @@ ${item.displayPath}<span class="${type}">${name}</span>\
bindings = EMPTY_BINDINGS_MAP;
}
}
/**
* @type {FunctionType}
*/
let result;
if (pathIndex < 0) {
// types less than 0 are generic parameters
// the actual names of generic parameters aren't stored, since they aren't API
return {
result = {
id: pathIndex,
ty: TY_GENERIC,
path: null,
generics,
bindings,
};
}
if (pathIndex === 0) {
} else if (pathIndex === 0) {
// `0` is used as a sentinel because it's fewer bytes than `null`
return {
result = {
id: null,
ty: null,
path: null,
generics,
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];
return {
id: buildTypeMapIndex(item.name, isAssocType),
ty: item.ty,
path: item.path,
generics,
bindings,
};
const cr = TYPES_POOL.get(result.id);
if (cr) {
// Shallow equality check. Since this function is used
// to construct every type object, this should be mostly
// equivalent to a deep equality check, except if there's
// a conflict, we don't keep the old one around, so it's
// 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;
}

/**
Expand All @@ -2817,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.
*
* 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 {{
* string: string,
Expand Down Expand Up @@ -2986,8 +3039,8 @@ ${item.displayPath}<span class="${type}">${name}</span>\
const fb = {
id: null,
ty: 0,
generics: [],
bindings: new Map(),
generics: EMPTY_GENERICS_ARRAY,
bindings: EMPTY_BINDINGS_MAP,
};
for (const [k, v] of type.bindings.entries()) {
fb.id = k;
Expand Down Expand Up @@ -3215,6 +3268,8 @@ ${item.displayPath}<span class="${type}">${name}</span>\
}
currentIndex += itemTypes.length;
}
// Drop the (rather large) hash table used for reusing function items
TYPES_POOL = new Map();
}

/**
Expand Down

0 comments on commit 1600cc9

Please sign in to comment.