Skip to content

Commit 7a36385

Browse files
author
willie
committed
feat: Add initial benchmarking setup
Will execute on pull requests, and run all benchmarks against master and post a comparison as a PR comment
1 parent d43373d commit 7a36385

File tree

3 files changed

+198
-0
lines changed

3 files changed

+198
-0
lines changed

.github/workflows/benchmarks.yaml

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
name: Benchmarks
2+
on:
3+
pull_request:
4+
branches:
5+
- master
6+
7+
concurrency:
8+
group: ${{ format('{0}-{1}', github.workflow_ref, github.head_ref) }}
9+
cancel-in-progress: true
10+
11+
jobs:
12+
benchmarks:
13+
if: github.repository_owner == 'mybatis'
14+
permissions:
15+
contents: read
16+
pull-requests: write # for benchmark comment
17+
runs-on: ubuntu-latest
18+
env:
19+
COMMENT_FILE: ${{ github.workspace }}/benchmark-comment.md
20+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
21+
steps:
22+
- id: get-sha
23+
run: |
24+
# getting exact master sha at the moment to reference it in the comment
25+
echo "sha=$( curl -u "u:${{ github.token }}" https://api.github.com/repos/${{ github.repository }}/git/ref/heads/${{ github.base_ref }} | jq .object.sha | tr -d '"' )" >> "$GITHUB_OUTPUT"
26+
27+
- uses: actions/checkout@v4
28+
with:
29+
ref: ${{ steps.get-sha.outputs.sha }}
30+
path: benchmark-master
31+
32+
- uses: actions/checkout@v4
33+
with:
34+
path: benchmark-pull-request
35+
36+
- name: Set up JDK
37+
uses: actions/setup-java@v4
38+
with:
39+
java-version: 21
40+
distribution: zulu
41+
42+
- name: Run benchmarks (pull-request)
43+
working-directory: benchmark-pull-request
44+
run: |
45+
./mvnw jmh:benchmark -Dlicense.skip=true
46+
47+
echo '> [!NOTE]' >> ${{ env.COMMENT_FILE }}
48+
echo '> These results are affected by shared workloads on GitHub runners. Use the results only to detect possible regressions, but always rerun on more stable machine before making any conclusions!' >> ${{ env.COMMENT_FILE }}
49+
echo '### Benchmark results (pull-request, ${{ github.event.pull_request.head.sha }})' >> ${{ env.COMMENT_FILE }}
50+
echo '```text' >> ${{ env.COMMENT_FILE }}
51+
cat target/benchmark-results.txt >> ${{ env.COMMENT_FILE }}
52+
echo '```' >> ${{ env.COMMENT_FILE }}
53+
54+
# todo: to be uncomented after this is on master
55+
# - name: Run benchmarks (master)
56+
# working-directory: benchmark-master
57+
# run: |
58+
# ./mvnw jmh:benchmark -Dlicense.skip=true
59+
#
60+
# echo '### Benchmark results (${{ github.base_ref }}, ${{ steps.get-sha.outputs.sha }})' >> ${{ env.COMMENT_FILE }}
61+
# echo '```text' >> ${{ env.COMMENT_FILE }}
62+
# cat target/benchmark-results.txt >> ${{ env.COMMENT_FILE }}
63+
# echo '```' >> ${{ env.COMMENT_FILE }}
64+
65+
- name: Find benchmark results comment
66+
uses: peter-evans/find-comment@v3
67+
id: benchmark-comment
68+
with:
69+
issue-number: ${{ github.event.pull_request.number }}
70+
comment-author: 'github-actions[bot]'
71+
body-includes: Benchmark results
72+
73+
- name: Create or update comment
74+
uses: peter-evans/create-or-update-comment@v4
75+
with:
76+
comment-id: ${{ steps.benchmark-comment.outputs.comment-id }}
77+
issue-number: ${{ github.event.pull_request.number }}
78+
body-path: ${{ env.COMMENT_FILE }}
79+
edit-mode: replace

pom.xml

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,8 @@
137137
<mockito.version>5.11.0</mockito.version>
138138
<mssql-jdbc.version>12.6.1.jre11</mssql-jdbc.version>
139139
<testcontainers.version>1.19.7</testcontainers.version>
140+
<jmh.version>1.37</jmh.version>
141+
<jmh.plugin.version>0.2.2</jmh.plugin.version>
140142

141143
<!-- Add slow test groups here and annotate classes similar to @Tag('groupName'). -->
142144
<!-- Excluded groups are ran on github ci, to force here, pass -d"excludedGroups=" -->
@@ -351,6 +353,20 @@
351353
<version>${log4j.version}</version>
352354
<scope>test</scope>
353355
</dependency>
356+
357+
<!-- Benchmarking support -->
358+
<dependency>
359+
<groupId>org.openjdk.jmh</groupId>
360+
<artifactId>jmh-core</artifactId>
361+
<version>${jmh.version}</version>
362+
<scope>test</scope>
363+
</dependency>
364+
<dependency>
365+
<groupId>org.openjdk.jmh</groupId>
366+
<artifactId>jmh-generator-annprocess</artifactId>
367+
<version>${jmh.version}</version>
368+
<scope>test</scope>
369+
</dependency>
354370
</dependencies>
355371

356372
<build>
@@ -455,6 +471,16 @@
455471
</excludes>
456472
</configuration>
457473
</plugin>
474+
<plugin>
475+
<groupId>pw.krejci</groupId>
476+
<artifactId>jmh-maven-plugin</artifactId>
477+
<version>${jmh.plugin.version}</version>
478+
<configuration>
479+
<resultsFile>${project.build.directory}/benchmark-results.txt</resultsFile>
480+
<resultFormat>text</resultFormat>
481+
<warmupIterations>1</warmupIterations>
482+
</configuration>
483+
</plugin>
458484
</plugins>
459485
</build>
460486

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
/*
2+
* Copyright 2009-2024 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.apache.ibatis.benchmarks.jmh.basic;
17+
18+
import java.util.concurrent.TimeUnit;
19+
20+
import javax.sql.DataSource;
21+
22+
import org.apache.ibatis.BaseDataTest;
23+
import org.apache.ibatis.binding.BoundAuthorMapper;
24+
import org.apache.ibatis.binding.BoundBlogMapper;
25+
import org.apache.ibatis.domain.blog.Author;
26+
import org.apache.ibatis.domain.blog.Blog;
27+
import org.apache.ibatis.domain.blog.Post;
28+
import org.apache.ibatis.mapping.Environment;
29+
import org.apache.ibatis.session.Configuration;
30+
import org.apache.ibatis.session.SqlSession;
31+
import org.apache.ibatis.session.SqlSessionFactory;
32+
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
33+
import org.apache.ibatis.transaction.TransactionFactory;
34+
import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory;
35+
import org.openjdk.jmh.annotations.Benchmark;
36+
import org.openjdk.jmh.annotations.BenchmarkMode;
37+
import org.openjdk.jmh.annotations.Fork;
38+
import org.openjdk.jmh.annotations.Mode;
39+
import org.openjdk.jmh.annotations.OutputTimeUnit;
40+
import org.openjdk.jmh.annotations.Scope;
41+
import org.openjdk.jmh.annotations.Setup;
42+
import org.openjdk.jmh.annotations.State;
43+
import org.openjdk.jmh.annotations.Warmup;
44+
45+
@Fork(1)
46+
@Warmup(iterations = 1)
47+
@BenchmarkMode(Mode.AverageTime)
48+
@OutputTimeUnit(TimeUnit.MICROSECONDS)
49+
public class BasicBlogBenchmark {
50+
51+
@State(Scope.Benchmark)
52+
public static class SessionFactoryState {
53+
54+
private SqlSessionFactory sqlSessionFactory;
55+
56+
@Setup
57+
public void setup() throws Exception {
58+
DataSource dataSource = BaseDataTest.createBlogDataSource();
59+
BaseDataTest.runScript(dataSource, BaseDataTest.BLOG_DDL);
60+
BaseDataTest.runScript(dataSource, BaseDataTest.BLOG_DATA);
61+
62+
TransactionFactory transactionFactory = new JdbcTransactionFactory();
63+
Environment environment = new Environment("Production", transactionFactory, dataSource);
64+
Configuration configuration = new Configuration(environment);
65+
configuration.getTypeAliasRegistry().registerAlias(Blog.class);
66+
configuration.getTypeAliasRegistry().registerAlias(Post.class);
67+
configuration.getTypeAliasRegistry().registerAlias(Author.class);
68+
configuration.addMapper(BoundBlogMapper.class);
69+
configuration.addMapper(BoundAuthorMapper.class);
70+
sqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration);
71+
}
72+
73+
public SqlSessionFactory getSqlSessionFactory() {
74+
return sqlSessionFactory;
75+
}
76+
}
77+
78+
@Benchmark
79+
public Blog retrieveSingleBlogUsingConstructorWithResultMap(SessionFactoryState sessionFactoryState) {
80+
try (SqlSession sqlSession = sessionFactoryState.getSqlSessionFactory().openSession()) {
81+
final BoundBlogMapper mapper = sqlSession.getMapper(BoundBlogMapper.class);
82+
return mapper.selectBlogUsingConstructorWithResultMap(1);
83+
}
84+
}
85+
86+
@Benchmark
87+
public Blog retrieveSingleBlog(SessionFactoryState sessionFactoryState) {
88+
try (SqlSession sqlSession = sessionFactoryState.getSqlSessionFactory().openSession()) {
89+
final BoundBlogMapper mapper = sqlSession.getMapper(BoundBlogMapper.class);
90+
return mapper.selectBlog(1);
91+
}
92+
}
93+
}

0 commit comments

Comments
 (0)