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.

Reply via email to