perf/hw_breakpoint: Optimize for thousands of tasks
Status from this message: The following commit has been merged into the perf/core branch of tip:
Get series with b4
b4 am [email protected]Versions newest first · linked by author and title
- v4 Latest14 patches 0 repliesApplied
- v314 patches 34 repliesSuperseded
- v213 patches 18 repliesSuperseded
Earlier versions · 1
| Version | Posted | Patches | Replies | Status |
|---|---|---|---|---|
| v1 | 8 patches | 25 replies | Superseded |
Changes in v4 from the cover letter
* Fix percpu_is_read_locked(): Due to spurious read_count increments in __percpu_down_read_trylock() if sem->block != 0, check that !sem->block (reported by Peter).
* Apply Reviewed/Acked-by.
Patches in v4 Applied
| # | Subject and files | Size | Replies | Applied |
|---|---|---|---|---|
| 1/14 | perf/hw_breakpoint: Add KUnit test for constraints accountingkernel/ kernel/ lib/ | +334 | 0 replies | |
| 2/14 | perf/hw_breakpoint: Provide hw_breakpoint_is_used() and use in testinclude/ kernel/ kernel/ | +43 | 0 replies | |
| 3/14 | perf/hw_breakpoint: Clean up headerskernel/ | +9 | 0 replies | |
| 4/14 | perf/hw_breakpoint: Optimize list of per-task breakpointsinclude/ kernel/ | +37 | 0 replies | |
| 5/14 | perf/hw_breakpoint: Mark data __ro_after_initkernel/ | +3 | 0 replies | |
| 6/14 | perf/hw_breakpoint: Optimize constant number of breakpoint slotsarch/ arch/ kernel/ | +63 | 0 replies | |
| 7/14 | perf/hw_breakpoint: Make hw_breakpoint_weight() inlinableinclude/ kernel/ | +3 | 0 replies | |
| 8/14 | perf/hw_breakpoint: Remove useless code related to flexible breakpointskernel/ | +17 | 0 replies | |
| 9/14 | powerpc/hw_breakpoint: Avoid relying on caller synchronizationarch/ | +40 | 0 replies | |
| 10/14 | locking/percpu-rwsem: Add percpu_is_write_locked() and percpu_is_read_locked()include/ kernel/ | +12 | 0 replies | |
| 11/14 | perf/hw_breakpoint: Reduce contention with large number of taskskernel/ | +133 | 0 replies | |
| 12/14 | perf/hw_breakpoint: Introduce bp_slots_histogramkernel/ | +63 | 0 replies | |
| 13/14 | perf/hw_breakpoint: Optimize max_bp_pinned_slots() for CPU-independent task targetskernel/ | +53 | 0 replies | |
| 14/14 | perf/hw_breakpoint: Optimize toggle_bp_slot() for CPU-independent task targetskernel/ | +124 | 0 replies |
Reviews trailers per patch
All 14 patches have a Reviewed-by or Acked-by.
| Patch | Dmitry Vyukov | Ian Rogers |
|---|---|---|
| 1/14 | R-b | A-b |
| 2/14 | R-b | A-b |
| 3/14 | A-b | A-b |
| 4/14 | R-b | A-b |
| 5/14 | R-b | A-b |
| 6/14 | A-b | A-b |
| 7/14 | R-b | A-b |
| 8/14 | R-b | A-b |
| 9/14 | A-b | A-b |
| 10/14 | R-b | A-b |
| 11/14 | R-b | A-b |
| 12/14 | R-b | A-b |
| 13/14 | R-b | A-b |
| 14/14 | R-b | A-b |
Trailers given in replies are filled; those carried in the patches from earlier versions are outlined with dashes.
Cover letter [PATCH v4 00/14] perf/hw_breakpoint: Optimize for thousands of tasks
The hw_breakpoint subsystem's code has seen little change in over 10 years. In that time, systems with >100s of CPUs have become common, along with improvements to the perf subsystem: using breakpoints on thousands of concurrent tasks should be a supported usecase.
The breakpoint constraints accounting algorithm is the major bottleneck in doing so:
1. toggle_bp_slot() and fetch_bp_busy_slots() are O(#cpus * #tasks):
Both iterate through all CPUs and call task_bp_pinned(), which is O(#tasks).
2. Everything is serialized on a global mutex, 'nr_bp_mutex'.
The series progresses with the simpler optimizations and finishes with the more complex optimizations:
1. We first optimize task_bp_pinned() to only take O(1) on average.
2. Rework synchronization to allow concurrency when checking and updating breakpoint constraints for tasks.
3. Eliminate the O(#cpus) loops in the CPU-independent case.
Along the way, smaller micro-optimizations and cleanups are done as they seemed obvious when staring at the code (but likely insignificant).
The result is (on a system with 256 CPUs) that we go from:
| $> perf bench -r 30 breakpoint thread -b 4 -p 64 -t 64
[ ^ more aggressive benchmark parameters took too long ]
| # Running 'breakpoint/thread' benchmark: | # Created/joined 30 threads with 4 breakpoints and 64 parallelism | Total time: 236.418 [sec] | | 123134.794271 usecs/op | 7880626.833333 usecs/op/cpu
... to the following with all optimizations:
| $> perf bench -r 30 breakpoint thread -b 4 -p 64 -t 64 | # Running 'breakpoint/thread' benchmark: | # Created/joined 30 threads with 4 breakpoints and 64 parallelism | Total time: 0.067 [sec] | | 35.292187 usecs/op | 2258.700000 usecs/op/cpu
On the used test system, that's an effective speedup of ~3490x per op.
Which is on par with the theoretical ideal performance through optimizations in hw_breakpoint.c (constraints accounting disabled), and only 12% slower than no breakpoints at all.
Changelog
---------
v4:
* Fix percpu_is_read_locked(): Due to spurious read_count increments in __percpu_down_read_trylock() if sem->block != 0, check that !sem->block (reported by Peter).
* Apply Reviewed/Acked-by.
v3: https://lkml.kernel.org/r/[email protected]
* Fix typos.
* Introduce hw_breakpoint_is_used() for the test.
* Add WARN_ON in bp_blots_histogram_add().
* Don't use raw_smp_processor_id() in test.
* Apply Acked-by/Reviewed-by given in v2 for mostly unchanged patches.
v2: https://lkml.kernel.org/r/[email protected]
* Add KUnit test suite.
* Remove struct bp_busy_slots and simplify functions.
* Add "powerpc/hw_breakpoint: Avoid relying on caller synchronization".
* Add "locking/percpu-rwsem: Add percpu_is_write_locked() and percpu_is_read_locked()".
* Use percpu-rwsem instead of rwlock.
* Use task_struct::perf_event_mutex instead of sharded mutex.
* Drop v1 "perf/hw_breakpoint: Optimize task_bp_pinned() if CPU-independent".
* Add "perf/hw_breakpoint: Introduce bp_slots_histogram".
* Add "perf/hw_breakpoint: Optimize max_bp_pinned_slots() for CPU-independent task targets".
* Add "perf/hw_breakpoint: Optimize toggle_bp_slot() for CPU-independent task targets".
* Apply Acked-by/Reviewed-by given in v1 for unchanged patches.
==> Speedup of ~3490x (vs. ~3315x in v1).
v1: https://lore.kernel.org/all/[email protected]/
Marco Elver (14):
perf/hw_breakpoint: Add KUnit test for constraints accounting perf/hw_breakpoint: Provide hw_breakpoint_is_used() and use in test perf/hw_breakpoint: Clean up headers
perf/hw_breakpoint: Optimize list of per-task breakpoints perf/hw_breakpoint: Mark data __ro_after_init perf/hw_breakpoint: Optimize constant number of breakpoint slots perf/hw_breakpoint: Make hw_breakpoint_weight() inlinable perf/hw_breakpoint: Remove useless code related to flexible
breakpoints
powerpc/hw_breakpoint: Avoid relying on caller synchronization locking/percpu-rwsem: Add percpu_is_write_locked() and
percpu_is_read_locked()
perf/hw_breakpoint: Reduce contention with large number of tasks perf/hw_breakpoint: Introduce bp_slots_histogram perf/hw_breakpoint: Optimize max_bp_pinned_slots() for CPU-independent
task targets
perf/hw_breakpoint: Optimize toggle_bp_slot() for CPU-independent task
targets
arch/powerpc/kernel/hw_breakpoint.c | 53 ++- arch/sh/include/asm/hw_breakpoint.h | 5 +- arch/x86/include/asm/hw_breakpoint.h | 5 +- include/linux/hw_breakpoint.h | 4 +- include/linux/percpu-rwsem.h | 6 + include/linux/perf_event.h | 3 +- kernel/events/Makefile | 1 + kernel/events/hw_breakpoint.c | 638 ++++++++++++++++++++------- kernel/events/hw_breakpoint_test.c | 333 ++++++++++++++ kernel/locking/percpu-rwsem.c | 6 + lib/Kconfig.debug | 10 + 11 files changed, 885 insertions(+), 179 deletions(-) create mode 100644 kernel/events/hw_breakpoint_test.c
Signature · 1 line
2.37.2.672.g94769d06f0-goog