Commit d0313e62 authored by Ian Rogers's avatar Ian Rogers Committed by Arnaldo Carvalho de Melo

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: default avatarIan 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.comSigned-off-by: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
parent 9118259c
...@@ -80,7 +80,9 @@ class BigCString: ...@@ -80,7 +80,9 @@ class BigCString:
are all the other C strings (to avoid memory issues the string 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 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 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] strings: Set[str]
big_string: Sequence[str] big_string: Sequence[str]
...@@ -96,6 +98,33 @@ class BigCString: ...@@ -96,6 +98,33 @@ class BigCString:
def compute(self) -> None: def compute(self) -> None:
"""Called once all strings are added to compute the string and offsets.""" """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 # big_string_offset is the current location within the C string
# being appended to - comments, etc. don't count. big_string is # being appended to - comments, etc. don't count. big_string is
# the string contents represented as a list. Strings are immutable # the string contents represented as a list. Strings are immutable
...@@ -104,13 +133,25 @@ class BigCString: ...@@ -104,13 +133,25 @@ class BigCString:
big_string_offset = 0 big_string_offset = 0
self.big_string = [] self.big_string = []
self.offsets = {} 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): for s in sorted(self.strings):
self.offsets[s] = big_string_offset if s not in folded_strings:
self.big_string.append(f'/* offset={big_string_offset} */ "') self.offsets[s] = big_string_offset
self.big_string.append(s) self.big_string.append(f'/* offset={big_string_offset} */ "')
self.big_string.append('"\n') self.big_string.append(s)
big_string_offset += c_len(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() _bcs = BigCString()
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment