Skip to content

Commit 02279de

Browse files
committed
Sweep dead process slots only when slots are scarce
init_proc_slot_withlock() swept every occupied slot for liveness on every join. The sweep reads /proc/<pid>/stat once per slot and runs with the region lock held, so N processes starting together perform O(N^2) serialised filesystem work. On the harness from #252, init p50 grows from 0.35ms to 6.06ms going from 1 to 64 concurrent processes. Reclaiming a slot whose process already died is not needed for a join to be correct: oom_check() sweeps before it reports OOM, which is where a stale slot actually changes an outcome. Sweep on join only once occupancy reaches three quarters of the table, and do it before the capacity check so a table filled with dead slots is recovered instead of being fatal. Slots that exit cleanup already marked with PID 0 are still compacted on every join; that path reads no files. With this change init p50 at 64 concurrent processes is 1.11ms. Signed-off-by: keshav9926 <kkakani160@gmail.com>
1 parent de6ce39 commit 02279de

2 files changed

Lines changed: 23 additions & 1 deletion

File tree

src/multiprocess/multiprocess_memory_limit.c

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1072,6 +1072,20 @@ void init_proc_slot_withlock() {
10721072
shared_region_t* region = region_info.shared_region;
10731073

10741074
int proc_num = atomic_load_explicit(&region->proc_num, memory_order_acquire);
1075+
1076+
// A full sweep reads /proc/<pid>/stat once per occupied slot, so it costs
1077+
// O(proc_num) filesystem syscalls while the region lock is held. Running it
1078+
// on every join makes N processes starting together O(N^2) serialised work.
1079+
// Reclaiming a slot whose process already died is not needed for the join
1080+
// itself to be correct: oom_check() sweeps before it reports OOM, which is
1081+
// where a stale slot actually changes an outcome. So sweep here only when
1082+
// slots are scarce -- and do it before deciding the table is full, so a
1083+
// table filled with dead slots is recovered instead of being fatal.
1084+
if (proc_num >= SHARED_REGION_SWEEP_THRESHOLD) {
1085+
clear_proc_slot_nolock(1);
1086+
proc_num = atomic_load_explicit(&region->proc_num, memory_order_acquire);
1087+
}
1088+
10751089
if (proc_num >= SHARED_REGION_MAX_PROCESS_NUM) {
10761090
exit_withlock(-1);
10771091
}
@@ -1123,7 +1137,9 @@ void init_proc_slot_withlock() {
11231137
atomic_fetch_add_explicit(&region->proc_num, 1, memory_order_release);
11241138
}
11251139

1126-
clear_proc_slot_nolock(1);
1140+
// Slots that exit cleanup already marked dead carry PID 0, and dropping
1141+
// those reads no files at all, so that part stays on the join path.
1142+
clear_proc_slot_nolock(0);
11271143
unlock_shrreg();
11281144
}
11291145

src/multiprocess/multiprocess_memory_limit.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,12 @@
4040

4141
#define SHARED_REGION_SIZE_MAGIC sizeof(shared_region_t)
4242
#define SHARED_REGION_MAX_PROCESS_NUM 1024
43+
// Slot-table occupancy at which joining a process performs a full liveness
44+
// sweep. See init_proc_slot_withlock(). Overridable at build time so the
45+
// regression test can reach the sweep without spawning 768 processes.
46+
#ifndef SHARED_REGION_SWEEP_THRESHOLD
47+
#define SHARED_REGION_SWEEP_THRESHOLD ((SHARED_REGION_MAX_PROCESS_NUM * 3) / 4)
48+
#endif
4349

4450
// macros for debugging
4551
#define SEQ_FIX_SHRREG_ACQUIRE_FLOCK_OK 0

0 commit comments

Comments
 (0)