Optimize cgroup ctx switch and remove cpuctx->unique_pmu
v2 cover Optimize cgroup ctx switch and remove cpuctx->unique_pmu
v2: Rename local variable, as suggested by Mark Rutland and
add his Acked-by.
First patch is a resend of ("perf/core: Make cgroup switch visit only cpuctxs with cgroup events") with updated change log.
Second patch cleans up the now unused cpuctx->unique_pmu.
David Carrillo-Cisneros (2):
perf/core: Make cgroup switch visit only cpuctxs with cgroup events perf/core: Remove perf_cpu_context::unique_pmu
include/linux/perf_event.h | 2 +- kernel/events/core.c | 129 ++++++++++++++++----------------------------- 2 files changed, 47 insertions(+), 84 deletions(-)
Signature · 1 line
2.11.0.483.g087da7b7c-goog
PATCH v2 1/2 perf/core: Make cgroup switch visit only cpuctxs with cgroup events
v2: Rename local variable as suggested by Mark Rutland.
v1: This is a resend of v2 of ("perf/core: only check cpuctx with cgroup
events during cgroup switch") with updated changelog.
This patch follows from a conversation in CQM/CMT's last series about speeding up the context switch for cgroup events:
https://patchwork.kernel.org/patch/9478617/
This is a low-hanging fruit optimization. It replaces the iteration over the "pmus" list in cgroup switch by an iteration over a new list that contains only cpuctxs with at least one cgroup event.
This is necessary because the number of pmus have increased over the years e.g modern x86 server systems have well above 50 pmus.
The iteration over the full pmu list is unneccessary and can be costly in heavy cache contention scenarios.
Below are some instrumentation measurements with 10, 50 and 90 percentiles of the total cost of context switch before and after this optimization for a simple array read/write microbenchark.
Contention
Level Nr events Before (us) After (us) Median
L2 L3 types (10%, 50%, 90%) (10%, 50%, 90% Speedup
--------------------------------------------------------------------------
Low Low 1 (1.72, 2.42, 5.85) (1.35, 1.64, 5.46) 29% High Low 1 (2.08, 4.56, 19.8) (1720, 2.20, 13.7) 51% High High 1 (2.86, 10.4, 12.7) (2.54, 4.32, 12.1) 58%
Low Low 2 (1.98, 3.20, 6.89) (1.68, 2.41, 8.89) 24% High Low 2 (2.48, 5.28, 22.4) (2150, 3.69, 14.6) 30% High High 2 (3.32, 8.09, 13.9) (2.80, 5.15, 13.7) 36%
where:
1 event type = cycles
2 event types = cycles,intel_cqm/llc_occupancy/
Contention L2 Low: workset < L2 cache size.
High: " >> L2 " " .
Contention L3 Low: workset of task on all sockets < L3 cache size.
High: " " " " " " >> L3 " " .
Median Speedup is (50%ile Before - 50%ile After) / 50%ile Before
Unsurprisingly, the benefits of this optimization decrease with the number of cpuctxs with a cgroup events, yet, is never detrimental.
--- include/linux/perf_event.h | 1 + kernel/events/core.c | 98 +++++++++++++++++++++------------------------- 2 files changed, 46 insertions(+), 53 deletions(-)
include/linux/perf_event.h +1 -0
@@ -788,6 +788,7 @@ struct perf_cpu_context {struct pmu *unique_pmu;#ifdef CONFIG_CGROUP_PERF struct perf_cgroup *cgrp; +struct list_head cgrp_cpuctx_entry;#endif struct list_head sched_cb_entry;
kernel/events/core.c +45 -53
139 lines not on this page · raw message
Signature · 1 line
2.11.0.483.g087da7b7c-goog
tip-bot for David Carrillo-Cisneros
merge bot[tip:perf/core] perf/core: Make cgroup switch visit only cpuctxs with cgroup events
perf/core: Make cgroup switch visit only cpuctxs with cgroup events
Commit-ID: 058fe1c0440e68a1ba3c2270ae43e9f0298b27d8 Gitweb: http://git.kernel.org/tip/058fe1c0440e68a1ba3c2270ae43e9f0298b27d8 Author: David Carrillo-Cisneros <[email protected]> AuthorDate: Wed, 18 Jan 2017 11:24:53 -0800 Committer: Ingo Molnar <[email protected]> CommitDate: Mon, 30 Jan 2017 12:01:13 +0100
perf/core: Make cgroup switch visit only cpuctxs with cgroup events
This patch follows from a conversation in CQM/CMT's last series about speeding up the context switch for cgroup events:
https://patchwork.kernel.org/patch/9478617/
This is a low-hanging fruit optimization. It replaces the iteration over the "pmus" list in cgroup switch by an iteration over a new list that contains only cpuctxs with at least one cgroup event.
This is necessary because the number of PMUs have increased over the years e.g modern x86 server systems have well above 50 PMUs.
The iteration over the full PMU list is unneccessary and can be costly in heavy cache contention scenarios.
Below are some instrumentation measurements with 10, 50 and 90 percentiles of the total cost of context switch before and after this optimization for a simple array read/write microbenchark.
Contention
Level Nr events Before (us) After (us) Median
L2 L3 types (10%, 50%, 90%) (10%, 50%, 90% Speedup
--------------------------------------------------------------------------
Low Low 1 (1.72, 2.42, 5.85) (1.35, 1.64, 5.46) 29% High Low 1 (2.08, 4.56, 19.8) (1720, 2.20, 13.7) 51% High High 1 (2.86, 10.4, 12.7) (2.54, 4.32, 12.1) 58%
Low Low 2 (1.98, 3.20, 6.89) (1.68, 2.41, 8.89) 24% High Low 2 (2.48, 5.28, 22.4) (2150, 3.69, 14.6) 30% High High 2 (3.32, 8.09, 13.9) (2.80, 5.15, 13.7) 36%
where:
1 event type = cycles
2 event types = cycles,intel_cqm/llc_occupancy/
Contention L2 Low: workset < L2 cache size.
High: " >> L2 " " .
Contention L3 Low: workset of task on all sockets < L3 cache size.
High: " " " " " " >> L3 " " .
Median Speedup is (50%ile Before - 50%ile After) / 50%ile Before
Unsurprisingly, the benefits of this optimization decrease with the number of cpuctxs with a cgroup events, yet, is never detrimental.
--- include/linux/perf_event.h | 1 + kernel/events/core.c | 98 +++++++++++++++++++++------------------------- 2 files changed, 46 insertions(+), 53 deletions(-)
include/linux/perf_event.h +1 -0
@@ -788,6 +788,7 @@ struct perf_cpu_context {struct pmu *unique_pmu;#ifdef CONFIG_CGROUP_PERF struct perf_cgroup *cgrp; +struct list_head cgrp_cpuctx_entry;#endif struct list_head sched_cb_entry;
kernel/events/core.c +45 -53
139 lines not on this page · raw message
PATCH v2 2/2 perf/core: Remove perf_cpu_context::unique_pmu
In reply to David Carrillo-Cisneroscpuctx->unique_pmu was originally introduced as a way to identify cpuctxs with shared pmus in order to avoid visiting the same cpuctx more than once in a for_each_pmu loop.
cpuctx->unique_pmu == cpuctx->pmu in non-software task contexts since they have only one pmu per cpuctx. Since perf_pmu_sched_task is only called in hw contexts, this patch replaces cpuctx->unique_pmu by cpuctx->pmu in it.
The change above, together with the previous patch in this series, removed the remaining uses of cpuctx->unique_pmu, so we remove it altogether.
--- include/linux/perf_event.h | 1 - kernel/events/core.c | 31 +------------------------------ 2 files changed, 1 insertion(+), 31 deletions(-)
include/linux/perf_event.h +0 -1
@@ -785,7 +785,6 @@ struct perf_cpu_context {ktime_t hrtimer_interval;unsigned int hrtimer_active; -struct pmu *unique_pmu;#ifdef CONFIG_CGROUP_PERF struct perf_cgroup *cgrp;struct list_head cgrp_cpuctx_entry;
kernel/events/core.c +1 -30
@@ -2883,7 +2883,7 @@ static void perf_pmu_sched_task(struct task_struct *prev,return ;list_for_each_entry (cpuctx,this_cpu_ptr (&sched_cb_list), sched_cb_entry) { - pmu = cpuctx->unique_pmu;/* software PMUs will not have sched_task */ + pmu = cpuctx->ctx.pmu;/* software PMUs will not have sched_task */ if (WARN_ON_ONCE (!pmu->sched_task))continue ;
@@ -8572,37 +8572,10 @@ static struct perf_cpu_context __percpu *find_pmu_context(int ctxn)return NULL ; } -static void update_pmu_context (struct pmu *pmu,struct pmu *old_pmu) -{ -int cpu; - -for_each_possible_cpu (cpu) { -struct perf_cpu_context *cpuctx; - - cpuctx =per_cpu_ptr (pmu->pmu_cpu_context, cpu); - -if (cpuctx->unique_pmu == old_pmu) - cpuctx->unique_pmu = pmu; - } -} -static void free_pmu_context (struct pmu *pmu) { -struct pmu *i; -mutex_lock (&pmus_lock); -/* -* Like a real lame refcount. -*/ -list_for_each_entry (i, &pmus, entry) { -if (i->pmu_cpu_context == pmu->pmu_cpu_context) { -update_pmu_context (i, pmu); - goto out; - } - } -free_percpu (pmu->pmu_cpu_context); -out:mutex_unlock (&pmus_lock); }
@@ -8806,8 +8779,6 @@ int perf_pmu_register(struct pmu *pmu, const char *name, int type) cpuctx->ctx.pmu = pmu;__perf_mux_hrtimer_init (cpuctx, cpu); - - cpuctx->unique_pmu = pmu; } got_cpu_context:
Signature · 1 line
2.11.0.483.g087da7b7c-goog
Peter Zijlstra
maintainerRe: [PATCH v2 2/2] perf/core: Remove perf_cpu_context::unique_pmu
Quoted 42 lines · David Carrillo-Cisneros
cpuctx->unique_pmuwas originally introduced as a way to identify cpuctxs with shared pmus in order to avoid visiting the same cpuctx more than once in afor_each_pmuloop.
cpuctx->unique_pmu==cpuctx->pmuin non-software task contexts since they have only one pmu per cpuctx. Sinceperf_pmu_sched_taskis only called in hw contexts, this patch replacescpuctx->unique_pmubycpuctx->pmuin it.The change above, together with the previous patch in this series, removed the remaining uses of
cpuctx->unique_pmu, so we remove it altogether.Signed-off-by David Carrillo-Cisneros <[email protected]>Acked-by Mark Rutland <[email protected]>kernel/events/core.c +0
-20@@ -8572,37 +8572,10 @@ static struct perf_cpu_context __percpu *find_pmu_context(int ctxn)return NULL ; } -static void update_pmu_context (struct pmu *pmu,struct pmu *old_pmu) -{ -int cpu; - -for_each_possible_cpu (cpu) { -struct perf_cpu_context *cpuctx; - - cpuctx =per_cpu_ptr (pmu->pmu_cpu_context, cpu); - -if (cpuctx->unique_pmu == old_pmu) - cpuctx->unique_pmu = pmu; - } -} -static void free_pmu_context (struct pmu *pmu) { -struct pmu *i; -mutex_lock (&pmus_lock); -/* -* Like a real lame refcount. -*/ -list_for_each_entry (i, &pmus, entry) {
kernel/events/core.c static struct perf_cpu_context __percpu *find_pmu_context(int ctxn) · line 8580
Quoted 10 lines
kernel/events/core.c +0
-7-if (i->pmu_cpu_context == pmu->pmu_cpu_context) { -update_pmu_context (i, pmu); - goto out; - } - } -free_percpu (pmu->pmu_cpu_context); -out:mutex_unlock (&pmus_lock); }
This very much relies on us never calling perf_pmu_unregister() on the software PMUs afaict. A condition not mention in the Changelog.
Mark Rutland
reviewerRe: [PATCH v2 2/2] perf/core: Remove perf_cpu_context::unique_pmu
Quoted 47 lines · Peter Zijlstra
Quoted 46 lines · David Carrillo-Cisneros
cpuctx->unique_pmuwas originally introduced as a way to identify cpuctxs with shared pmus in order to avoid visiting the same cpuctx more than once in afor_each_pmuloop.
cpuctx->unique_pmu==cpuctx->pmuin non-software task contexts since they have only one pmu per cpuctx. Sinceperf_pmu_sched_taskis only called in hw contexts, this patch replacescpuctx->unique_pmubycpuctx->pmuin it.The change above, together with the previous patch in this series, removed the remaining uses of
cpuctx->unique_pmu, so we remove it altogether.Signed-off-by David Carrillo-Cisneros <[email protected]>Acked-by Mark Rutland <[email protected]>kernel/events/core.c +0
-25@@ -8572,37 +8572,10 @@ static struct perf_cpu_context __percpu *find_pmu_context(int ctxn)return NULL ; } -static void update_pmu_context (struct pmu *pmu,struct pmu *old_pmu) -{ -int cpu; - -for_each_possible_cpu (cpu) { -struct perf_cpu_context *cpuctx; - - cpuctx =per_cpu_ptr (pmu->pmu_cpu_context, cpu); - -if (cpuctx->unique_pmu == old_pmu) - cpuctx->unique_pmu = pmu; - } -} -static void free_pmu_context (struct pmu *pmu) { -struct pmu *i; -mutex_lock (&pmus_lock); -/* -* Like a real lame refcount. -*/ -list_for_each_entry (i, &pmus, entry) { -if (i->pmu_cpu_context == pmu->pmu_cpu_context) { -update_pmu_context (i, pmu); - goto out; - } - }
Quoted 8 lines
kernel/events/core.c static struct perf_cpu_context __percpu *find_pmu_context(int ctxn) · line 8580
Quoted 5 lines
kernel/events/core.c +0
-2-free_percpu (pmu->pmu_cpu_context); -out:mutex_unlock (&pmus_lock); }This very much relies on us never calling
perf_pmu_unregister()on the software PMUs afaict. A condition not mention in the Changelog.
Ah; I did not consider that that could leave perf_pmu_sched_task() seeing a stale cpuctx->ctx.pmu.
That said, is this not already a problem elsewhere? We don't update ctx->pmu in perf_pmu_unregister, so this would be a problem for any path using ctx->pmu today (e.g. perf_event_context_sched_in()).
Thanks,
Mark.
Re: [PATCH v2 2/2] perf/core: Remove perf_cpu_context::unique_pmu
In reply to Peter ZijlstraQuoted 48 lines · Peter Zijlstra
Quoted 47 lines · David Carrillo-Cisneros
cpuctx->unique_pmuwas originally introduced as a way to identify cpuctxs with shared pmus in order to avoid visiting the same cpuctx more than once in afor_each_pmuloop.
cpuctx->unique_pmu==cpuctx->pmuin non-software task contexts since they have only one pmu per cpuctx. Sinceperf_pmu_sched_taskis only called in hw contexts, this patch replacescpuctx->unique_pmubycpuctx->pmuin it.The change above, together with the previous patch in this series, removed the remaining uses of
cpuctx->unique_pmu, so we remove it altogether.Signed-off-by David Carrillo-Cisneros <[email protected]>Acked-by Mark Rutland <[email protected]>kernel/events/core.c +0
-25@@ -8572,37 +8572,10 @@ static struct perf_cpu_context __percpu *find_pmu_context(int ctxn)return NULL ; } -static void update_pmu_context (struct pmu *pmu,struct pmu *old_pmu) -{ -int cpu; - -for_each_possible_cpu (cpu) { -struct perf_cpu_context *cpuctx; - - cpuctx =per_cpu_ptr (pmu->pmu_cpu_context, cpu); - -if (cpuctx->unique_pmu == old_pmu) - cpuctx->unique_pmu = pmu; - } -} -static void free_pmu_context (struct pmu *pmu) { -struct pmu *i; -mutex_lock (&pmus_lock); -/* -* Like a real lame refcount. -*/ -list_for_each_entry (i, &pmus, entry) { -if (i->pmu_cpu_context == pmu->pmu_cpu_context) { -update_pmu_context (i, pmu); - goto out; - } - }
Quoted 9 lines
kernel/events/core.c static struct perf_cpu_context __percpu *find_pmu_context(int ctxn) · line 8580
Quoted 5 lines
kernel/events/core.c +0
-2-free_percpu (pmu->pmu_cpu_context); -out:mutex_unlock (&pmus_lock); }This very much relies on us never calling
perf_pmu_unregister()on the software PMUs afaict. A condition not mention in the Changelog.
What's a good way to solve this? Update the Changelog or add code to update ctx->pmu?
This issue would go away cleanly if we were to remove the context sharing across pmu's. Would you support work in that direction?
Thanks,
David
Peter Zijlstra
maintainerRe: [PATCH v2 2/2] perf/core: Remove perf_cpu_context::unique_pmu
Quoted 18 lines · David Carrillo-Cisneros
Quoted 17 lines · Peter Zijlstra
Quoted 13 lines · David Carrillo-Cisneros
cpuctx->unique_pmuwas originally introduced as a way to identify cpuctxs with shared pmus in order to avoid visiting the same cpuctx more than once in afor_each_pmuloop.
cpuctx->unique_pmu==cpuctx->pmuin non-software task contexts since they have only one pmu per cpuctx. Sinceperf_pmu_sched_taskis only called in hw contexts, this patch replacescpuctx->unique_pmubycpuctx->pmuin it.The change above, together with the previous patch in this series, removed the remaining uses of
cpuctx->unique_pmu, so we remove it altogether.Signed-off-by David Carrillo-Cisneros <[email protected]>Acked-by Mark Rutland <[email protected]>
Quoted 5 lines
Quoted 3 lines
This very much relies on us never calling
perf_pmu_unregister()on the software PMUs afaict. A condition not mention in the Changelog.What's a good way to solve this? Update the Changelog or add code to update
ctx->pmu?
I think just update the Changelog and maybe put a comment near perf_pmu_register() and/or the sw pmu abuse that relies on this.
Quoted 2 lines
This issue would go away cleanly if we were to remove the context sharing across pmu's. Would you support work in that direction?
Its something that I've considered, the trivial solution is folding it all into the one swevent pmu by adding a switch in all the add/del/start/stop/read methods. Its a wee bit ugly but straight fwd.
I've not really found anything less ugly though; and I have to fully admit to the current situation being rather vile.
I also just found the below patch that I've had bitrotting since 2015.
---
Subject: perf: Move all software PMUs into their own file
From: Peter Zijlstra <[email protected]>
Date: Fri Apr 17 19:52:17 CEST 2015
--- kernel/events/Makefile | 2 kernel/events/core.c | 1280 +++++------------------------------------------ kernel/events/internal.h | 13 kernel/events/software.c | 1021 +++++++++++++++++++++++++++++++++++++ 4 files changed, 1184 insertions(+), 1132 deletions(-)
kernel/events/Makefile +1 -1
@@ -2,7 +2,7 @@ ifdef CONFIG_FUNCTION_TRACER CFLAGS_REMOVE_core.o = $(CC_FLAGS_FTRACE) endif -obj-y := core.o ring_buffer.o callchain.o +obj-y := core.o software.o ring_buffer.o callchain.o obj-$(CONFIG_HAVE_HW_BREAKPOINT) += hw_breakpoint.o obj-$(CONFIG_UPROBES) += uprobes.o
kernel/events/core.c +149 -1131
1,433 lines not on this page · raw message
kernel/events/internal.h +13 -0
@@ -228,4 +228,17 @@ static inline bool arch_perf_have_user_s#define perf_user_stack_pointer (regs) 0#endif /* CONFIG_HAVE_PERF_USER_STACK_DUMP */ +#define MAX_INTERRUPTS (~0ULL) + +extern int __perf_event_overflow (struct perf_event *event, +int throttle,struct perf_sample_data *data, +struct pt_regs *regs); + +extern void perf_event_free_filter (struct perf_event *event); +extern void perf_event_free_bpf_prog (struct perf_event *event); +extern int perf_event_set_filter (struct perf_event *event,void __user *arg); +extern int perf_event_set_bpf_prog (struct perf_event *event,u32 prog_fd); + +extern void perf_swevent_register (void ); +#endif /* _KERNEL_EVENTS_INTERNAL_H */
kernel/events/software.c new file +1021 -0
1,021 lines not on this page · raw message
tip-bot for David Carrillo-Cisneros
merge bot[tip:perf/core] perf/core: Remove perf_cpu_context::unique_pmu
In reply to David Carrillo-Cisnerosperf/core: Remove perf_cpu_context::unique_pmu
Commit-ID: 1fd7e416995401ec082fc0fe6090a223969beda5 Gitweb: http://git.kernel.org/tip/1fd7e416995401ec082fc0fe6090a223969beda5 Author: David Carrillo-Cisneros <[email protected]> AuthorDate: Wed, 18 Jan 2017 11:24:54 -0800 Committer: Ingo Molnar <[email protected]> CommitDate: Mon, 30 Jan 2017 12:01:14 +0100
perf/core: Remove perf_cpu_context::unique_pmu
cpuctx->unique_pmu was originally introduced as a way to identify cpuctxs with shared pmus in order to avoid visiting the same cpuctx more than once in a for_each_pmu loop.
cpuctx->unique_pmu == cpuctx->pmu in non-software task contexts since they have only one pmu per cpuctx. Since perf_pmu_sched_task() is only called in hw contexts, this patch replaces cpuctx->unique_pmu by cpuctx->pmu in it.
The change above, together with the previous patch in this series, removed the remaining uses of cpuctx->unique_pmu, so we remove it altogether.
--- include/linux/perf_event.h | 1 - kernel/events/core.c | 31 +------------------------------ 2 files changed, 1 insertion(+), 31 deletions(-)
include/linux/perf_event.h +0 -1
@@ -785,7 +785,6 @@ struct perf_cpu_context {ktime_t hrtimer_interval;unsigned int hrtimer_active; -struct pmu *unique_pmu;#ifdef CONFIG_CGROUP_PERF struct perf_cgroup *cgrp;struct list_head cgrp_cpuctx_entry;
kernel/events/core.c +1 -30
@@ -2932,7 +2932,7 @@ static void perf_pmu_sched_task(struct task_struct *prev,return ;list_for_each_entry (cpuctx,this_cpu_ptr (&sched_cb_list), sched_cb_entry) { - pmu = cpuctx->unique_pmu;/* software PMUs will not have sched_task */ + pmu = cpuctx->ctx.pmu;/* software PMUs will not have sched_task */ if (WARN_ON_ONCE (!pmu->sched_task))continue ;
@@ -8636,37 +8636,10 @@ static struct perf_cpu_context __percpu *find_pmu_context(int ctxn)return NULL ; } -static void update_pmu_context (struct pmu *pmu,struct pmu *old_pmu) -{ -int cpu; - -for_each_possible_cpu (cpu) { -struct perf_cpu_context *cpuctx; - - cpuctx =per_cpu_ptr (pmu->pmu_cpu_context, cpu); - -if (cpuctx->unique_pmu == old_pmu) - cpuctx->unique_pmu = pmu; - } -} -static void free_pmu_context (struct pmu *pmu) { -struct pmu *i; -mutex_lock (&pmus_lock); -/* -* Like a real lame refcount. -*/ -list_for_each_entry (i, &pmus, entry) { -if (i->pmu_cpu_context == pmu->pmu_cpu_context) { -update_pmu_context (i, pmu); - goto out; - } - } -free_percpu (pmu->pmu_cpu_context); -out:mutex_unlock (&pmus_lock); }
@@ -8870,8 +8843,6 @@ skip_type: cpuctx->ctx.pmu = pmu;__perf_mux_hrtimer_init (cpuctx, cpu); - - cpuctx->unique_pmu = pmu; } got_cpu_context: