Skip to content

Commit 29073e5

Browse files
committed
Add benchmark workflow and update documentation
Introduces a GitHub Actions workflow for running JMH benchmarks and reporting results. Updates README and CLAUDE.md with expanded usage, installation, and architecture details. Removes deprecated files and improves Maven dependency management. Adds new event listener and S3 file access abstraction for extensibility and performance.
1 parent 26b1d4a commit 29073e5

83 files changed

Lines changed: 2522 additions & 3140 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.editorconfig

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ ij_hocon_spaces_within_braces = false
2929
ij_hocon_spaces_within_brackets = false
3030
ij_hocon_spaces_within_method_call_parentheses = false
3131

32+
# noinspection EditorConfigKeyCorrectness
3233
[*.java]
3334
ij_java_align_consecutive_assignments = false
3435
ij_java_align_consecutive_variable_declarations = false
@@ -72,6 +73,7 @@ ij_java_blank_lines_around_method = 1
7273
ij_java_blank_lines_around_method_in_interface = 1
7374
ij_java_blank_lines_before_class_end = 0
7475
ij_java_blank_lines_before_imports = 1
76+
# noinspection EditorConfigKeyCorrectness
7577
ij_java_blank_lines_before_method_body = 0
7678
ij_java_blank_lines_before_package = 0
7779
ij_java_block_brace_style = end_of_line
@@ -677,7 +679,7 @@ ij_groovy_while_brace_force = never
677679
ij_groovy_while_on_new_line = false
678680
ij_groovy_wrap_long_lines = false
679681

680-
[{*.gradle.kts,*.kt,*.kts,*.main.kts}]
682+
[{*.kt,*.kts}]
681683
indent_size = 4
682684
tab_width = 4
683685
ij_continuation_indent_size = 8

