better API safety #548
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
name: bench | |
on: | |
push: | |
branches: | |
- main | |
workflow_dispatch: | |
inputs: | |
selector: | |
description: 'Benchmark selector' | |
required: false | |
default: 'Kyo' | |
warmupIterations: | |
description: 'Warmup iterations' | |
required: false | |
default: '13' | |
measurementIterations: | |
description: 'Measurement iterations' | |
required: false | |
default: '5' | |
iterationTime: | |
description: 'Iteration time' | |
required: false | |
default: '1' | |
forks: | |
description: 'Number of forks' | |
required: false | |
default: '1' | |
threads: | |
description: 'Number of threads' | |
required: false | |
default: '1' | |
profileEvents: | |
description: 'Profile events' | |
required: false | |
default: 'alloc,cpu' | |
jobs: | |
bench: | |
name: bench | |
runs-on: bench | |
timeout-minutes: 90 | |
env: | |
SE: ${{ github.event.inputs.selector || 'Kyo' }} | |
WI: ${{ github.event.inputs.warmupIterations || '13' }} | |
MI: ${{ github.event.inputs.measurementIterations || '5' }} | |
IT: ${{ github.event.inputs.iterationTime || '1' }} | |
FK: ${{ github.event.inputs.forks || '1' }} | |
TH: ${{ github.event.inputs.threads || '1' }} | |
PE: ${{ github.event.inputs.profileEvents || 'alloc,cpu' }} | |
steps: | |
- uses: actions/[email protected] | |
with: | |
fetch-depth: 0 | |
- uses: olafurpg/setup-scala@v13 | |
with: | |
java-version: [email protected] | |
- name: install async-profiler | |
run: | | |
cd /home/runner | |
wget https://github.com/jvm-profiling-tools/async-profiler/releases/download/v2.9/async-profiler-2.9-linux-x64.tar.gz | |
tar -xvzf async-profiler-2.9-linux-x64.tar.gz | |
sudo mkdir -p /usr/java/packages/lib/ | |
sudo cp async-profiler-2.9-linux-x64/build/libasyncProfiler.so /usr/java/packages/lib/ | |
sudo sysctl kernel.perf_event_paranoid=1 | |
sudo sysctl kernel.kptr_restrict=0 | |
- name: run | |
run: | | |
IFS=', ' read -r -a array <<< "$PE" | |
for EVENT in "${array[@]}" | |
do | |
ESCAPED_EVENT=$(printf '%q' "$EVENT") | |
sbt "kyo-bench/jmh:clean;kyo-bench/jmh:run -wi $WI -i $MI -r $IT -w $IT -f $FK -t $TH -foe true -prof \"async:event=$ESCAPED_EVENT;output=flamegraph\" $SE" | |
done | |
sbt "kyo-bench/jmh:clean;kyo-bench/jmh:run -wi $WI -i $MI -r $IT -w $IT -f $FK -t $TH -foe true -prof gc -rf json $SE" | |
- name: prepare results | |
run: | | |
short_sha=$(echo "${{ github.sha }}" | cut -c 1-7) | |
mkdir -p output/ | |
cp -r kyo-bench/.jvm/jmh-result.json output/#${short_sha}-jmh-result.json | |
cp -r kyo-bench/.jvm/kyo.bench.* output/ | |
rm -rf output/**/*reverse.html | |
for file in $(find output -name "*.html"); do | |
newfile=$(echo $file | sed 's/\([^.]*\)-.*flame-\([^-]*\)-forward.html/\1-\2.html/') | |
if [ "$file" != "$newfile" ]; then | |
mv "$file" "$newfile" | |
fi | |
done | |
for file in $(find output -name "*.html"); do | |
new_name="${short_sha}-$(basename "$file")" | |
mv "$file" "$(dirname "$file")/$new_name" | |
done | |
ls output/ | |
- name: publish results | |
id: publish-results | |
uses: actions/github-script@v5 | |
with: | |
github-token: ${{ secrets.GIST_PAT }} | |
script: | | |
const fs = require('fs').promises; | |
const path = require('path'); | |
async function getFiles(dir) { | |
const dirents = await fs.readdir(dir, { withFileTypes: true }); | |
const files = await Promise.all(dirents.map((dirent) => { | |
const res = path.resolve(dir, dirent.name); | |
return dirent.isDirectory() ? getFiles(res) : res; | |
})); | |
return Array.prototype.concat(...files); | |
} | |
let gistFiles = {}; | |
const files = await getFiles('output/'); | |
for (const file of files) { | |
const content = await fs.readFile(file, 'utf-8'); | |
const relativePath = path.relative('output/', file); | |
let gistFileName = relativePath.split(path.sep).join('-'); | |
gistFiles[gistFileName] = { content: content }; | |
} | |
const gist = await github.rest.gists.create({ | |
files: gistFiles, | |
public: true | |
}); | |
gistUrl = gist.data.html_url; | |
fs.appendFile(process.env.GITHUB_ENV, `GIST_URL=${gistUrl}\n`, 'utf8'); | |
- name: Check Build Cache for Index URL | |
id: check-cache | |
uses: actions/cache@v2 | |
with: | |
path: jmh-github-action-index.txt | |
key: index-url | |
- name: Get commit message | |
id: get-commit-message | |
run: | | |
COMMIT_MESSAGE="$(git log --format=%B -n 1 ${{ github.sha }})" | |
echo "COMMIT_MESSAGE=${COMMIT_MESSAGE}" >> $GITHUB_ENV | |
- name: Update Index | |
id: update-index | |
uses: actions/github-script@v5 | |
with: | |
github-token: ${{ secrets.GIST_PAT }} | |
script: | | |
const indexFileName = 'jmh-github-action.csv'; | |
const runUrl = `https://github.com/${process.env.GITHUB_REPOSITORY}/actions/runs/${process.env.GITHUB_RUN_ID}`; | |
const commitUrl = `https://github.com/${process.env.GITHUB_REPOSITORY}/commit/${context.sha}`; | |
const commitMessage = process.env.COMMIT_MESSAGE.replace(/,/g, '\\,'); | |
const selector = process.env.SE; | |
const currentDateTime = new Date(); | |
const gistUrl = process.env.GIST_URL; | |
const timestamp = currentDateTime.toLocaleString('en-US', { | |
year: 'numeric', | |
month: '2-digit', | |
day: '2-digit', | |
hour: '2-digit', | |
minute: '2-digit', | |
hour12: false | |
}).replace(',', ''); | |
let indexGist; | |
let indexUrl = core.getState('index_url'); | |
async function getGists(page = 1, perPage = 100) { | |
const { data: gistList } = await github.rest.gists.list({ page, per_page: perPage }); | |
return gistList; | |
} | |
async function findIndexGist() { | |
let page = 1; | |
let gists; | |
do { | |
gists = await getGists(page); | |
indexGist = gists.find(gist => gist.files[indexFileName]); | |
page++; | |
} while (!indexGist && gists.length > 0); | |
return indexGist; | |
} | |
const https = require('https'); | |
async function updateIndexGist() { | |
if (indexGist) { | |
const indexFile = indexGist.files[indexFileName]; | |
let fileContent = ''; | |
if (indexFile && indexFile.raw_url) { | |
fileContent = await new Promise((resolve, reject) => { | |
https.get(indexFile.raw_url, (res) => { | |
let data = ''; | |
res.on('data', (chunk) => { | |
data += chunk; | |
}); | |
res.on('end', () => { | |
resolve(data); | |
}); | |
}).on('error', (err) => { | |
console.log("Error: " + err.message); | |
reject(err); | |
}); | |
}); | |
} | |
const newIndexContent = (fileContent ? fileContent + "\n" : "") + `${commitUrl},${commitMessage},${selector},${runUrl},${gistUrl}`; | |
await github.rest.gists.update({ | |
gist_id: indexGist.id, | |
files: { | |
[indexFileName]: { | |
content: newIndexContent | |
} | |
} | |
}); | |
} else { | |
const header = "commit_url,commit_message,selector,run,gist"; | |
const newIndexContent = `${header}\n${commitUrl},${commitMessage},${selector},${runUrl},${gistUrl}`; | |
const newGist = await github.rest.gists.create({ | |
files: { | |
[indexFileName]: { | |
content: newIndexContent | |
} | |
}, | |
public: true | |
}); | |
indexUrl = newGist.data.html_url; | |
core.saveState('index_url', indexUrl); | |
} | |
} | |
if (indexUrl) { | |
const indexGistId = indexUrl.split('/').pop(); | |
indexGist = await github.rest.gists.get({ gist_id: indexGistId }); | |
} | |
if (indexGist) { | |
await updateIndexGist(); | |
} else { | |
indexGist = await findIndexGist(); | |
await updateIndexGist(); | |
} |