core: clear dependency graph before freeing all units
When the manager tears down every unit during reload or shutdown, unit_free() removes each dependency from both endpoints. This work is redundant because every dependency map is about to disappear, and it scales poorly with thousands of synthesized mount units. Clear all unit dependency maps in one manager-wide pass before invoking the existing unit_free() cleanup. With 10,000 bind mounts, this reduced the mean daemon-reload time from 63.558s to 14.708s.
This commit is contained in:
@@ -1670,11 +1670,36 @@ static unsigned manager_dispatch_stop_notify_queue(Manager *m) {
|
||||
return n;
|
||||
}
|
||||
|
||||
static void manager_clear_unit_dependencies(Manager *m) {
|
||||
Unit *u;
|
||||
const char *name;
|
||||
|
||||
assert(m);
|
||||
|
||||
HASHMAP_FOREACH_KEY(u, name, m->units) {
|
||||
|
||||
/* ignore aliases */
|
||||
if (u->id != name)
|
||||
continue;
|
||||
|
||||
for (Hashmap *dependencies; (dependencies = hashmap_steal_first(u->dependencies));)
|
||||
hashmap_free(dependencies);
|
||||
|
||||
u->dependencies = hashmap_free(u->dependencies);
|
||||
u->dependency_generation++;
|
||||
}
|
||||
}
|
||||
|
||||
static void manager_clear_jobs_and_units(Manager *m) {
|
||||
Unit *u;
|
||||
|
||||
assert(m);
|
||||
|
||||
/* All units are going away, hence discard the full dependency graph in one pass. Otherwise
|
||||
* unit_free() removes every edge from both endpoints separately, which becomes very costly with
|
||||
* large numbers of synthesized mount units. */
|
||||
manager_clear_unit_dependencies(m);
|
||||
|
||||
while ((u = hashmap_first(m->units)))
|
||||
unit_free(u);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user