Skip to content

Commit

Permalink
pfm parsing for perf stat codes per instruction
Browse files Browse the repository at this point in the history
  • Loading branch information
PhilipDeegan committed Nov 9, 2024
1 parent 8810bad commit 9f7f103
Show file tree
Hide file tree
Showing 14 changed files with 215 additions and 12 deletions.
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,6 @@ bin
__pycache__
dist
phlop.egg-info/
scope_timer.txt

*scope_timer.txt
tpp
24 changes: 24 additions & 0 deletions mkn.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,28 @@ profile:
- name: base
inc: inc

- name: pfm
inc: tpp/pfm/include
sub: pfm&tpp/pfm(git://perfmon2.git.sourceforge.net/gitroot/perfmon2/libpfm4)

- name: pfm_lib
parent: pfm
mode: static
inc: |
tpp/pfm/include
tpp/pfm/lib, 0
src: tpp/pfm/lib

- name: pfm_events
self: pfm_lib
main: tpp/pfm/examples/check_events.c
out: check_events

- name: pfm_info
self: pfm_lib
main: tpp/pfm/examples/showevtinfo.c
out: showevtinfo

- name: scope_timer
parent: base
src: src/phlop/timing/scope_timer.cpp
Expand All @@ -18,3 +40,5 @@ profile:
src: src/phlop/timing/threaded_scope_timer.cpp
mode: shared
test: tests/timing/test_threaded_scope_timer.cpp


3 changes: 3 additions & 0 deletions phlop/app/perf.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ def cli_args_parser(description="Perf tool"):
logging="0=off, 1=on non zero exit code, 2=always",
outfile="path for saved file if active",
tool="stat/record/etc",
extra="forward string to perf command",
)

