perf jevents: Fold strings optimization

If a shorter string ends a longer string then the shorter string may
reuse the longer string at an offset. For example, on x86 the event
arith.cycles_div_busy and cycles_div_busy can be folded, even though
they have difference names the strings are identical after 6
characters. cycles_div_busy can reuse the arith.cycles_div_busy string
at an offset of 6.

In pmu-events.c this looks like the following where the 'also:' lists
folded strings:

/* offset=177541 */ "arith.cycles_div_busy\000\000pipeline\000Cycles the divider is busy\000\000\000event=0x14,period=2000000,umask=0x1\000\000\000\000\000\000\000\000\000" /* also: cycles_div_busy\000\000pipeline\000Cycles the divider is busy\000\000\000event=0x14,period=2000000,umask=0x1\000\000\000\000\000\000\000\000\000 */

As jevents.py combines multiple strings for an event into a larger
string, the amount of folding is minimal as all parts of the event must
align. Other organizations can benefit more from folding, but lose space
by say recording more offsets.

Signed-off-by: Ian Rogers <irogers@google.com>
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Andi Kleen <ak@linux.intel.com>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: James Clark <james.clark@arm.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: John Garry <john.garry@huawei.com>
Cc: Kan Liang <kan.liang@linux.intel.com>
Cc: Leo Yan <leo.yan@linaro.org>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Mike Leach <mike.leach@linaro.org>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Ravi Bangoria <ravi.bangoria@amd.com>
Cc: Stephane Eranian <eranian@google.com>
Cc: Will Deacon <will@kernel.org>
Cc: Xing Zhengjun <zhengjun.xing@linux.intel.com>
Cc: linux-arm-kernel@lists.infradead.org
Link: https://lore.kernel.org/r/20220812230949.683239-15-irogers@google.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
This commit is contained in:
Ian Rogers 2022-08-12 16:09:49 -07:00 committed by Arnaldo Carvalho de Melo
parent 9118259c1d
commit d0313e629f
1 changed files with 48 additions and 7 deletions

View File

@ -80,7 +80,9 @@ class BigCString:
are all the other C strings (to avoid memory issues the string
itself is held as a list of strings). The offsets within the big
string are recorded and when stored to disk these don't need
relocation.
relocation. To reduce the size of the string further, identical
strings are merged. If a longer string ends-with the same value as a
shorter string, these entries are also merged.
"""
strings: Set[str]
big_string: Sequence[str]
@ -96,6 +98,33 @@ class BigCString:
def compute(self) -> None:
"""Called once all strings are added to compute the string and offsets."""
folded_strings = {}
# Determine if two strings can be folded, ie. let 1 string use the
# end of another. First reverse all strings and sort them.
sorted_reversed_strings = sorted([x[::-1] for x in self.strings])
# Strings 'xyz' and 'yz' will now be [ 'zy', 'zyx' ]. Scan forward
# for each string to see if there is a better candidate to fold it
# into, in the example rather than using 'yz' we can use'xyz' at
# an offset of 1. We record which string can be folded into which
# in folded_strings, we don't need to record the offset as it is
# trivially computed from the string lengths.
for pos,s in enumerate(sorted_reversed_strings):
best_pos = pos
for check_pos in range(pos + 1, len(sorted_reversed_strings)):
if sorted_reversed_strings[check_pos].startswith(s):
best_pos = check_pos
else:
break
if pos != best_pos:
folded_strings[s[::-1]] = sorted_reversed_strings[best_pos][::-1]
# Compute reverse mappings for debugging.
fold_into_strings = collections.defaultdict(set)
for key, val in folded_strings.items():
if key != val:
fold_into_strings[val].add(key)
# big_string_offset is the current location within the C string
# being appended to - comments, etc. don't count. big_string is
# the string contents represented as a list. Strings are immutable
@ -104,13 +133,25 @@ class BigCString:
big_string_offset = 0
self.big_string = []
self.offsets = {}
# Emit all strings in a sorted manner.
# Emit all strings that aren't folded in a sorted manner.
for s in sorted(self.strings):
self.offsets[s] = big_string_offset
self.big_string.append(f'/* offset={big_string_offset} */ "')
self.big_string.append(s)
self.big_string.append('"\n')
big_string_offset += c_len(s)
if s not in folded_strings:
self.offsets[s] = big_string_offset
self.big_string.append(f'/* offset={big_string_offset} */ "')
self.big_string.append(s)
self.big_string.append('"')
if s in fold_into_strings:
self.big_string.append(' /* also: ' + ', '.join(fold_into_strings[s]) + ' */')
self.big_string.append('\n')
big_string_offset += c_len(s)
continue
# Compute the offsets of the folded strings.
for s in folded_strings.keys():
assert s not in self.offsets
folded_s = folded_strings[s]
self.offsets[s] = self.offsets[folded_s] + c_len(folded_s) - c_len(s)
_bcs = BigCString()