https://bugs.kde.org/show_bug.cgi?id=523813
--- Comment #2 from [email protected] --- (In reply to Zamundaaa from comment #1) > > the root cause is when the work runs, not how fast > If it takes 48ms, it doesn't matter when it happens, it will cause stutter. > > From a quick search, it looks like munmap can actually block mmap calls... > so even if we moved this work to another thread, it could still cause > problems. I'm not sure there's anything we can really do to improve this. Fair point, but I measured the mmap_lock part before giving up on it, because it changes what the fix would look like. All the numbers below come from a small test program that copies what ShmPool does: a memfd at 2880x1800x4 (19.8 MiB), with 4 KiB folios like real wl_shm clients. Kernel 7.1.7, plugged in. On battery it's all slower, so these are best cases. The part that surprised me is the order in the destructor. ShmPool declares `mapping` before `fd`, so ~ShmPool closes the fd first. That close costs almost nothing (0.5 us), because the mapping is still keeping the inode alive. So munmap ends up doing all the work of freeing the folios: 833 us, all of it under mmap_lock. If you swap the two: now (close then munmap): close 0.5 us munmap 833 us swapped (munmap then close): munmap 199 us close 616 us Same total work, but only about a fifth of it under the lock. If I'm reading the destructor right, that's a one line change. On munmap blocking mmap: it does, but I couldn't make it hurt much. I ran a thread doing mmap, touch, munmap about 200k times a second, which is far more than a compositor would ever do, while pools were being freed constantly. It went from p50 2.2 us to 2.4 us, and p99 5.2 us to 6.6 us. Page faults weren't affected at all, which I think is per-VMA locking doing its job: MADV_DONTNEED plus refault was p99 1140 us when idle, and 749 us during teardown. The worst case is one mmap waiting for one munmap to finish, and I did catch one that took 936 us. After the swap above that should be closer to 200 us. That leaves the "48 ms is 48 ms" part. I don't think it's one 48 ms job. It's about 0.82 ms per pool, so it's dozens of small ones. If that's right, then just spreading them out is enough: put freed pools in a queue, and once a frame is committed, spend about a millisecond emptying the queue. The rest waits for the next frame. No extra thread, so the lock question never comes up. The only cost is that memory goes back a few hundred ms late. The part I'm least sure about is the pool count. 48 ms at this speed works out to roughly 1 GB of shm per burst, and that's me doing the maths, not measuring it. I can measure the real pools and bytes per stutter if that would help, and I can attach the test programs too. -- You are receiving this mail because: You are watching all bug changes.