parser = argparse.ArgumentParser(
Expand All @@ -133,6 +134,8 @@ def cli_args_parser(description="Perf tool"):
parser.add_argument("-o", "--outfile", default=None, help=_help.outfile)
parser.add_argument("-t", "--tool", default="stat", help=_help.tool)
parser.add_argument("--logging", type=int, default=1, help=_help.logging)
parser.add_argument("-e", "--extra", type=str, default="", help=_help.extra)

return parser


Expand Down
5 changes: 5 additions & 0 deletions phlop/app/pfm/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#
#
#
#
#
48 changes: 48 additions & 0 deletions phlop/app/pfm/check_events.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
#
#
#
#
#


import logging
from pathlib import Path

from phlop.os import pushd
from phlop.proc import run
from phlop.string import decode_bytes

FILE_DIR = Path(__file__).resolve().parent

logger = logging.getLogger(__name__)
check_events_start = "Total events:"


def parse_check_events_output(lines):
return lines[-1].split(":")[1].strip().replace("0x", "r")


def run_check_events(code):
with pushd(FILE_DIR.parent.parent.parent):
return decode_bytes(
run(f"./tpp/pfm/examples/check_events {code}").stdout
).splitlines()


def get_evt_perf_code(code):
return parse_check_events_output(run_check_events(code))


if __name__ == "__main__":
from phlop.app.pfm.showevtinfo import get_evt_info

key, code = "[MULT_FLOPS]", ""
for info in get_evt_info():
if key in info.umask:
code = f"{info.name}:{info.umask[key].code}"
break

assert code != ""

# print("get_evt_perf_code", get_evt_perf_code(code))
print(run(f"perf stat -e {get_evt_perf_code(code)} sleep 5"))
81 changes: 81 additions & 0 deletions phlop/app/pfm/showevtinfo.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
#
#
#
#
#


import logging
from dataclasses import dataclass, field
from pathlib import Path

from phlop.os import pushd
from phlop.proc import run
from phlop.string import decode_bytes

FILE_DIR = Path(__file__).resolve().parent

logger = logging.getLogger(__name__)
EVTINFO_delimiter = "#-----------------------------"


@dataclass
class EVTUMask:
id: str
desc: str
code: str


@dataclass
class EVTInfo:
idx: str
pmu: str
name: str
umask: dict = field(default_factory=lambda: {})
ect: dict = field(default_factory=lambda: {})


def _parse_evtinfo(bits_list):
assert len(bits_list) >= 7
info = EVTInfo(
idx=bits_list[0][1].strip(),
pmu=bits_list[1][1].strip(),
name=bits_list[2][1].strip(),
)
for bits in bits_list[7:]:
if bits[0].strip().startswith("Umask"):
info.umask[bits[3].strip()] = EVTUMask(
id=bits[3].strip(), desc=bits[5].strip(), code=bits[1].strip()
)
return info


def parse_evtinfo_output(lines):
start_idx = 0
for line in lines:
start_idx += 1
if line.strip() == EVTINFO_delimiter:
break

bits_list, results = [], []
for line in lines[start_idx:]:
if line == EVTINFO_delimiter:
results.append(_parse_evtinfo(bits_list))
bits_list = []
continue
bits_list.append(line.strip().split(":"))

return results


def run_evtinfo():
with pushd(FILE_DIR.parent.parent.parent):
return decode_bytes(run("./tpp/pfm/examples/showevtinfo").stdout).splitlines()


def get_evt_info():
return parse_evtinfo_output(run_evtinfo())


if __name__ == "__main__":
print(get_evt_info())
1 change: 0 additions & 1 deletion phlop/app/stats_man.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
from phlop.dict import ValDict
from phlop.proc import run_raw

logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger(__name__)

_default_interval = 2
Expand Down
1 change: 0 additions & 1 deletion phlop/run/mpirun_perf.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@

from phlop.app import perf as p

logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger(__name__)

MPI_RANK = os.environ.get("OMPI_COMM_WORLD_RANK")
Expand Down
1 change: 0 additions & 1 deletion phlop/run/mpirun_stats_man.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@

from phlop.app import stats_man as sman

logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger(__name__)

MPI_RANK = os.environ.get("OMPI_COMM_WORLD_RANK")
Expand Down
10 changes: 7 additions & 3 deletions phlop/run/perf.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,11 @@
"""


def perf_stat_cmd(cli_args, path, line):
def perf_stat_cmd(cli_args, path, line, options):
file = Path(line.split(" ")[-1]).stem
outpath = logpath / path.stem
outpath.mkdir(parents=True, exist_ok=True)
return p.stat_cmd(line, p.stat_events, outpath / f"{file}.json")
return p.stat_cmd(line, p.stat_events, outpath / f"{file}.json", options)


def get_from_files(cli_args):
Expand All @@ -50,7 +50,11 @@ def get_remaining(cli_args):
test_batches = {}
path = Path(cli_args.remaining[-1]).parent
test_case = tc.determine_cores_for_test_case(
tc.TestCase(cmd=perf_stat_cmd(cli_args, path, " ".join(cli_args.remaining)))
tc.TestCase(
cmd=perf_stat_cmd(
cli_args, path, " ".join(cli_args.remaining), cli_args.extra
)
)
)
test_batches[test_case.cores] = [test_case]
return [tc.TestBatch(v, k) for k, v in test_batches.items()]
Expand Down
3 changes: 1 addition & 2 deletions phlop/testing/parallel_processor.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,7 @@
logger = getLogger(__name__)


class TestCaseFailure(Exception):
...
class TestCaseFailure(Exception): ...


class LoggingMode(Enum):
Expand Down
11 changes: 11 additions & 0 deletions sh/setup_pfm.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#!/usr/bin/env bash

CWD="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" && cd "$CWD"/..

set -ex

[ ! -d "tpp/pfm" ] && (
git clone git://perfmon2.git.sourceforge.net/gitroot/perfmon2/libpfm4 tpp/pfm
cd tpp/pfm
make
)
8 changes: 5 additions & 3 deletions sh/test.sh
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
#!/usr/bin/env bash

CWD="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
cd "$CWD"/..
CWD="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" && cd "$CWD"/..

set -ex

Expand All @@ -19,4 +18,7 @@ py -O tests/timing/test_scope_timer.py test_scope_timer -f scope_timer.txt
py -Om phlop.run.valgrind echo yes
py -Om phlop.run.valgrind --tool=massif echo yes

py -Om phlop.run.perf echo yes || echo "perf failed, assumed CI"
py -Om phlop.run.perf -e="--all-user" echo yes || echo "perf failed, assumed CI"

# install via ./sh/setup_pfm.sh
[ -d "tpp/pfm" ] && py -O tests/_phlop/app/pfm/test_pfm.py || echo "pfm missing, skipped"
27 changes: 27 additions & 0 deletions tests/_phlop/app/pfm/test_pfm.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#
#
#


from phlop.proc import run
from phlop.string import decode_bytes

if __name__ == "__main__":
from phlop.app.pfm.check_events import get_evt_perf_code
from phlop.app.pfm.showevtinfo import get_evt_info

code = ""
key0, key1 = "[MULT_FLOPS]", "[ADD_SUB_FLOPS]"
for info in get_evt_info():
if key0 in info.umask:
for key, umask in info.umask.items():
code += f"{info.name}:{umask.code} "
break
# if key1 in info.umask:
# code += f"{info.name}:{info.umask[key1].code} "

code = code.strip()
assert code != ""

events = " ".join([f"-e {get_evt_perf_code(ev)}" for ev in code.split(" ")])
print(decode_bytes(run(f"perf stat {events} sleep 5").stderr))

0 comments on commit 9f7f103

Please sign in to comment.