Skip to content

Commit c420f48

Browse files
authored
Merge pull request #111 from sayefsakin/main
Incorporate PerfAnalyzer
2 parents b3d8a50 + f677887 commit c420f48

20 files changed

+3677
-0
lines changed

.github/workflows/perf_check.yml

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
# This is a basic workflow to help you get started with Actions
2+
3+
name: Code Performance Analyzer
4+
5+
# Controls when the workflow will run
6+
on:
7+
# Triggers the workflow on push or pull request events but only for the "main" branch
8+
# push:
9+
# branches: [ "main" ]
10+
# pull_request:
11+
# branches: [ "main" ]
12+
13+
# Allows you to run this workflow manually from the Actions tab
14+
workflow_dispatch:
15+
# inputs:
16+
# hashes:
17+
# required: true
18+
# type: choice
19+
# description: Make a choice
20+
# options:
21+
# - foo
22+
# - bar
23+
# - baz
24+
25+
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
26+
jobs:
27+
define-matrix:
28+
runs-on: ubuntu-latest
29+
30+
outputs:
31+
hashes: ${{ steps.hashes.outputs.hashes }}
32+
33+
steps:
34+
- name: Define Hashes
35+
id: hashes
36+
run: |
37+
echo 'hashes=["158e23d08f73d36f71e144851451955b3ae02dff", "89cc919b28f687a25d30b44ddf547201da930c14"]' >> "$GITHUB_OUTPUT"
38+
39+
produce-performance-artifacts:
40+
runs-on: ubuntu-latest
41+
defaults:
42+
run:
43+
shell: bash -el {0}
44+
needs: define-matrix
45+
strategy:
46+
matrix:
47+
hashes: ${{ fromJSON(needs.define-matrix.outputs.hashes) }}
48+
49+
steps:
50+
- uses: actions/checkout@v4
51+
with:
52+
repository: UK-MAC/CloverLeaf_ref
53+
ref: ${{ matrix.hashes }}
54+
55+
- uses: fortran-lang/[email protected]
56+
57+
- name: Install OpenMPI
58+
run: sudo apt install -y openmpi-bin libopenmpi-dev
59+
60+
# check all the requirements and their versions
61+
- name: Check installed dependencies
62+
run: |
63+
gcc --version
64+
gfortran --version
65+
mpirun --version
66+
lscpu | grep -E '^Thread|^Core|^Socket|^CPU\('
67+
68+
- name: Compile cloverleaf
69+
run: |
70+
make COMPILER=GNU
71+
72+
- name: Run cloverleaf
73+
run: |
74+
mpirun -np 2 clover_leaf
75+
mv clover.out clover_output_${{ matrix.hashes }}.out
76+
77+
- name: Produce Artifact
78+
uses: actions/upload-artifact@v4
79+
with:
80+
name: clover_artifact_${{ matrix.hashes }}
81+
path: clover_output_${{ matrix.hashes }}.out
82+
83+
consume-artifacts:
84+
runs-on: macos-latest
85+
needs:
86+
- produce-performance-artifacts
87+
88+
steps:
89+
- name: Download all workflow run artifacts
90+
uses: actions/download-artifact@v4
91+
with:
92+
path: clover_artifact
93+
pattern: clover_artifact_*
94+
merge-multiple: true
95+
96+
- name: Check artifact files
97+
run: |
98+
ls -R clover_artifact
99+
cd clover_artifact
100+
tail -n 10 clover_output_*
101+
# # This workflow contains a single job called "build"
102+
# build:
103+
# # The type of runner that the job will run on
104+
# runs-on: macos-latest
105+
106+
# defaults:
107+
# run:
108+
# shell: bash -el {0}
109+
110+
# # Steps represent a sequence of tasks that will be executed as part of the job
111+
# steps:
112+
# # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
113+
# - uses: actions/checkout@v4
114+
# # with:
115+
# # repository: UK-MAC/CloverLeaf_ref
116+
# # ref: 0ddf495cf21cc59f84e274617522a1383e2c328c
117+
118+
# # - uses: actions/setup-python@v5
119+
# # with:
120+
# # python-version: '3.10'
121+
122+
# # - name: Add conda to system path
123+
# # run: |
124+
# # # $CONDA is an environment variable pointing to the root of the miniconda directory
125+
# # echo $CONDA/bin >> $GITHUB_PATH
126+
127+
128+
129+
# - uses: conda-incubator/setup-miniconda@v3
130+
# with:
131+
# channels: defaults,conda-forge,spyder-ide
132+
# activate-environment: cdsi
133+
# environment-file: examples/cloverleaf/environment.yml
134+
# auto-activate-base: false
135+
136+
# # - uses: s-weigand/[email protected]
137+
138+
# # - name: Install dependencies
139+
# # run: |
140+
# # cd examples/cloverleaf
141+
# # conda env create --file environment.yml --name cdsi
142+
# # conda activate cdsi
143+
144+
# # check all the requirements and their versions
145+
# - name: Check installed dependencies
146+
# run: |
147+
# python3 --version
148+
# gcc --version
149+
# conda --version
150+
# gfortran --version
151+
# conda info
152+
# conda list
153+
154+
# # Runs a set of commands using the runners shell
155+
# - name: Run a multi-line script
156+
# run: |
157+
# echo Add other actions to build,
158+
# echo test, and deploy your project.
159+
# ls .

