Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add initial benchmarking setup #3120

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
78 changes: 78 additions & 0 deletions .github/workflows/benchmarks.yaml
@@ -0,0 +1,78 @@
name: Benchmarks
on:
pull_request_target:
branches:
- master

concurrency:
group: ${{ format('{0}-{1}', github.workflow_ref, github.head_ref) }}
cancel-in-progress: true

jobs:
benchmarks:
if: github.repository_owner == 'mybatis'
permissions:
contents: read
pull-requests: write # for benchmark comment
runs-on: ubuntu-latest
env:
COMMENT_FILE: ${{ github.workspace }}/benchmark-comment.md
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
steps:
- id: get-sha
run: |
# getting exact master sha at the moment to reference it in the comment
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"

- uses: actions/checkout@v4
with:
ref: ${{ steps.get-sha.outputs.sha }}
path: benchmark-master

- uses: actions/checkout@v4
with:
path: benchmark-pull-request

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI we also started using pull_request_target recently and one difference we found is that for this event, the ref has to be explicit:
https://github.com/Breus/json-masker/blob/master/.github/workflows/benchmark.yml#L34

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for this! ill update :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shamelessly added all of your updates in here ;-)


- name: Set up JDK
uses: actions/setup-java@v4
with:
java-version: 21
distribution: zulu

- name: Run benchmarks (pull-request)
working-directory: benchmark-pull-request
run: |
./mvnw -B -V --no-transfer-progress jmh:benchmark -Dlicense.skip=true

echo '> [!NOTE]' >> ${{ env.COMMENT_FILE }}
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 }}
echo '### Benchmark results (pull-request, ${{ github.event.pull_request.head.sha }})' >> ${{ env.COMMENT_FILE }}
echo '```text' >> ${{ env.COMMENT_FILE }}
cat target/benchmark-results.txt >> ${{ env.COMMENT_FILE }}
echo '```' >> ${{ env.COMMENT_FILE }}

- name: Run benchmarks (master)
working-directory: benchmark-master
run: |
./mvnw -B -V --no-transfer-progress jmh:benchmark -Dlicense.skip=true

echo '### Benchmark results (${{ github.base_ref }}, ${{ steps.get-sha.outputs.sha }})' >> ${{ env.COMMENT_FILE }}
echo '```text' >> ${{ env.COMMENT_FILE }}
cat target/benchmark-results.txt >> ${{ env.COMMENT_FILE }}
echo '```' >> ${{ env.COMMENT_FILE }}

- name: Find benchmark results comment
uses: peter-evans/find-comment@v3
id: benchmark-comment
with:
issue-number: ${{ github.event.pull_request.number }}
comment-author: 'github-actions[bot]'
body-includes: Benchmark results

- name: Create or update comment
uses: peter-evans/create-or-update-comment@v4
with:
comment-id: ${{ steps.benchmark-comment.outputs.comment-id }}
issue-number: ${{ github.event.pull_request.number }}
body-path: ${{ env.COMMENT_FILE }}
edit-mode: replace
27 changes: 27 additions & 0 deletions pom.xml
Expand Up @@ -137,6 +137,8 @@
<mockito.version>5.11.0</mockito.version>
<mssql-jdbc.version>12.6.1.jre11</mssql-jdbc.version>
<testcontainers.version>1.19.7</testcontainers.version>
<jmh.version>1.37</jmh.version>
<jmh.plugin.version>0.2.2</jmh.plugin.version>

<!-- Add slow test groups here and annotate classes similar to @Tag('groupName'). -->
<!-- Excluded groups are ran on github ci, to force here, pass -d"excludedGroups=" -->
Expand Down Expand Up @@ -351,6 +353,20 @@
<version>${log4j.version}</version>
<scope>test</scope>
</dependency>

<!-- Benchmarking support -->
<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-core</artifactId>
<version>${jmh.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-generator-annprocess</artifactId>
<version>${jmh.version}</version>
<scope>test</scope>
</dependency>
</dependencies>

<build>
Expand Down Expand Up @@ -455,6 +471,17 @@
</excludes>
</configuration>
</plugin>
<plugin>
<groupId>pw.krejci</groupId>
<artifactId>jmh-maven-plugin</artifactId>
<version>${jmh.plugin.version}</version>
<configuration>
<resultsFile>${project.build.directory}/benchmark-results.txt</resultsFile>
<resultFormat>text</resultFormat>
<warmupIterations>1</warmupIterations>
<profiler>gc</profiler>
</configuration>
</plugin>
</plugins>
</build>

Expand Down
@@ -0,0 +1,93 @@
/*
* Copyright 2009-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.ibatis.benchmarks.jmh.basic;

import java.util.concurrent.TimeUnit;

import javax.sql.DataSource;

import org.apache.ibatis.BaseDataTest;
import org.apache.ibatis.binding.BoundAuthorMapper;
import org.apache.ibatis.binding.BoundBlogMapper;
import org.apache.ibatis.domain.blog.Author;
import org.apache.ibatis.domain.blog.Blog;
import org.apache.ibatis.domain.blog.Post;
import org.apache.ibatis.mapping.Environment;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.apache.ibatis.transaction.TransactionFactory;
import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.Warmup;

@Fork(1)
@Warmup(iterations = 1)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
public class BasicBlogBenchmark {

@State(Scope.Benchmark)
public static class SessionFactoryState {

private SqlSessionFactory sqlSessionFactory;

@Setup
public void setup() throws Exception {
DataSource dataSource = BaseDataTest.createBlogDataSource();
BaseDataTest.runScript(dataSource, BaseDataTest.BLOG_DDL);
BaseDataTest.runScript(dataSource, BaseDataTest.BLOG_DATA);

TransactionFactory transactionFactory = new JdbcTransactionFactory();
Environment environment = new Environment("Production", transactionFactory, dataSource);
Configuration configuration = new Configuration(environment);
configuration.getTypeAliasRegistry().registerAlias(Blog.class);
configuration.getTypeAliasRegistry().registerAlias(Post.class);
configuration.getTypeAliasRegistry().registerAlias(Author.class);
configuration.addMapper(BoundBlogMapper.class);
configuration.addMapper(BoundAuthorMapper.class);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration);
}

public SqlSessionFactory getSqlSessionFactory() {
return sqlSessionFactory;
}
}

@Benchmark
public Blog retrieveSingleBlogUsingConstructorWithResultMap(SessionFactoryState sessionFactoryState) {
try (SqlSession sqlSession = sessionFactoryState.getSqlSessionFactory().openSession()) {
final BoundBlogMapper mapper = sqlSession.getMapper(BoundBlogMapper.class);
return mapper.selectBlogUsingConstructorWithResultMap(1);
}
}

@Benchmark
public Blog retrieveSingleBlog(SessionFactoryState sessionFactoryState) {
try (SqlSession sqlSession = sessionFactoryState.getSqlSessionFactory().openSession()) {
final BoundBlogMapper mapper = sqlSession.getMapper(BoundBlogMapper.class);
return mapper.selectBlog(1);
}
}
}