.github/workflows/benchmark.yml

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
name: Benchmarks
2+
3+
on:
4+
push:
5+
branches: [ main ]
6+
pull_request:
7+
branches: [ main ]
8+
9+
jobs:
10+
benchmark:
11+
runs-on: ubuntu-latest
12+
13+
steps:
14+
- uses: actions/checkout@v4
15+
16+
- name: Set up JDK 17
17+
uses: actions/setup-java@v4
18+
with:
19+
java-version: '17'
20+
distribution: 'temurin'
21+
cache: maven
22+
23+
- name: Run benchmarks
24+
run: |
25+
./mvnw clean test-compile exec:java -P benchmark -Djmh.options="-wi 2 -i 3 -f 1 -rf json -rff benchmark-results.json"
26+
27+
- name: Process benchmark results
28+
if: always()
29+
run: |
30+
if [ -f benchmark-results.json ]; then
31+
echo "## Benchmark Results" >> $GITHUB_STEP_SUMMARY
32+
echo '```' >> $GITHUB_STEP_SUMMARY
33+
34+
# Extract and display results with jq
35+
jq -r '.[] | "\(.benchmark): \(.primaryMetric.score) ± \(.primaryMetric.scoreError) \(.primaryMetric.scoreUnit)"' benchmark-results.json >> $GITHUB_STEP_SUMMARY
36+
37+
echo '```' >> $GITHUB_STEP_SUMMARY
38+
39+
# Check for performance regressions (fail if any benchmark takes >50ms)
40+
FAILED=0
41+
while IFS= read -r line; do
42+
SCORE=$(echo "$line" | jq -r '.primaryMetric.score')
43+
BENCHMARK=$(echo "$line" | jq -r '.benchmark')
44+
45+
# Convert to integer for comparison (assuming milliseconds)
46+
SCORE_INT=$(echo "$SCORE" | cut -d. -f1)
47+
48+
if [ "$SCORE_INT" -gt 50 ]; then
49+
echo "❌ Performance regression detected in $BENCHMARK: ${SCORE}ms exceeds 50ms threshold" >> $GITHUB_STEP_SUMMARY
50+
FAILED=1
51+
fi
52+
done < <(jq -c '.[]' benchmark-results.json)
53+
54+
if [ "$FAILED" -eq 1 ]; then
55+
echo "::error::Performance regression detected - one or more benchmarks exceeded the 50ms threshold"
56+
exit 1
57+
else
58+
echo "✅ All benchmarks passed performance thresholds" >> $GITHUB_STEP_SUMMARY
59+
fi
60+
else
61+
echo "::error::No benchmark results found"
62+
exit 1
63+
fi
64+
65+
- name: Upload benchmark results
66+
if: always()
67+
uses: actions/upload-artifact@v4
68+
with:
69+
name: benchmark-results
70+
path: benchmark-results.json
71+
retention-days: 30
72+
73+
- name: Comment PR with benchmark results
74+
if: github.event_name == 'pull_request' && always()
75+
uses: actions/github-script@v7
76+
with:
77+
script: |
78+
const fs = require('fs');
79+
80+
if (!fs.existsSync('benchmark-results.json')) {
81+
console.log('No benchmark results found');
82+
return;
83+
}
84+
85+
const results = JSON.parse(fs.readFileSync('benchmark-results.json', 'utf8'));
86+
87+
let comment = '## 📊 Benchmark Results\n\n';
88+
comment += '| Benchmark | Score | Error | Unit |\n';
89+
comment += '|-----------|-------|-------|------|\n';
90+
91+
let hasRegression = false;
92+
93+
results.forEach(result => {
94+
const score = result.primaryMetric.score;
95+
const error = result.primaryMetric.scoreError;
96+
const unit = result.primaryMetric.scoreUnit;
97+
const benchmark = result.benchmark.split('.').pop();
98+
99+
// Check if score exceeds threshold
100+
const emoji = score > 50 ? '🔴' : '🟢';
101+
if (score > 50) hasRegression = true;
102+
103+
comment += `| ${emoji} ${benchmark} | ${score.toFixed(3)} | ±${error.toFixed(3)} | ${unit} |\n`;
104+
});
105+
106+
if (hasRegression) {
107+
comment += '\n⚠️ **Performance regression detected**: One or more benchmarks exceeded the 50ms threshold.\n';
108+
} else {
109+
comment += '\n✅ **All benchmarks passed** performance thresholds.\n';
110+
}
111+
112+
// Find and update or create comment
113+
const { data: comments } = await github.rest.issues.listComments({
114+
owner: context.repo.owner,
115+
repo: context.repo.repo,
116+
issue_number: context.issue.number,
117+
});
118+
119+
const botComment = comments.find(comment =>
120+
comment.user.type === 'Bot' && comment.body.includes('📊 Benchmark Results')
121+
);
122+
123+
if (botComment) {
124+
await github.rest.issues.updateComment({
125+
owner: context.repo.owner,
126+
repo: context.repo.repo,
127+
comment_id: botComment.id,
128+
body: comment
129+
});
130+
} else {
131+
await github.rest.issues.createComment({
132+
owner: context.repo.owner,
133+
repo: context.repo.repo,
134+
issue_number: context.issue.number,
135+
body: comment
136+
});
137+
}

.idea/inspectionProfiles/Project_Default.xml

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

CLAUDE.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,24 @@ This is a Java library for parsing and validating eLearning module manifests in
4343
# Run with code coverage
4444
./mvnw test jacoco:report
4545
# Coverage report: target/site/jacoco/index.html
46+
47+
# Run benchmarks (compiles but skips unit tests)
48+
./mvnw clean test-compile exec:java -P benchmark
49+
50+
# Run benchmarks with custom JMH options
51+
./mvnw clean test-compile exec:java -P benchmark -Djmh.options="-wi 3 -i 5 -f 1"
52+
53+
# List available benchmarks
54+
./mvnw clean test-compile exec:java -P benchmark -Djmh.options="-l"
55+
56+
# Run specific benchmark
57+
./mvnw clean test-compile exec:java -P benchmark -Djmh.options="Scorm12Benchmark"
58+
59+
# Run benchmarks with more iterations for stable results
60+
./mvnw clean test-compile exec:java -P benchmark -Djmh.options="-wi 5 -i 10 -f 2"
61+
62+
# Run benchmarks without forking (useful for debugging)
63+
./mvnw clean test-compile exec:java -P benchmark -Djmh.options="-f 0"
4664
```
4765

4866
## Architecture

0 commit comments

Comments
 (0)