examples/perf_analyzer/Readme.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
## PerfAnalyzer
2+
3+
A tool to analze software performance along with code history. This is built on top of DSI SQLite plugin.
4+
5+
Run `fly_server.py` file. Then the dashboard can be accessed through `http://127.0.0.1:8050/`.
6+
7+
##### TODO: add a requirement file
8+
9+
Update the `runner_script.sh` to compile, copy input file, and run the program.
10+
11+
Update the `parse_clover_output.py` file and update `parse_clover_output_file` function to parse specific output file. Return a dictionary containing the contents of the parsed output file.
12+
13+
#### The features available in the dashboard
14+
15+
PerfAnalyzer is a dashboard based visualizer to analyze performance using git commit history and different performance metric. This has the following features
16+
17+
- Git History Graph
18+
- Ordered by commit dates
19+
- Filter Git Branch
20+
- Select a subset of git commits
21+
- Show Commit details like message, committer name, date, and hash.
22+
- Performance metric line chart
23+
- Filter by different metric
24+
- Show details on hover
25+
- Commit table
26+
- Search and filter by date, hash, and message.
27+
- Execute the `runner_script` on selected commit.
28+
- Show difference between two commits (using git diff)
29+
- Variable Search
30+
- Use any regex or string to search
31+
- Show table of found variable and file
32+
- Show file content
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
#! /bin/bash
2+
3+
# make sure that cdsi environment is activated
4+
if [[ $CONDA_DEFAULT_ENV != 'cdsi' ]]; then
5+
echo "activate conda cdsi environment."
6+
exit 0
7+
fi
8+
if [ -z ${SOURCE_BASE_DIRECTORY+x} ]; then
9+
echo "SOURCE_BASE_DIRECTORY is unset"
10+
exit 0;
11+
else
12+
echo "SOURCE_BASE_DIRECTORY is set to '$SOURCE_BASE_DIRECTORY'";
13+
fi
14+
15+
# SOURCE_BASE_DIRECTORY="/Users/ssakin/projects/CloverLeaf/CloverLeaf_ref"
16+
MPI_THREADS=4
17+
export CHECK_PREV_COMMITS=15
18+
export OMP_NUM_THREADS=4
19+
base_directory=$(pwd)
20+
21+
run_and_check_commit() {
22+
echo "current commit hash $1"
23+
24+
cd $SOURCE_BASE_DIRECTORY
25+
git checkout $1
26+
make clean
27+
make COMPILER=GNU
28+
echo "================================ Compile Done ================================ "
29+
30+
echo "============================= Running CloverLeaf ============================= "
31+
mpirun -np $MPI_THREADS clover_leaf
32+
cp clover.out $base_directory"/clover_output/clover_$1.out"
33+
echo "======================= CloverLeaf Executed for has $1 ======================= "
34+
35+
echo "=========================== Running output parser ============================ "
36+
cd $base_directory
37+
python3 parse_clover_output.py --testname random_test --gitdir $SOURCE_BASE_DIRECTORY
38+
echo "============================ Output CSV updated ============================== "
39+
}
40+
41+
track_variables() {
42+
echo "current commit hash $1"
43+
44+
cd $SOURCE_BASE_DIRECTORY
45+
git checkout $1
46+
47+
echo "=========================== Running code sensing ============================ "
48+
cd $base_directory
49+
python3 code_sensing.py --testname random_test --gitdir $SOURCE_BASE_DIRECTORY
50+
echo "============================ Output CSV updated ============================== "
51+
}
52+
53+
cd $SOURCE_BASE_DIRECTORY
54+
prev_hash=( $(git log master -n "$CHECK_PREV_COMMITS" --format=format:%h) )
55+
56+
for c_hash in "${prev_hash[@]}"
57+
do
58+
# run_and_check_commit $c_hash
59+
track_variables $c_hash
60+
done
61+
62+
cd $SOURCE_BASE_DIRECTORY
63+
git checkout master
64+
echo "=========================== Auto Perf Script Completed ============================ "
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
#!/usr/bin/env python3
2+
3+
import argparse
4+
import pickle
5+
import sys
6+
import glob
7+
import re
8+
import git
9+
10+
def recursive_c_directive_match(re_list, search_file_list, cur_dir):
11+
""" The data is parsed from all of the files in the current directory """
12+
occurance = dict()
13+
for code_files in glob.iglob('**', root_dir=cur_dir, recursive=True):
14+
for f_type in search_file_list:
15+
if re.search(f_type + '$', code_files):
16+
with open(cur_dir + "/" + code_files, 'r') as cf:
17+
line_number = 1
18+
for line in cf:
19+
for each_re in re_list:
20+
line_match = re.compile(r"\s*[#]" + each_re + r"\s+(\w+)[\t\s]+(.*)\s*(\r\n|\r|\n)").match(line)
21+
if line_match is not None:
22+
c_line = "#" + each_re + " " + line_match.group(1)
23+
second_part = line_match.group(2)
24+
if second_part is not None and len(second_part) > 0:
25+
c_line = c_line + " " + line_match.group(2)
26+
c_line = c_line.rstrip()
27+
occurance[c_line] = occurance.get(c_line, dict())
28+
# occurance[line]["first"] = line_match.group(1)
29+
# occurance[line]["second"] = line_match.group(2)
30+
occurance[c_line][code_files] = occurance[c_line].get(code_files, list())
31+
occurance[c_line][code_files].append(line_number)
32+
line_number = line_number + 1
33+
print("matching done")
34+
return occurance
35+
36+
def recursive_customized_match(re_list, search_file_list, cur_dir):
37+
""" The data is parsed from all of the files in the current directory """
38+
occurance = dict()
39+
for code_files in glob.iglob('**', root_dir=cur_dir, recursive=True):
40+
for f_type in search_file_list:
41+
if re.search(f_type + '$', code_files):
42+
with open(cur_dir + "/" + code_files, 'r') as cf:
43+
line_number = 1
44+
for line in cf:
45+
for each_re in re_list:
46+
line_match = re.compile(r".*(" + each_re + r").*").match(line)
47+
if line_match is not None:
48+
# c_line = line_match.group(1)
49+
c_line = line.rstrip()
50+
occurance[c_line] = occurance.get(c_line, dict())
51+
# occurance[line]["first"] = line_match.group(1)
52+
# occurance[line]["second"] = line_match.group(2)
53+
occurance[c_line][code_files] = occurance[c_line].get(code_files, list())
54+
occurance[c_line][code_files].append(line_number)
55+
line_number = line_number + 1
56+
print("matching done")
57+
return occurance
58+
59+
def main():
60+
""" A testname argument is required """
61+
parser = argparse.ArgumentParser()
62+
parser.add_argument('--testname', help='the test name')
63+
parser.add_argument('--gitdir', help='the git directory')
64+
args = parser.parse_args()
65+
# testname = "temp_test"
66+
testname = args.testname
67+
git_repo = args.gitdir
68+
if testname is None or git_repo is None:
69+
parser.print_help()
70+
sys.exit(0)
71+
72+
git_hash = git.Repo(git_repo).head.object.hexsha
73+
re_list = ["pragma", "define"]
74+
search_file_list = [r"\.c", r"\.cc"]
75+
# occ = recursive_c_directive_match(re_list, search_file_list, git_repo)
76+
77+
# with open(git_hash + '.pickle', 'wb') as handle:
78+
# pickle.dump(occ, handle, protocol=pickle.HIGHEST_PROTOCOL)
79+
80+
# re_list = [r"OMP PARALLEL", r"vol=0\.0", r"\w+=\d+\.\d+"]
81+
search_file_list = [r"\.c", r"\.cc", r"\.f90"]
82+
recursive_customized_match(re_list, search_file_list, git_repo)
83+
84+
# with open(git_hash + '.pickle', 'rb') as handle:
85+
# b = pickle.load(handle)
86+
# print(b)
87+
88+
if __name__ == '__main__':
89+
main()
90+

0 commit comments

Comments
 (0)