perf hist: Add fast path for duplicate entries check
authorKan Liang <kan.liang@linux.intel.com>
Thu, 19 Mar 2020 20:25:17 +0000 (13:25 -0700)
committerArnaldo Carvalho de Melo <acme@redhat.com>
Sat, 18 Apr 2020 12:05:01 +0000 (09:05 -0300)
Perf checks the duplicate entries in a callchain before adding an entry.
However the check is very slow especially with deeper call stack.
Almost ~50% elapsed time of perf report is spent on the check when the
call stack is always depth of 32.

The hist_entry__cmp() is used to compare the new entry with the old
entries. It will go through all the available sorts in the sort_list,
and call the specific cmp of each sort, which is very slow.

Actually, for most cases, there are no duplicate entries in callchain.
The symbols are usually different. It's much faster to do a quick check
for symbols first. Only do the full cmp when the symbols are exactly the
same.

The quick check is only to check symbols, not dso. Export
_sort__sym_cmp.

  $ perf record --call-graph lbr ./tchain_edit_64

  Without the patch
  $time perf report --stdio
  real    0m21.142s
  user    0m21.110s
  sys     0m0.033s

  With the patch
  $time perf report --stdio
  real    0m10.977s
  user    0m10.948s
  sys     0m0.027s

Signed-off-by: Kan Liang <kan.liang@linux.intel.com>
Acked-by: Jiri Olsa <jolsa@redhat.com>
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: Alexey Budankov <alexey.budankov@linux.intel.com>
Cc: Andi Kleen <ak@linux.intel.com>
Cc: Mathieu Poirier <mathieu.poirier@linaro.org>
Cc: Michael Ellerman <mpe@ellerman.id.au>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Pavel Gerasimov <pavel.gerasimov@intel.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Ravi Bangoria <ravi.bangoria@linux.ibm.com>
Cc: Stephane Eranian <eranian@google.com>
Cc: Vitaly Slobodskoy <vitaly.slobodskoy@intel.com>
Link: http://lore.kernel.org/lkml/20200319202517.23423-18-kan.liang@linux.intel.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
tools/perf/util/hist.c
tools/perf/util/sort.c
tools/perf/util/sort.h

index 283a69ff6a3dcf941506cbbd7f4a5ed9e7ec7dd0..c2550dbe7dc332778321f66847c80b179ca465a1 100644 (file)
@@ -1070,6 +1070,20 @@ iter_next_cumulative_entry(struct hist_entry_iter *iter,
        return fill_callchain_info(al, node, iter->hide_unresolved);
 }
 
+static bool
+hist_entry__fast__sym_diff(struct hist_entry *left,
+                          struct hist_entry *right)
+{
+       struct symbol *sym_l = left->ms.sym;
+       struct symbol *sym_r = right->ms.sym;
+
+       if (!sym_l && !sym_r)
+               return left->ip != right->ip;
+
+       return !!_sort__sym_cmp(sym_l, sym_r);
+}
+
+
 static int
 iter_add_next_cumulative_entry(struct hist_entry_iter *iter,
                               struct addr_location *al)
@@ -1096,6 +1110,7 @@ iter_add_next_cumulative_entry(struct hist_entry_iter *iter,
        };
        int i;
        struct callchain_cursor cursor;
+       bool fast = hists__has(he_tmp.hists, sym);
 
        callchain_cursor_snapshot(&cursor, &callchain_cursor);
 
@@ -1106,6 +1121,14 @@ iter_add_next_cumulative_entry(struct hist_entry_iter *iter,
         * It's possible that it has cycles or recursive calls.
         */
        for (i = 0; i < iter->curr; i++) {
+               /*
+                * For most cases, there are no duplicate entries in callchain.
+                * The symbols are usually different. Do a quick check for
+                * symbols first.
+                */
+               if (fast && hist_entry__fast__sym_diff(he_cache[i], &he_tmp))
+                       continue;
+
                if (hist_entry__cmp(he_cache[i], &he_tmp) == 0) {
                        /* to avoid calling callback function */
                        iter->he = NULL;
index f14cc728c358a84c016ac3951cc147571b19902e..dc15ddc18b7d3fed72536447d2550b1fe999f269 100644 (file)
@@ -237,7 +237,7 @@ static int64_t _sort__addr_cmp(u64 left_ip, u64 right_ip)
        return (int64_t)(right_ip - left_ip);
 }
 
-static int64_t _sort__sym_cmp(struct symbol *sym_l, struct symbol *sym_r)
+int64_t _sort__sym_cmp(struct symbol *sym_l, struct symbol *sym_r)
 {
        if (!sym_l || !sym_r)
                return cmp_null(sym_l, sym_r);
index cfa6ac6f7d06da5580542767ff04800ee61c8583..66d39c4cfe2b3213344720b5460b04de6edd69f7 100644 (file)
@@ -311,5 +311,7 @@ int64_t
 sort__daddr_cmp(struct hist_entry *left, struct hist_entry *right);
 int64_t
 sort__dcacheline_cmp(struct hist_entry *left, struct hist_entry *right);
+int64_t
+_sort__sym_cmp(struct symbol *sym_l, struct symbol *sym_r);
 char *hist_entry__srcline(struct hist_entry *he);
 #endif /* __PERF_SORT_H */