From 45e2e125df2600de1b66a1c1ca55effef55ae59a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 24 Jul 2026 12:09:53 +0000 Subject: [PATCH 01/39] Bump org.slf4j:slf4j-api from 2.0.16 to 2.0.18 Bumps org.slf4j:slf4j-api from 2.0.16 to 2.0.18. --- updated-dependencies: - dependency-name: org.slf4j:slf4j-api dependency-version: 2.0.18 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index a7f7675..2df8418 100644 --- a/pom.xml +++ b/pom.xml @@ -83,7 +83,7 @@ UTF-8 - 2.0.16 + 2.0.18 1.5.16 3.7.3 1.37 From 4ea74a2ba33db16a7cf123ecfe0b2ad19aeb7c04 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 24 Jul 2026 12:10:01 +0000 Subject: [PATCH 02/39] Bump reactor.version from 3.7.3 to 3.8.6 Bumps `reactor.version` from 3.7.3 to 3.8.6. Updates `io.projectreactor:reactor-core` from 3.7.3 to 3.8.6 - [Release notes](https://github.com/reactor/reactor-core/releases) - [Commits](https://github.com/reactor/reactor-core/compare/v3.7.3...v3.8.6) Updates `io.projectreactor:reactor-test` from 3.7.3 to 3.8.6 - [Release notes](https://github.com/reactor/reactor-core/releases) - [Commits](https://github.com/reactor/reactor-core/compare/v3.7.3...v3.8.6) --- updated-dependencies: - dependency-name: io.projectreactor:reactor-core dependency-version: 3.8.6 dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: io.projectreactor:reactor-test dependency-version: 3.8.6 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index a7f7675..e93e62e 100644 --- a/pom.xml +++ b/pom.xml @@ -85,7 +85,7 @@ 2.0.16 1.5.16 - 3.7.3 + 3.8.6 1.37 4.1.107.Final 4.5.7 From a84b2baee804c6f055fde4b3dafeee14cd9487ae Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 24 Jul 2026 12:10:06 +0000 Subject: [PATCH 03/39] Bump com.diffplug.spotless:spotless-maven-plugin from 2.44.3 to 3.8.0 Bumps [com.diffplug.spotless:spotless-maven-plugin](https://github.com/diffplug/spotless) from 2.44.3 to 3.8.0. - [Release notes](https://github.com/diffplug/spotless/releases) - [Changelog](https://github.com/diffplug/spotless/blob/main/CHANGES.md) - [Commits](https://github.com/diffplug/spotless/compare/maven/2.44.3...maven/3.8.0) --- updated-dependencies: - dependency-name: com.diffplug.spotless:spotless-maven-plugin dependency-version: 3.8.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index a7f7675..7c0bf39 100644 --- a/pom.xml +++ b/pom.xml @@ -108,7 +108,7 @@ 1.6.13 - 2.44.3 + 3.8.0 3.6.0 10.21.4 3.26.0 From 7872bd16ce400e56253e10195579dfead1814610 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 24 Jul 2026 12:10:28 +0000 Subject: [PATCH 04/39] Bump org.apache.maven.plugins:maven-javadoc-plugin from 3.11.2 to 3.12.0 Bumps [org.apache.maven.plugins:maven-javadoc-plugin](https://github.com/apache/maven-javadoc-plugin) from 3.11.2 to 3.12.0. - [Release notes](https://github.com/apache/maven-javadoc-plugin/releases) - [Commits](https://github.com/apache/maven-javadoc-plugin/compare/maven-javadoc-plugin-3.11.2...maven-javadoc-plugin-3.12.0) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-javadoc-plugin dependency-version: 3.12.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index a7f7675..6dbd178 100644 --- a/pom.xml +++ b/pom.xml @@ -101,7 +101,7 @@ 3.5.3 3.4.2 3.3.1 - 3.11.2 + 3.12.0 3.2.7 3.1.3 3.3.1 From 45416e2b61f7aee5932cb3c766d86ce939d0f3fe Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 24 Jul 2026 12:10:35 +0000 Subject: [PATCH 05/39] Bump org.apache.maven.plugins:maven-surefire-plugin from 3.5.3 to 3.5.6 Bumps [org.apache.maven.plugins:maven-surefire-plugin](https://github.com/apache/maven-surefire) from 3.5.3 to 3.5.6. - [Release notes](https://github.com/apache/maven-surefire/releases) - [Commits](https://github.com/apache/maven-surefire/compare/surefire-3.5.3...surefire-3.5.6) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-surefire-plugin dependency-version: 3.5.6 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index a7f7675..0827a5f 100644 --- a/pom.xml +++ b/pom.xml @@ -97,7 +97,7 @@ 3.14.0 - 3.5.3 + 3.5.6 3.5.3 3.4.2 3.3.1 From 3d7d4bf3bb5a8bcf06d8a74be9898a6a3c1a1642 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 24 Jul 2026 12:10:38 +0000 Subject: [PATCH 06/39] Bump org.apache.maven.plugins:maven-jar-plugin from 3.4.2 to 3.5.1 Bumps [org.apache.maven.plugins:maven-jar-plugin](https://github.com/apache/maven-jar-plugin) from 3.4.2 to 3.5.1. - [Release notes](https://github.com/apache/maven-jar-plugin/releases) - [Commits](https://github.com/apache/maven-jar-plugin/compare/maven-jar-plugin-3.4.2...maven-jar-plugin-3.5.1) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-jar-plugin dependency-version: 3.5.1 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index a7f7675..a41e878 100644 --- a/pom.xml +++ b/pom.xml @@ -99,7 +99,7 @@ 3.14.0 3.5.3 3.5.3 - 3.4.2 + 3.5.1 3.3.1 3.11.2 3.2.7 From 0614627c41d6b34a1da6f43d0d1b3dc5cec165e8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 24 Jul 2026 17:38:02 +0000 Subject: [PATCH 07/39] build(deps): bump pmd-tool.version from 7.16.0 to 7.26.0 Bumps `pmd-tool.version` from 7.16.0 to 7.26.0. Updates `net.sourceforge.pmd:pmd-java` from 7.16.0 to 7.26.0 - [Release notes](https://github.com/pmd/pmd/releases) - [Commits](https://github.com/pmd/pmd/compare/pmd_releases/7.16.0...pmd_releases/7.26.0) Updates `net.sourceforge.pmd:pmd-core` from 7.16.0 to 7.26.0 - [Release notes](https://github.com/pmd/pmd/releases) - [Commits](https://github.com/pmd/pmd/compare/pmd_releases/7.16.0...pmd_releases/7.26.0) --- updated-dependencies: - dependency-name: net.sourceforge.pmd:pmd-core dependency-version: 7.26.0 dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: net.sourceforge.pmd:pmd-java dependency-version: 7.26.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 5a4d78f..3f92be0 100644 --- a/pom.xml +++ b/pom.xml @@ -112,7 +112,7 @@ 3.6.0 10.21.4 3.28.0 - 7.16.0 + 7.26.0 4.10.3.0 0.8.15 From 78af4fc079294ec9083dad4e2a87e5bcfc2f027d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 24 Jul 2026 17:47:37 +0000 Subject: [PATCH 08/39] build(deps-dev): bump org.assertj:assertj-core from 3.27.3 to 3.27.7 Bumps [org.assertj:assertj-core](https://github.com/assertj/assertj) from 3.27.3 to 3.27.7. - [Release notes](https://github.com/assertj/assertj/releases) - [Commits](https://github.com/assertj/assertj/compare/assertj-build-3.27.3...assertj-build-3.27.7) --- updated-dependencies: - dependency-name: org.assertj:assertj-core dependency-version: 3.27.7 dependency-type: direct:development ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 5a4d78f..2c92665 100644 --- a/pom.xml +++ b/pom.xml @@ -93,7 +93,7 @@ 5.11.4 5.15.2 - 3.27.3 + 3.27.7 3.14.0 From 351dd41fe305c1b16679a2a82f98758c29ca10e5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 24 Jul 2026 17:47:40 +0000 Subject: [PATCH 09/39] build(deps): bump crazy-max/ghaction-import-gpg from 6 to 7 Bumps [crazy-max/ghaction-import-gpg](https://github.com/crazy-max/ghaction-import-gpg) from 6 to 7. - [Release notes](https://github.com/crazy-max/ghaction-import-gpg/releases) - [Commits](https://github.com/crazy-max/ghaction-import-gpg/compare/v6...v7) --- updated-dependencies: - dependency-name: crazy-max/ghaction-import-gpg dependency-version: '7' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 1551d4a..a7b8e60 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -29,7 +29,7 @@ jobs: server-password: MAVEN_PASSWORD - name: Import GPG Key - uses: crazy-max/ghaction-import-gpg@v6 + uses: crazy-max/ghaction-import-gpg@v7 with: gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }} From 5e3a7d33108656d5b326fe72b505f7649b1cf06f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 24 Jul 2026 17:49:31 +0000 Subject: [PATCH 10/39] build(deps): bump io.vertx:vertx-core from 4.5.7 to 4.5.27 Bumps [io.vertx:vertx-core](https://github.com/eclipse/vert.x) from 4.5.7 to 4.5.27. - [Commits](https://github.com/eclipse/vert.x/compare/4.5.7...4.5.27) --- updated-dependencies: - dependency-name: io.vertx:vertx-core dependency-version: 4.5.27 dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 2c92665..4308f06 100644 --- a/pom.xml +++ b/pom.xml @@ -88,7 +88,7 @@ 3.7.3 1.37 4.1.107.Final - 4.5.7 + 4.5.27 5.11.4 From 044f522226ce3084bc8bd5af1e1ca99c07d295bc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 24 Jul 2026 17:50:32 +0000 Subject: [PATCH 11/39] build(deps-dev): bump mockito.version from 5.15.2 to 5.23.0 Bumps `mockito.version` from 5.15.2 to 5.23.0. Updates `org.mockito:mockito-core` from 5.15.2 to 5.23.0 - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v5.15.2...v5.23.0) Updates `org.mockito:mockito-junit-jupiter` from 5.15.2 to 5.23.0 - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v5.15.2...v5.23.0) --- updated-dependencies: - dependency-name: org.mockito:mockito-core dependency-version: 5.23.0 dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: org.mockito:mockito-junit-jupiter dependency-version: 5.23.0 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 2c92665..0862575 100644 --- a/pom.xml +++ b/pom.xml @@ -92,7 +92,7 @@ 5.11.4 - 5.15.2 + 5.23.0 3.27.7 From a51f19a90c8b9ebf8f9d0eb96c755117ec21a609 Mon Sep 17 00:00:00 2001 From: NAMAN JAIN Date: Fri, 24 Jul 2026 23:39:46 +0530 Subject: [PATCH 12/39] Update pom --- pom.xml | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/pom.xml b/pom.xml index 4308f06..76f2bcd 100644 --- a/pom.xml +++ b/pom.xml @@ -105,7 +105,7 @@ 3.2.7 3.1.3 3.3.1 - 1.6.13 + 2.44.3 @@ -346,16 +346,15 @@ ${maven-deploy-plugin.version} - + - org.sonatype.plugins - nexus-staging-maven-plugin - ${nexus-staging-maven-plugin.version} + org.sonatype.central + central-publishing-maven-plugin + 0.5.0 true - ossrh - https://s01.oss.sonatype.org/ - true + ossrh + true @@ -599,8 +598,8 @@ maven-gpg-plugin - org.sonatype.plugins - nexus-staging-maven-plugin + org.sonatype.central + central-publishing-maven-plugin From 48816745e8f87626fbc59c6e5f5826d0bb156ee6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 24 Jul 2026 18:11:47 +0000 Subject: [PATCH 13/39] build(deps-dev): bump org.apache.maven.plugins:maven-resources-plugin Bumps [org.apache.maven.plugins:maven-resources-plugin](https://github.com/apache/maven-resources-plugin) from 3.3.1 to 3.5.0. - [Release notes](https://github.com/apache/maven-resources-plugin/releases) - [Commits](https://github.com/apache/maven-resources-plugin/compare/maven-resources-plugin-3.3.1...maven-resources-plugin-3.5.0) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-resources-plugin dependency-version: 3.5.0 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 76f2bcd..fdf1d92 100644 --- a/pom.xml +++ b/pom.xml @@ -104,7 +104,7 @@ 3.11.2 3.2.7 3.1.3 - 3.3.1 + 3.5.0 From 0192bf1b9cc0eb26c54b0d54d29e2b497326aeb6 Mon Sep 17 00:00:00 2001 From: NAMAN JAIN Date: Fri, 24 Jul 2026 23:48:46 +0530 Subject: [PATCH 14/39] fix pom --- pom.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 76f2bcd..f656645 100644 --- a/pom.xml +++ b/pom.xml @@ -327,7 +327,8 @@ ${maven-javadoc-plugin.version} none - --enable-native-access=ALL-UNNAMED + false + false From 2cdcf2343767c14d1592b395285d06997438ee00 Mon Sep 17 00:00:00 2001 From: NAMAN JAIN Date: Fri, 24 Jul 2026 23:55:06 +0530 Subject: [PATCH 15/39] fix release ci --- .github/workflows/release.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 4167a74..c66eca0 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -40,6 +40,7 @@ jobs: MAVEN_PASSWORD: ${{ secrets.MAVEN_PASSWORD }} - name: Create GitHub Release + if: startsWith(github.ref, 'refs/tags/') uses: softprops/action-gh-release@v2 with: generate_release_notes: true From 0ce73d0ec1cf7ac6c159d08f95d35192990e47ed Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 24 Jul 2026 18:26:59 +0000 Subject: [PATCH 16/39] build(deps): bump softprops/action-gh-release from 2 to 3 Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from 2 to 3. - [Release notes](https://github.com/softprops/action-gh-release/releases) - [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md) - [Commits](https://github.com/softprops/action-gh-release/compare/v2...v3) --- updated-dependencies: - dependency-name: softprops/action-gh-release dependency-version: '3' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c66eca0..3210920 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -41,6 +41,6 @@ jobs: - name: Create GitHub Release if: startsWith(github.ref, 'refs/tags/') - uses: softprops/action-gh-release@v2 + uses: softprops/action-gh-release@v3 with: generate_release_notes: true From bcf22287a8375ac7382169292e08770d4227f155 Mon Sep 17 00:00:00 2001 From: NAMAN JAIN Date: Sat, 25 Jul 2026 18:35:56 +0530 Subject: [PATCH 17/39] fix benchmark ci --- .github/workflows/benchmark.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index 6005cbe..109712a 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -41,7 +41,7 @@ jobs: run: ./mvnw clean install -DskipTests --batch-mode --no-transfer-progress - name: Run JMH Benchmarks - run: ./mvnw exec:exec -pl zthread-benchmark -Dexec.executable="java" -Dexec.args="-classpath %classpath org.openjdk.jmh.Main -rf json -rff target/benchmark.json -prof gc" --batch-mode + run: ./mvnw exec:exec -pl zthread-benchmark -Dexec.executable="java" -Dexec.args="-classpath %classpath org.openjdk.jmh.Main (SpscEventBenchmark|S1_S5_ThroughputLatencyBenchmark) -p payloadSize=64 -rf json -rff target/benchmark.json -prof gc -f 0 -wi 1 -i 2 -r 1s" --batch-mode - name: Generate Charts and Report run: | From 78ca7b8a187a112262d5e75ba5bdd0df033ff6d6 Mon Sep 17 00:00:00 2001 From: NAMAN JAIN Date: Sat, 25 Jul 2026 19:05:06 +0530 Subject: [PATCH 18/39] fix 2 bechmark ci parllel matrix workflows --- .github/workflows/benchmark.yml | 78 +++++++++++++++++++++++++-------- 1 file changed, 59 insertions(+), 19 deletions(-) diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index 109712a..072d26d 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -8,8 +8,21 @@ permissions: jobs: benchmark: - name: Run Enterprise Benchmarks + name: Bench ยท ${{ matrix.name }} runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + include: + - name: spsc-queue + filter: "SpscEventBenchmark" + extra_params: "" + - name: scaling-low + filter: "S1_S5_ThroughputLatencyBenchmark" + extra_params: "-p payloadSize=64 -p concurrency=1:1,4:1" + - name: scaling-high + filter: "S1_S5_ThroughputLatencyBenchmark" + extra_params: "-p payloadSize=64 -p concurrency=8:1,16:4,32:8" steps: - name: Checkout @@ -22,10 +35,37 @@ jobs: distribution: 'temurin' cache: maven - - name: Enable perf_events for async-profiler + - name: Build zThread + run: ./mvnw clean install -DskipTests --batch-mode --no-transfer-progress + + - name: Run Benchmarks + timeout-minutes: 10 run: | - sudo sysctl -w kernel.perf_event_paranoid=1 - sudo sysctl -w kernel.kptr_restrict=0 + timeout 300 ./mvnw exec:exec -pl zthread-benchmark -Dexec.executable="java" \ + -Dexec.args="-classpath %classpath \ + --enable-native-access=ALL-UNNAMED -XX:+UseZGC \ + org.openjdk.jmh.Main ${{ matrix.filter }} ${{ matrix.extra_params }} \ + -rf json -rff target/benchmark-${{ matrix.name }}.json -prof gc \ + -f 0 -wi 1 -i 2 -r 1s" --batch-mode || true + if [ ! -s zthread-benchmark/target/benchmark-${{ matrix.name }}.json ]; then + echo "ERROR: benchmark-${{ matrix.name }}.json was not generated" + exit 1 + fi + + - name: Upload Results + uses: actions/upload-artifact@v4 + with: + name: benchmark-${{ matrix.name }} + path: zthread-benchmark/target/benchmark-${{ matrix.name }}.json + + report: + name: Generate Reports + runs-on: ubuntu-latest + needs: benchmark + + steps: + - name: Checkout + uses: actions/checkout@v4 - name: Set up Python uses: actions/setup-python@v5 @@ -33,36 +73,36 @@ jobs: python-version: '3.12' - name: Install Charting Dependencies - run: | - python -m pip install --upgrade pip - pip install matplotlib pandas seaborn + run: pip install matplotlib pandas seaborn - - name: Build zThread - run: ./mvnw clean install -DskipTests --batch-mode --no-transfer-progress - - - name: Run JMH Benchmarks - run: ./mvnw exec:exec -pl zthread-benchmark -Dexec.executable="java" -Dexec.args="-classpath %classpath org.openjdk.jmh.Main (SpscEventBenchmark|S1_S5_ThroughputLatencyBenchmark) -p payloadSize=64 -rf json -rff target/benchmark.json -prof gc -f 0 -wi 1 -i 2 -r 1s" --batch-mode + - name: Download All Results + uses: actions/download-artifact@v4 + with: + path: benchmark-results + pattern: benchmark-* + merge-multiple: true - - name: Generate Charts and Report + - name: Merge & Generate Charts run: | - # Generate benchmark report inside zthread-benchmark + mkdir -p zthread-benchmark/target + jq -s 'add' benchmark-results/*.json > zthread-benchmark/target/benchmark.json + echo "Merged $(jq length zthread-benchmark/target/benchmark.json) benchmark entries" + cd zthread-benchmark python scripts/generate_charts.py target/benchmark.json - - # Generate main README.md graph and update table cd .. python scripts/generate-graph.py zthread-benchmark/target/benchmark.json - - name: Commit Updated Benchmark Data + - name: Commit Updated Data uses: stefanzweifel/git-auto-commit-action@v5 with: commit_message: "docs(benchmark): auto-update performance charts and README" file_pattern: "README.md assets/benchmark_graph.svg zthread-benchmark/charts/* zthread-benchmark/benchmark.md" - - name: Upload Benchmark Results + - name: Upload Final Report uses: actions/upload-artifact@v4 with: - name: benchmark-results + name: benchmark-report path: | zthread-benchmark/target/benchmark.json zthread-benchmark/charts/ From 8cabe2cd1add87b04ab208782a7989c331931d9f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 25 Jul 2026 13:37:03 +0000 Subject: [PATCH 19/39] build(deps): bump actions/setup-python from 5 to 7 Bumps [actions/setup-python](https://github.com/actions/setup-python) from 5 to 7. - [Release notes](https://github.com/actions/setup-python/releases) - [Commits](https://github.com/actions/setup-python/compare/v5...v7) --- updated-dependencies: - dependency-name: actions/setup-python dependency-version: '7' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/benchmark.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index 072d26d..84b3984 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -68,7 +68,7 @@ jobs: uses: actions/checkout@v4 - name: Set up Python - uses: actions/setup-python@v5 + uses: actions/setup-python@v7 with: python-version: '3.12' From c713c79b32da011a20849cbbbc70fcdd9e85162e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 25 Jul 2026 13:37:08 +0000 Subject: [PATCH 20/39] build(deps): bump actions/checkout from 4 to 7 Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 7. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v4...v7) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '7' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/benchmark.yml | 4 ++-- .github/workflows/ci.yml | 4 ++-- .github/workflows/release.yml | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index 072d26d..823a3c1 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -26,7 +26,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v7 - name: Set up JDK 25 uses: actions/setup-java@v4 @@ -65,7 +65,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v7 - name: Set up Python uses: actions/setup-python@v5 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2b0059f..dbfbc24 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,7 +20,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v7 - name: Set up JDK ${{ matrix.java }} uses: actions/setup-java@v4 @@ -47,7 +47,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v7 - name: Set up JDK 25 uses: actions/setup-java@v4 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c66eca0..06910f8 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -16,7 +16,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v7 - name: Set up JDK 25 uses: actions/setup-java@v4 From 18965d6e84a35ff6388d20c774dd74a29601d910 Mon Sep 17 00:00:00 2001 From: NAMAN JAIN Date: Sat, 25 Jul 2026 19:12:41 +0530 Subject: [PATCH 21/39] fix deps in becnh ci --- .github/workflows/benchmark.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index 072d26d..92875ea 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -73,7 +73,7 @@ jobs: python-version: '3.12' - name: Install Charting Dependencies - run: pip install matplotlib pandas seaborn + run: pip install matplotlib pandas seaborn tabulate - name: Download All Results uses: actions/download-artifact@v4 From 7b966c35edc433005e0a4a74d289a659532b3458 Mon Sep 17 00:00:00 2001 From: namanONcode <156304712+namanONcode@users.noreply.github.com> Date: Sat, 25 Jul 2026 13:50:48 +0000 Subject: [PATCH 22/39] docs(benchmark): auto-update performance charts and README --- README.md | 36 +- assets/benchmark_graph.svg | 3189 +++++++++++------ zthread-benchmark/benchmark.md | 27 + .../charts/scaling_throughput.png | Bin 0 -> 40815 bytes 4 files changed, 2100 insertions(+), 1152 deletions(-) create mode 100644 zthread-benchmark/benchmark.md create mode 100644 zthread-benchmark/charts/scaling_throughput.png diff --git a/README.md b/README.md index f47411d..2765a8b 100644 --- a/README.md +++ b/README.md @@ -16,14 +16,34 @@ zThread is designed for maximum throughput and minimal latency. In single-produc | Framework / Mechanism | Throughput (Higher is better) | Average Latency (Lower is better) | Engine Architecture | | :--- | :--- | :--- | :--- | -| **zThread (Linux FFM / Epoll)** | **~13.21 M ops/sec** | **~75.7 ns / event** | Kernel `epoll` + Lock-free RingBuffer via Panama FFM | -| **Project Reactor** | ~10.68 M ops/sec | ~93.6 ns / event | RingBuffer-backed Schedulers | -| **SynchronousQueue** | ~10.34 M ops/sec | ~96.7 ns / event | Dual stack / queue thread handoff | -| **ArrayBlockingQueue** | ~10.00 M ops/sec | ~100.0 ns / event | ReentrantLock + Condition queues | -| **LinkedBlockingQueue** | ~9.88 M ops/sec | ~101.2 ns / event | Two-lock queue algorithm | -| **Vert.x (Event Loop)** | ~8.32 M ops/sec | ~120.2 ns / event | Netty-backed event loop dispatch | -| **Netty (NIO EventLoop)** | ~7.21 M ops/sec | ~138.7 ns / event | `Selector` + ConcurrentLinkedQueue dispatch | -| **Java Virtual Threads** | ~1.12 M ops/sec | ~895.2 ns / event | Carrier thread park/unpark overhead | +| **ConcurrentLinkedQueue** | ~20.22 M ops/sec | ~49.5 ns / event | Lock-free queue algorithm | +| **ArrayBlockingQueue** | ~19.83 M ops/sec | ~50.4 ns / event | ReentrantLock + Condition queues | +| **LinkedBlockingQueue** | ~12.33 M ops/sec | ~81.1 ns / event | Two-lock queue algorithm | +| **zThread (Linux FFM / Epoll)** | **~11.72 M ops/sec** | **~85.3 ns / event** | Kernel `epoll` + Lock-free RingBuffer via Panama FFM | +| **SynchronousQueue** | ~11.04 M ops/sec | ~90.6 ns / event | Dual stack / queue thread handoff | +| **Netty (NIO EventLoop)** | ~11.01 M ops/sec | ~90.8 ns / event | `Selector` + ConcurrentLinkedQueue dispatch | +| **Project Reactor (Schedulers)** | ~9.15 M ops/sec | ~109.3 ns / event | RingBuffer-backed Schedulers | +| **Vert.x (Event Loop)** | ~8.34 M ops/sec | ~119.9 ns / event | Netty-backed event loop dispatch | +| **Java Virtual Threads (Loom)** | ~4.91 M ops/sec | ~203.7 ns / event | Carrier thread park/unpark overhead | +| **benchmark** | ~4.34 M ops/sec | ~230.4 ns / event | Unknown architecture | +| **benchmark** | ~4.04 M ops/sec | ~247.6 ns / event | Unknown architecture | +| **benchmark** | ~3.96 M ops/sec | ~252.7 ns / event | Unknown architecture | +| **benchmark** | ~3.76 M ops/sec | ~266.3 ns / event | Unknown architecture | +| **benchmark** | ~3.69 M ops/sec | ~270.8 ns / event | Unknown architecture | +| **benchmark** | ~3.68 M ops/sec | ~271.4 ns / event | Unknown architecture | +| **benchmark** | ~3.62 M ops/sec | ~276.0 ns / event | Unknown architecture | +| **benchmark** | ~3.61 M ops/sec | ~276.8 ns / event | Unknown architecture | +| **benchmark** | ~3.27 M ops/sec | ~305.9 ns / event | Unknown architecture | +| **benchmark** | ~3.02 M ops/sec | ~330.9 ns / event | Unknown architecture | +| **benchmark** | ~2.98 M ops/sec | ~335.6 ns / event | Unknown architecture | +| **benchmark** | ~2.89 M ops/sec | ~345.7 ns / event | Unknown architecture | +| **benchmark** | ~2.88 M ops/sec | ~347.8 ns / event | Unknown architecture | +| **benchmark** | ~2.81 M ops/sec | ~355.4 ns / event | Unknown architecture | +| **benchmark** | ~2.71 M ops/sec | ~369.6 ns / event | Unknown architecture | +| **benchmark** | ~1.34 M ops/sec | ~746.6 ns / event | Unknown architecture | +| **benchmark** | ~1.06 M ops/sec | ~940.4 ns / event | Unknown architecture | +| **benchmark** | ~0.75 M ops/sec | ~1340.4 ns / event | Unknown architecture | +| **benchmark** | ~0.56 M ops/sec | ~1784.6 ns / event | Unknown architecture | ## Installation diff --git a/assets/benchmark_graph.svg b/assets/benchmark_graph.svg index 2c4fc74..22dabf9 100644 --- a/assets/benchmark_graph.svg +++ b/assets/benchmark_graph.svg @@ -6,7 +6,7 @@ - 2026-07-24T13:22:48.257280 + 2026-07-25T13:50:47.044407 image/svg+xml @@ -42,16 +42,16 @@ z +" clip-path="url(#pfebc989e72)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #334155; stroke-opacity: 0.7; stroke-width: 0.8"/> - - + @@ -86,228 +86,164 @@ z - + - + - - + + - - + - + - + - - + + - - + + - + - + - - - - - - + + + + - + - + - - + + - - + + - + - + - - - - - - - + + + + @@ -862,67 +798,70 @@ z - - + - - + + - - - + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - + - - + + - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - + + - + - - - + + + - - - - - - - - - - - - - - - - - - - - - +M 1259 4147 +L 1259 2741 +L 2194 2741 +Q 2656 2741 2882 2914 +Q 3109 3088 3109 3444 +Q 3109 3797 2882 3972 +Q 2656 4147 2194 4147 +L 1259 4147 +z +M 628 4666 +L 2241 4666 +Q 2963 4666 3353 4366 +Q 3744 4066 3744 3513 +Q 3744 3084 3544 2831 +Q 3344 2578 2956 2516 +Q 3422 2416 3680 2098 +Q 3938 1781 3938 1306 +Q 3938 681 3513 340 +Q 3088 0 2303 0 +L 628 0 +L 628 4666 +z +" transform="scale(0.015625)"/> + + + + + + + + + + + + + + + + + + + + + - - + + - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +" clip-path="url(#pfebc989e72)" style="fill: #38bdf8"/> - +" clip-path="url(#pfebc989e72)" style="fill: #38bdf8"/> - +" clip-path="url(#pfebc989e72)" style="fill: #38bdf8"/> - +" clip-path="url(#pfebc989e72)" style="fill: #38bdf8"/> - +" clip-path="url(#pfebc989e72)" style="fill: #38bdf8"/> - +" clip-path="url(#pfebc989e72)" style="fill: #38bdf8"/> - +" clip-path="url(#pfebc989e72)" style="fill: #38bdf8"/> - +" clip-path="url(#pfebc989e72)" style="fill: #38bdf8"/> - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +" clip-path="url(#pfebc989e72)" style="fill: #38bdf8"/> - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - - - + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -2212,11 +3070,11 @@ z - - - + + + - + @@ -2226,13 +3084,13 @@ z - - - - + + + + - + @@ -2243,11 +3101,11 @@ z - - + + - + @@ -2257,12 +3115,12 @@ z - - - - + + + + - + @@ -2275,10 +3133,10 @@ z - - + + - + @@ -2288,45 +3146,13 @@ z - - - - - - - + + + + - - + + @@ -2337,11 +3163,11 @@ z - - - + + + - + @@ -2351,13 +3177,13 @@ z - - - - + + + + - + @@ -2369,8 +3195,70 @@ z + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -2382,61 +3270,10 @@ z - - - + + + - - - + + + + - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + - + - + @@ -2551,28 +3452,28 @@ z - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + - + @@ -2582,28 +3483,28 @@ z - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + - + @@ -2613,28 +3514,28 @@ z - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + - + @@ -2644,7 +3545,7 @@ z - + @@ -3234,7 +4135,7 @@ z - + @@ -3414,7 +4315,7 @@ z - + diff --git a/zthread-benchmark/benchmark.md b/zthread-benchmark/benchmark.md new file mode 100644 index 0000000..f3b7f58 --- /dev/null +++ b/zthread-benchmark/benchmark.md @@ -0,0 +1,27 @@ +# zThread Benchmark Results + +## Scaling Throughput (64B) +![Scaling](charts/scaling_throughput.png) + +## Raw Data +| Framework | Concurrency | PayloadSize | Throughput (ops/s) | Error | +|:------------------------|:--------------|--------------:|---------------------:|--------:| +| ARRAY_BLOCKING_QUEUE | 8:1 | 64 | 2.8751e+06 | nan | +| LINKED_BLOCKING_QUEUE | 8:1 | 64 | 2.7059e+06 | nan | +| CONCURRENT_LINKED_QUEUE | 8:1 | 64 | 3.69265e+06 | nan | +| SYNCHRONOUS_QUEUE | 8:1 | 64 | 560335 | nan | +| THREAD_POOL | 8:1 | 64 | 2.97949e+06 | nan | +| FORK_JOIN | 8:1 | 64 | 3.95786e+06 | nan | +| VIRTUAL_THREADS | 8:1 | 64 | 1.33933e+06 | nan | +| REACTOR | 8:1 | 64 | 3.75569e+06 | nan | +| NETTY | 8:1 | 64 | 3.68406e+06 | nan | +| ZTHREAD | 1:1 | 64 | 3.26889e+06 | nan | +| ARRAY_BLOCKING_QUEUE | 1:1 | 64 | 2.81407e+06 | nan | +| LINKED_BLOCKING_QUEUE | 1:1 | 64 | 2.8929e+06 | nan | +| CONCURRENT_LINKED_QUEUE | 1:1 | 64 | 3.61304e+06 | nan | +| SYNCHRONOUS_QUEUE | 1:1 | 64 | 1.0634e+06 | nan | +| THREAD_POOL | 1:1 | 64 | 3.02172e+06 | nan | +| FORK_JOIN | 1:1 | 64 | 4.03807e+06 | nan | +| VIRTUAL_THREADS | 1:1 | 64 | 746053 | nan | +| REACTOR | 1:1 | 64 | 3.62314e+06 | nan | +| NETTY | 1:1 | 64 | 4.33965e+06 | nan | diff --git a/zthread-benchmark/charts/scaling_throughput.png b/zthread-benchmark/charts/scaling_throughput.png new file mode 100644 index 0000000000000000000000000000000000000000..aa391da0ff4936280dc3957e9e044e7b69bd541d GIT binary patch literal 40815 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 Date: Sat, 25 Jul 2026 19:29:11 +0530 Subject: [PATCH 23/39] feat: filter benchmark graphs to only include SpscEventBenchmark results --- scripts/generate-graph.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/scripts/generate-graph.py b/scripts/generate-graph.py index 14ee9ce..98b15df 100755 --- a/scripts/generate-graph.py +++ b/scripts/generate-graph.py @@ -39,7 +39,9 @@ def main(): } for result in data: - full_name = result['benchmark'] + full_name = result.get('benchmark', '') + if 'SpscEventBenchmark' not in full_name: + continue method_name = full_name.split('.')[-1] display_name = name_map.get(method_name, method_name) From fdda7676616f901eb3f4b3fc1aea055adf15b423 Mon Sep 17 00:00:00 2001 From: NAMAN JAIN Date: Sat, 25 Jul 2026 19:46:27 +0530 Subject: [PATCH 24/39] revise benchmarks --- scripts/generate-graph.py | 392 ++++++++++-------- zthread-benchmark/scripts/generate_charts.py | 41 +- .../benchmark/queue/SpscEventBenchmark.java | 52 +-- .../S1_S5_ThroughputLatencyBenchmark.java | 14 +- .../scenarios/S7_BurstTrafficBenchmark.java | 5 +- .../scenarios/S8_SteadyTrafficBenchmark.java | 5 +- 6 files changed, 270 insertions(+), 239 deletions(-) diff --git a/scripts/generate-graph.py b/scripts/generate-graph.py index 98b15df..cf63360 100755 --- a/scripts/generate-graph.py +++ b/scripts/generate-graph.py @@ -3,197 +3,261 @@ import os import sys import matplotlib.pyplot as plt +import matplotlib.ticker as mticker import numpy as np -def main(): - if len(sys.argv) > 1: - json_path = sys.argv[1] - else: - json_path = 'jmh-result.json' - - output_path = 'assets/benchmark_graph.svg' - readme_path = 'README.md' +# โ”€โ”€ Frameworks kept after the ABQ/LBQ/CLQ removal โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +FRAMEWORKS = [ + 'zThread', + 'SynchronousQueue', + 'Netty', + 'Vert.x', + 'Reactor', + 'Virtual Threads', +] - if not os.path.exists(json_path): - print(f"File {json_path} not found. Cannot generate graph.") - return +# SpscEventBenchmark method โ†’ display name +SPSC_NAME_MAP = { + 'benchZThread': 'zThread', + 'benchNetty': 'Netty', + 'benchVertx': 'Vert.x', + 'benchReactor': 'Reactor', + 'benchVirtualThreads': 'Virtual Threads', + 'benchSynchronousQueue': 'SynchronousQueue', +} - with open(json_path, 'r') as f: - data = json.load(f) +# S1_S5 framework param โ†’ display name +SCALING_NAME_MAP = { + 'ZTHREAD': 'zThread', + 'NETTY': 'Netty', + 'VERTX': 'Vert.x', + 'REACTOR': 'Reactor', + 'VIRTUAL_THREADS': 'Virtual Threads', + 'SYNCHRONOUS_QUEUE':'SynchronousQueue', + 'THREAD_POOL': 'Thread Pool', + 'FORK_JOIN': 'Fork/Join', +} - benchmarks = [] - scores = [] - errors = [] - - # Map method names to clear, descriptive names - name_map = { - 'benchZThread': 'zThread (Linux FFM / Epoll)', - 'benchNetty': 'Netty (NIO EventLoop)', - 'benchVertx': 'Vert.x (Event Loop)', - 'benchReactor': 'Project Reactor (Schedulers)', - 'benchVirtualThreads': 'Java Virtual Threads (Loom)', - 'benchArrayBlockingQueue': 'ArrayBlockingQueue', - 'benchLinkedBlockingQueue': 'LinkedBlockingQueue', - 'benchConcurrentLinkedQueue': 'ConcurrentLinkedQueue', - 'benchSynchronousQueue': 'SynchronousQueue' - } - - for result in data: - full_name = result.get('benchmark', '') - if 'SpscEventBenchmark' not in full_name: - continue - method_name = full_name.split('.')[-1] - - display_name = name_map.get(method_name, method_name) - score = float(result['primaryMetric']['score']) - - raw_error = result['primaryMetric'].get('scoreError', 0) - try: - error = float(raw_error) - if np.isnan(error): - error = 0 - except: - error = 0 - - benchmarks.append(display_name) - scores.append(score) - errors.append(error) - - if not benchmarks: - print("No benchmarks found.") +# README table: full display name โ†’ arch description +FULL_NAME_MAP = { + 'benchZThread': 'zThread (Linux FFM / Epoll)', + 'benchNetty': 'Netty (NIO EventLoop)', + 'benchVertx': 'Vert.x (Event Loop)', + 'benchReactor': 'Project Reactor (Schedulers)', + 'benchVirtualThreads': 'Java Virtual Threads (Loom)', + 'benchSynchronousQueue': 'SynchronousQueue', +} + +ARCH_MAP = { + 'zThread (Linux FFM / Epoll)': 'Kernel `epoll` + Lock-free RingBuffer via Panama FFM', + 'Project Reactor (Schedulers)': 'RingBuffer-backed Schedulers', + 'SynchronousQueue': 'Dual stack / queue thread handoff', + 'Vert.x (Event Loop)': 'Netty-backed event loop dispatch', + 'Netty (NIO EventLoop)': '`Selector` + ConcurrentLinkedQueue dispatch', + 'Java Virtual Threads (Loom)': 'Carrier thread park/unpark overhead', +} + +# โ”€โ”€ Color palette (modern dark-theme) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +FRAMEWORK_COLORS = { + 'zThread': '#10b981', # emerald + 'Netty': '#38bdf8', # sky + 'Vert.x': '#a78bfa', # violet + 'Reactor': '#fb923c', # orange + 'Virtual Threads': '#f87171', # rose + 'SynchronousQueue': '#facc15', # amber + 'Thread Pool': '#34d399', # teal + 'Fork/Join': '#60a5fa', # blue +} + +BG_COLOR = '#0d1117' +GRID_COLOR = '#1e293b' +TEXT_COLOR = '#e2e8f0' +SUBTLE_COLOR = '#94a3b8' + + +def parse_benchmark_data(data): + """Parse merged JMH JSON into SPSC and scaling data dicts keyed by framework display name.""" + spsc = {} # { display_name: throughput_ops_s } + # scaling: { (display_name, concurrency): throughput_ops_s } + scaling_low = {} + scaling_high = {} + + for entry in data: + name = entry.get('benchmark', '') + score = float(entry.get('primaryMetric', {}).get('score', 0)) + + if 'SpscEventBenchmark' in name: + method = name.split('.')[-1] + dname = SPSC_NAME_MAP.get(method) + if dname: + spsc[dname] = score + + elif 'S1_S5_ThroughputLatencyBenchmark' in name: + params = entry.get('params', {}) + fw_raw = params.get('framework', '') + conc = params.get('concurrency', '1:1') + dname = SCALING_NAME_MAP.get(fw_raw) + if not dname: + continue + if conc in ('1:1', '4:1'): + scaling_low.setdefault(dname, {})[conc] = score + elif conc in ('8:1', '16:4', '32:8'): + scaling_high.setdefault(dname, {})[conc] = score + + return spsc, scaling_low, scaling_high + + +def draw_radar_chart(spsc, scaling_low, scaling_high, output_path): + """Generate a modern dark-theme radar chart with 3 series: SPSC, Scaling-Low, Scaling-High.""" + + # Use the intersection of all 3 datasets as axes + common_fw = [fw for fw in FRAMEWORKS if fw in spsc] + if not common_fw: + print("No common frameworks for radar chart.") return - # Sort by score ascending (lowest to highest) so highest score is at top of barh chart - sorted_data = sorted(zip(scores, benchmarks, errors)) - scores, benchmarks, errors = zip(*sorted_data) + N = len(common_fw) + angles = np.linspace(0, 2 * np.pi, N, endpoint=False).tolist() + angles += angles[:1] # close the polygon - # Convert to Millions (ops/sec) - scores_m = [s / 1_000_000 for s in scores] - errors_m = [e / 1_000_000 for e in errors] + # Collect raw values for each series (average across concurrency levels for scaling) + def avg_scaling(scaling_dict, fw): + vals = scaling_dict.get(fw, {}) + return np.mean(list(vals.values())) if vals else 0 - # Calculate average latency in nanoseconds (1 / ops_sec * 1e9) - latencies_ns = [(1.0 / s) * 1e9 if s > 0 else 0 for s in scores] + spsc_vals = [spsc.get(fw, 0) for fw in common_fw] + slo_vals = [avg_scaling(scaling_low, fw) for fw in common_fw] + shi_vals = [avg_scaling(scaling_high, fw) for fw in common_fw] - # Set up dark theme figure with explicit background color (not transparent) - fig, ax = plt.subplots(figsize=(11, 6.5), facecolor='#0d1117') - ax.set_facecolor('#0d1117') + # Normalize to 0-1 (percentage of global max) for a balanced radar + global_max = max(max(spsc_vals), max(slo_vals), max(shi_vals)) or 1 + spsc_norm = [v / global_max for v in spsc_vals] + [spsc_vals[0] / global_max] + slo_norm = [v / global_max for v in slo_vals] + [slo_vals[0] / global_max] + shi_norm = [v / global_max for v in shi_vals] + [shi_vals[0] / global_max] - # Color palette - colors = [] - for b in benchmarks: - if 'zThread' in b: - colors.append('#10b981') # Vibrant Emerald Green for zThread - elif 'Virtual' in b: - colors.append('#ef4444') # Red accent for low-throughput baseline - else: - colors.append('#38bdf8') # Bright Sky Blue for other frameworks - - bars = ax.barh(benchmarks, scores_m, xerr=errors_m, color=colors, capsize=4, height=0.55, - error_kw={'ecolor': '#94a3b8', 'linewidth': 1.2}) - - # Styling labels and title - ax.set_xlabel('Throughput (Million Operations / Sec โ€” Higher is Better)', - fontsize=12, fontweight='bold', color='#e2e8f0', labelpad=12) - - fig.suptitle('Event Loop & Micro-Task Throughput Benchmark', - fontsize=16, fontweight='bold', color='#ffffff', y=0.96) - ax.set_title('Evaluated using JMH โ€ข SPSC Event Passing โ€ข JDK 25 Linux', - fontsize=10, color='#94a3b8', style='italic', pad=12) - - # Gridlines and spines - ax.grid(axis='x', linestyle=':', color='#334155', alpha=0.7) - ax.set_axisbelow(True) - - for spine in ax.spines.values(): - spine.set_color('#334155') - ax.spines['top'].set_visible(False) - ax.spines['right'].set_visible(False) - - # Tick parameters - ax.tick_params(axis='x', colors='#cbd5e1', labelsize=10) - ax.tick_params(axis='y', colors='#f8fafc', labelsize=11) - - # Max score for padding - max_score = max(scores_m) - ax.set_xlim(0, max_score * 1.35) - - # Add numeric annotation: "13.21 M ops/s (~75.7 ns)" - for idx, (bar, score_m, latency_ns) in enumerate(zip(bars, scores_m, latencies_ns)): - width = bar.get_width() - bench_name = benchmarks[idx] - - text_color = '#34d399' if 'zThread' in bench_name else '#cbd5e1' - font_weight = 'bold' if 'zThread' in bench_name else 'normal' - - annotation = f'{score_m:.2f} M ops/s ({latency_ns:.1f} ns/op)' - ax.text(width + 0.3, bar.get_y() + bar.get_height()/2, annotation, - ha='left', va='center', color=text_color, fontweight=font_weight, fontsize=10) - - plt.subplots_adjust(left=0.32, right=0.96, top=0.84, bottom=0.12) - plt.savefig(output_path, format='svg', facecolor=fig.get_facecolor(), edgecolor='none') - print(f"Graph successfully generated at {output_path}") - - # --- Update README.md Table --- + # โ”€โ”€ Figure โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + fig, ax = plt.subplots(figsize=(9, 9), subplot_kw=dict(polar=True), facecolor=BG_COLOR) + ax.set_facecolor(BG_COLOR) + + # Series colors & labels + series = [ + (spsc_norm, '#10b981', 'SPSC Queue (Single Producer โ†’ Single Consumer)'), + (slo_norm, '#38bdf8', 'Scaling Low (1:1 ยท 4:1)'), + (shi_norm, '#f472b6', 'Scaling High (8:1 ยท 16:4 ยท 32:8)'), + ] + + for vals, color, label in series: + ax.plot(angles, vals, 'o-', linewidth=2.2, color=color, label=label, markersize=6) + ax.fill(angles, vals, alpha=0.12, color=color) + + # Axis labels (framework names) + ax.set_xticks(angles[:-1]) + ax.set_xticklabels(common_fw, fontsize=12, fontweight='bold', color=TEXT_COLOR) + + # Radial grid + ax.set_rlabel_position(30) + ax.yaxis.set_major_formatter(mticker.FuncFormatter( + lambda v, _: f'{v * global_max / 1e6:.0f}M' if v > 0 else '')) + ax.tick_params(axis='y', labelsize=9, colors=SUBTLE_COLOR) + + # Grid styling + ax.spines['polar'].set_color(GRID_COLOR) + ax.grid(color=GRID_COLOR, linewidth=0.8, alpha=0.6) + + # Title + fig.suptitle('zThread ยท Multi-Dimensional Performance Radar', + fontsize=17, fontweight='bold', color='#ffffff', y=0.97) + ax.set_title('JMH Benchmark ยท JDK 25 ยท Linux', + fontsize=10, color=SUBTLE_COLOR, style='italic', pad=24) + + # Legend + legend = ax.legend(loc='lower center', bbox_to_anchor=(0.5, -0.14), + ncol=3, fontsize=10, framealpha=0.0, + labelcolor=TEXT_COLOR) + for text in legend.get_texts(): + text.set_color(TEXT_COLOR) + + plt.subplots_adjust(top=0.88, bottom=0.12) + os.makedirs(os.path.dirname(output_path) or '.', exist_ok=True) + plt.savefig(output_path, format='svg', facecolor=fig.get_facecolor(), edgecolor='none', + bbox_inches='tight', pad_inches=0.3) + plt.close() + print(f"Radar chart generated at {output_path}") + + +def update_readme_table(spsc, readme_path): + """Update the benchmark table in README.md with SPSC results only (6 frameworks).""" if not os.path.exists(readme_path): print(f"README file {readme_path} not found. Skipping table update.") return - # Generate new table content table_lines = [ "| Framework / Mechanism | Throughput (Higher is better) | Average Latency (Lower is better) | Engine Architecture |", "| :--- | :--- | :--- | :--- |" ] - - # Engine architecture mapping - arch_map = { - 'zThread (Linux FFM / Epoll)': 'Kernel `epoll` + Lock-free RingBuffer via Panama FFM', - 'Project Reactor (Schedulers)': 'RingBuffer-backed Schedulers', - 'SynchronousQueue': 'Dual stack / queue thread handoff', - 'ArrayBlockingQueue': 'ReentrantLock + Condition queues', - 'LinkedBlockingQueue': 'Two-lock queue algorithm', - 'Vert.x (Event Loop)': 'Netty-backed event loop dispatch', - 'Netty (NIO EventLoop)': '`Selector` + ConcurrentLinkedQueue dispatch', - 'Java Virtual Threads (Loom)': 'Carrier thread park/unpark overhead', - 'ConcurrentLinkedQueue': 'Lock-free queue algorithm' - } - - # Sort descending for table (highest score first) - sorted_table_data = sorted(zip(scores_m, benchmarks, latencies_ns), reverse=True) - - for score_m, bench_name, latency_ns in sorted_table_data: - arch = arch_map.get(bench_name, "Unknown architecture") - - # Bold zThread for emphasis - if "zThread" in bench_name: - bench_display = f"**{bench_name}**" - score_display = f"**~{score_m:.2f} M ops/sec**" - lat_display = f"**~{latency_ns:.1f} ns / event**" + + # Build rows sorted by throughput descending + rows = [] + for method, display in FULL_NAME_MAP.items(): + short = SPSC_NAME_MAP.get(method) + if not short or short not in spsc: + continue + score = spsc[short] + score_m = score / 1_000_000 + latency_ns = (1.0 / score * 1e9) if score > 0 else 0 + arch = ARCH_MAP.get(display, 'Unknown') + rows.append((score_m, display, latency_ns, arch)) + + rows.sort(reverse=True) + + for score_m, display, latency_ns, arch in rows: + if 'zThread' in display: + table_lines.append( + f"| **{display}** | **~{score_m:.2f} M ops/sec** | **~{latency_ns:.1f} ns / event** | {arch} |") else: - bench_display = f"**{bench_name}**" - score_display = f"~{score_m:.2f} M ops/sec" - lat_display = f"~{latency_ns:.1f} ns / event" - - table_lines.append(f"| {bench_display} | {score_display} | {lat_display} | {arch} |") - - new_table_str = "\n".join(table_lines) + "\n" - - # Read README, find markers, and replace + table_lines.append( + f"| **{display}** | ~{score_m:.2f} M ops/sec | ~{latency_ns:.1f} ns / event | {arch} |") + + new_table = "\n".join(table_lines) + "\n" + with open(readme_path, 'r') as f: content = f.read() - + start_marker = "" end_marker = "" - - start_idx = content.find(start_marker) - end_idx = content.find(end_marker) - - if start_idx != -1 and end_idx != -1: - new_content = content[:start_idx + len(start_marker)] + "\n" + new_table_str + content[end_idx:] + si = content.find(start_marker) + ei = content.find(end_marker) + + if si != -1 and ei != -1: + new_content = content[:si + len(start_marker)] + "\n" + new_table + content[ei:] with open(readme_path, 'w') as f: f.write(new_content) - print(f"Table successfully updated in {readme_path}") + print(f"Table updated in {readme_path}") else: - print(f"Markers not found in {readme_path}. Could not update table.") + print(f"Markers not found in {readme_path}. Skipping table update.") + + +def main(): + json_path = sys.argv[1] if len(sys.argv) > 1 else 'jmh-result.json' + output_path = 'assets/benchmark_graph.svg' + readme_path = 'README.md' + + if not os.path.exists(json_path): + print(f"File {json_path} not found.") + return + + with open(json_path, 'r') as f: + data = json.load(f) + + spsc, scaling_low, scaling_high = parse_benchmark_data(data) + + if not spsc: + print("No SPSC benchmark data found.") + return + + draw_radar_chart(spsc, scaling_low, scaling_high, output_path) + update_readme_table(spsc, readme_path) + if __name__ == "__main__": main() diff --git a/zthread-benchmark/scripts/generate_charts.py b/zthread-benchmark/scripts/generate_charts.py index 7a40adb..41434e3 100755 --- a/zthread-benchmark/scripts/generate_charts.py +++ b/zthread-benchmark/scripts/generate_charts.py @@ -3,6 +3,9 @@ import os import sys +# Frameworks removed from benchmarks โ€” filter them out of charts +EXCLUDED_FRAMEWORKS = {'ARRAY_BLOCKING_QUEUE', 'LINKED_BLOCKING_QUEUE', 'CONCURRENT_LINKED_QUEUE'} + def main(): if len(sys.argv) < 2: print("Usage: generate_charts.py ") @@ -32,6 +35,11 @@ def main(): if "S1_S5_ThroughputLatencyBenchmark" in name: params = bench.get("params", {}) framework = params.get("framework", "UNKNOWN") + + # Skip removed frameworks + if framework in EXCLUDED_FRAMEWORKS: + continue + concurrency = params.get("concurrency", "1:1") payload = params.get("payloadSize", "64") @@ -56,15 +64,38 @@ def main(): out_dir = os.path.join(os.path.dirname(json_path), "../charts") os.makedirs(out_dir, exist_ok=True) - # Filter out by default payload for a clean scaling chart + # Dark theme styling + plt.style.use('dark_background') + + # Filter for 64B payload df_64 = df[df['PayloadSize'] == '64'] # Plot Scaling (Throughput vs Concurrency) - plt.figure(figsize=(10, 6)) - sns.barplot(data=df_64, x='Concurrency', y='Throughput (ops/s)', hue='Framework') - plt.title('Throughput Scaling (64B Payload)') + fig, ax = plt.subplots(figsize=(12, 7)) + fig.patch.set_facecolor('#0d1117') + ax.set_facecolor('#0d1117') + + palette = sns.color_palette([ + '#10b981', '#38bdf8', '#a78bfa', '#fb923c', + '#f87171', '#facc15', '#34d399', '#60a5fa' + ]) + + sns.barplot(data=df_64, x='Concurrency', y='Throughput (ops/s)', + hue='Framework', palette=palette, ax=ax) + + ax.set_title('Throughput Scaling ยท 64B Payload', fontsize=15, + fontweight='bold', color='#ffffff', pad=16) + ax.set_xlabel('Concurrency (Producers : Consumers)', fontsize=12, + color='#e2e8f0', labelpad=10) + ax.set_ylabel('Throughput (ops/s)', fontsize=12, color='#e2e8f0', labelpad=10) + ax.tick_params(colors='#cbd5e1') + ax.grid(axis='y', linestyle=':', color='#1e293b', alpha=0.6) + ax.legend(title='Framework', fontsize=9, title_fontsize=10, + loc='upper right', framealpha=0.3, labelcolor='#e2e8f0') + plt.tight_layout() - plt.savefig(os.path.join(out_dir, 'scaling_throughput.png')) + plt.savefig(os.path.join(out_dir, 'scaling_throughput.png'), + dpi=150, facecolor=fig.get_facecolor()) plt.close() print(f"Charts generated in {out_dir}") diff --git a/zthread-benchmark/src/main/java/io/github/namanoncode/zthread/benchmark/queue/SpscEventBenchmark.java b/zthread-benchmark/src/main/java/io/github/namanoncode/zthread/benchmark/queue/SpscEventBenchmark.java index c96e1c8..8eb9b41 100644 --- a/zthread-benchmark/src/main/java/io/github/namanoncode/zthread/benchmark/queue/SpscEventBenchmark.java +++ b/zthread-benchmark/src/main/java/io/github/namanoncode/zthread/benchmark/queue/SpscEventBenchmark.java @@ -20,10 +20,7 @@ public class SpscEventBenchmark { public static final int BATCH_SIZE = 1_000_000; - // Standard Queues - private ArrayBlockingQueue arrayQueue; - private LinkedBlockingQueue linkedQueue; - private ConcurrentLinkedQueue concurrentQueue; + // Standard Queue private SynchronousQueue synchronousQueue; // Async Runtimes @@ -44,13 +41,9 @@ public class SpscEventBenchmark { private Thread queueConsumerThread; private CountDownLatch queueLatch; private BlockingQueue activeBlockingQueue; - private ConcurrentLinkedQueue activeConcurrentQueue; @Setup(Level.Trial) public void setup() { - arrayQueue = new ArrayBlockingQueue<>(BATCH_SIZE); - linkedQueue = new LinkedBlockingQueue<>(BATCH_SIZE); - concurrentQueue = new ConcurrentLinkedQueue<>(); synchronousQueue = new SynchronousQueue<>(); // Setup zThread with enough buffer for the batch to prevent EventLoopException: buffer full @@ -110,7 +103,6 @@ public void teardownInvocation() throws InterruptedException { queueConsumerThread = null; } activeBlockingQueue = null; - activeConcurrentQueue = null; } private void startBlockingQueueConsumer(BlockingQueue queue, Blackhole bh) { @@ -131,39 +123,9 @@ private void startBlockingQueueConsumer(BlockingQueue queue, Blackhole b queueConsumerThread.start(); } - private void startConcurrentQueueConsumer(ConcurrentLinkedQueue queue, Blackhole bh) { - activeConcurrentQueue = queue; - queueConsumerThread = new Thread(() -> { - while (running) { - Object obj = activeConcurrentQueue.poll(); - if (obj != null) { - bh.consume(obj); - queueLatch.countDown(); - } - } - }); - queueConsumerThread.start(); - } - @Benchmark - @OperationsPerInvocation(BATCH_SIZE) - public void benchArrayBlockingQueue(Blackhole bh) throws InterruptedException { - startBlockingQueueConsumer(arrayQueue, bh); - for (int i = 0; i < BATCH_SIZE; i++) { - arrayQueue.put(EVENT); - } - queueLatch.await(); - } - @Benchmark - @OperationsPerInvocation(BATCH_SIZE) - public void benchLinkedBlockingQueue(Blackhole bh) throws InterruptedException { - startBlockingQueueConsumer(linkedQueue, bh); - for (int i = 0; i < BATCH_SIZE; i++) { - linkedQueue.put(EVENT); - } - queueLatch.await(); - } + @Benchmark @OperationsPerInvocation(BATCH_SIZE) @@ -175,15 +137,7 @@ public void benchSynchronousQueue(Blackhole bh) throws InterruptedException { queueLatch.await(); } - @Benchmark - @OperationsPerInvocation(BATCH_SIZE) - public void benchConcurrentLinkedQueue(Blackhole bh) throws InterruptedException { - startConcurrentQueueConsumer(concurrentQueue, bh); - for (int i = 0; i < BATCH_SIZE; i++) { - concurrentQueue.offer(EVENT); - } - queueLatch.await(); - } + @Benchmark @OperationsPerInvocation(BATCH_SIZE) diff --git a/zthread-benchmark/src/main/java/io/github/namanoncode/zthread/benchmark/scenarios/S1_S5_ThroughputLatencyBenchmark.java b/zthread-benchmark/src/main/java/io/github/namanoncode/zthread/benchmark/scenarios/S1_S5_ThroughputLatencyBenchmark.java index da11965..a9f08de 100644 --- a/zthread-benchmark/src/main/java/io/github/namanoncode/zthread/benchmark/scenarios/S1_S5_ThroughputLatencyBenchmark.java +++ b/zthread-benchmark/src/main/java/io/github/namanoncode/zthread/benchmark/scenarios/S1_S5_ThroughputLatencyBenchmark.java @@ -19,9 +19,6 @@ public class S1_S5_ThroughputLatencyBenchmark { public enum Framework { ZTHREAD, - ARRAY_BLOCKING_QUEUE, - LINKED_BLOCKING_QUEUE, - CONCURRENT_LINKED_QUEUE, SYNCHRONOUS_QUEUE, THREAD_POOL, FORK_JOIN, @@ -31,7 +28,7 @@ public enum Framework { VERTX } - @Param({"ZTHREAD", "ARRAY_BLOCKING_QUEUE", "LINKED_BLOCKING_QUEUE", "CONCURRENT_LINKED_QUEUE", "SYNCHRONOUS_QUEUE", "THREAD_POOL", "FORK_JOIN", "VIRTUAL_THREADS", "REACTOR", "NETTY", "VERTX"}) + @Param({"ZTHREAD", "SYNCHRONOUS_QUEUE", "THREAD_POOL", "FORK_JOIN", "VIRTUAL_THREADS", "REACTOR", "NETTY", "VERTX"}) private Framework framework; @Param({"64", "256", "1024", "4096"}) @@ -58,15 +55,6 @@ public void setup() { case ZTHREAD: adapter = new ZThreadAdapter(); break; - case ARRAY_BLOCKING_QUEUE: - adapter = new BlockingQueueAdapter(new ArrayBlockingQueue<>(10_000)); - break; - case LINKED_BLOCKING_QUEUE: - adapter = new BlockingQueueAdapter(new LinkedBlockingQueue<>()); - break; - case CONCURRENT_LINKED_QUEUE: - adapter = new BlockingQueueAdapter(new LinkedTransferQueue<>()); // Using LinkedTransferQueue as standard unbounded blocking queue - break; case SYNCHRONOUS_QUEUE: adapter = new BlockingQueueAdapter(new SynchronousQueue<>()); break; diff --git a/zthread-benchmark/src/main/java/io/github/namanoncode/zthread/benchmark/scenarios/S7_BurstTrafficBenchmark.java b/zthread-benchmark/src/main/java/io/github/namanoncode/zthread/benchmark/scenarios/S7_BurstTrafficBenchmark.java index 182d936..de49d7b 100644 --- a/zthread-benchmark/src/main/java/io/github/namanoncode/zthread/benchmark/scenarios/S7_BurstTrafficBenchmark.java +++ b/zthread-benchmark/src/main/java/io/github/namanoncode/zthread/benchmark/scenarios/S7_BurstTrafficBenchmark.java @@ -18,7 +18,7 @@ @State(Scope.Benchmark) public class S7_BurstTrafficBenchmark { - @Param({"ZTHREAD", "ARRAY_BLOCKING_QUEUE", "THREAD_POOL", "REACTOR", "NETTY", "VERTX"}) + @Param({"ZTHREAD", "THREAD_POOL", "REACTOR", "NETTY", "VERTX"}) private S1_S5_ThroughputLatencyBenchmark.Framework framework; @Param({"1000000"}) @@ -33,9 +33,6 @@ public void setup() { case ZTHREAD: adapter = new ZThreadAdapter(); break; - case ARRAY_BLOCKING_QUEUE: - adapter = new BlockingQueueAdapter(new java.util.concurrent.ArrayBlockingQueue<>(1_000_000)); - break; case THREAD_POOL: adapter = new ExecutorAdapter(Executors.newFixedThreadPool(4)); break; diff --git a/zthread-benchmark/src/main/java/io/github/namanoncode/zthread/benchmark/scenarios/S8_SteadyTrafficBenchmark.java b/zthread-benchmark/src/main/java/io/github/namanoncode/zthread/benchmark/scenarios/S8_SteadyTrafficBenchmark.java index f0f9311..c95b7b2 100644 --- a/zthread-benchmark/src/main/java/io/github/namanoncode/zthread/benchmark/scenarios/S8_SteadyTrafficBenchmark.java +++ b/zthread-benchmark/src/main/java/io/github/namanoncode/zthread/benchmark/scenarios/S8_SteadyTrafficBenchmark.java @@ -18,7 +18,7 @@ @State(Scope.Benchmark) public class S8_SteadyTrafficBenchmark { - @Param({"ZTHREAD", "ARRAY_BLOCKING_QUEUE", "THREAD_POOL", "REACTOR", "NETTY", "VERTX"}) + @Param({"ZTHREAD", "THREAD_POOL", "REACTOR", "NETTY", "VERTX"}) private S1_S5_ThroughputLatencyBenchmark.Framework framework; @Param({"1000", "5000", "10000"}) // Target events per second @@ -34,9 +34,6 @@ public void setup() { case ZTHREAD: adapter = new ZThreadAdapter(); break; - case ARRAY_BLOCKING_QUEUE: - adapter = new BlockingQueueAdapter(new java.util.concurrent.ArrayBlockingQueue<>(10_000)); - break; case THREAD_POOL: adapter = new ExecutorAdapter(Executors.newFixedThreadPool(4)); break; From 7f99cc64b71094eabf364f905b4020ee7941d54d Mon Sep 17 00:00:00 2001 From: namanONcode <156304712+namanONcode@users.noreply.github.com> Date: Sat, 25 Jul 2026 14:26:30 +0000 Subject: [PATCH 25/39] docs(benchmark): auto-update performance charts and README --- README.md | 34 +- assets/benchmark_graph.svg | 6064 ++++++----------- zthread-benchmark/benchmark.md | 36 +- .../charts/scaling_throughput.png | Bin 40815 -> 74610 bytes 4 files changed, 2162 insertions(+), 3972 deletions(-) diff --git a/README.md b/README.md index 2765a8b..dd01819 100644 --- a/README.md +++ b/README.md @@ -16,34 +16,12 @@ zThread is designed for maximum throughput and minimal latency. In single-produc | Framework / Mechanism | Throughput (Higher is better) | Average Latency (Lower is better) | Engine Architecture | | :--- | :--- | :--- | :--- | -| **ConcurrentLinkedQueue** | ~20.22 M ops/sec | ~49.5 ns / event | Lock-free queue algorithm | -| **ArrayBlockingQueue** | ~19.83 M ops/sec | ~50.4 ns / event | ReentrantLock + Condition queues | -| **LinkedBlockingQueue** | ~12.33 M ops/sec | ~81.1 ns / event | Two-lock queue algorithm | -| **zThread (Linux FFM / Epoll)** | **~11.72 M ops/sec** | **~85.3 ns / event** | Kernel `epoll` + Lock-free RingBuffer via Panama FFM | -| **SynchronousQueue** | ~11.04 M ops/sec | ~90.6 ns / event | Dual stack / queue thread handoff | -| **Netty (NIO EventLoop)** | ~11.01 M ops/sec | ~90.8 ns / event | `Selector` + ConcurrentLinkedQueue dispatch | -| **Project Reactor (Schedulers)** | ~9.15 M ops/sec | ~109.3 ns / event | RingBuffer-backed Schedulers | -| **Vert.x (Event Loop)** | ~8.34 M ops/sec | ~119.9 ns / event | Netty-backed event loop dispatch | -| **Java Virtual Threads (Loom)** | ~4.91 M ops/sec | ~203.7 ns / event | Carrier thread park/unpark overhead | -| **benchmark** | ~4.34 M ops/sec | ~230.4 ns / event | Unknown architecture | -| **benchmark** | ~4.04 M ops/sec | ~247.6 ns / event | Unknown architecture | -| **benchmark** | ~3.96 M ops/sec | ~252.7 ns / event | Unknown architecture | -| **benchmark** | ~3.76 M ops/sec | ~266.3 ns / event | Unknown architecture | -| **benchmark** | ~3.69 M ops/sec | ~270.8 ns / event | Unknown architecture | -| **benchmark** | ~3.68 M ops/sec | ~271.4 ns / event | Unknown architecture | -| **benchmark** | ~3.62 M ops/sec | ~276.0 ns / event | Unknown architecture | -| **benchmark** | ~3.61 M ops/sec | ~276.8 ns / event | Unknown architecture | -| **benchmark** | ~3.27 M ops/sec | ~305.9 ns / event | Unknown architecture | -| **benchmark** | ~3.02 M ops/sec | ~330.9 ns / event | Unknown architecture | -| **benchmark** | ~2.98 M ops/sec | ~335.6 ns / event | Unknown architecture | -| **benchmark** | ~2.89 M ops/sec | ~345.7 ns / event | Unknown architecture | -| **benchmark** | ~2.88 M ops/sec | ~347.8 ns / event | Unknown architecture | -| **benchmark** | ~2.81 M ops/sec | ~355.4 ns / event | Unknown architecture | -| **benchmark** | ~2.71 M ops/sec | ~369.6 ns / event | Unknown architecture | -| **benchmark** | ~1.34 M ops/sec | ~746.6 ns / event | Unknown architecture | -| **benchmark** | ~1.06 M ops/sec | ~940.4 ns / event | Unknown architecture | -| **benchmark** | ~0.75 M ops/sec | ~1340.4 ns / event | Unknown architecture | -| **benchmark** | ~0.56 M ops/sec | ~1784.6 ns / event | Unknown architecture | +| **zThread (Linux FFM / Epoll)** | **~9.79 M ops/sec** | **~102.1 ns / event** | Kernel `epoll` + Lock-free RingBuffer via Panama FFM | +| **Project Reactor (Schedulers)** | ~7.91 M ops/sec | ~126.4 ns / event | RingBuffer-backed Schedulers | +| **Netty (NIO EventLoop)** | ~6.39 M ops/sec | ~156.5 ns / event | `Selector` + ConcurrentLinkedQueue dispatch | +| **Vert.x (Event Loop)** | ~6.13 M ops/sec | ~163.1 ns / event | Netty-backed event loop dispatch | +| **Java Virtual Threads (Loom)** | ~4.48 M ops/sec | ~223.4 ns / event | Carrier thread park/unpark overhead | +| **SynchronousQueue** | ~1.36 M ops/sec | ~735.2 ns / event | Dual stack / queue thread handoff | ## Installation diff --git a/assets/benchmark_graph.svg b/assets/benchmark_graph.svg index 22dabf9..4b748a0 100644 --- a/assets/benchmark_graph.svg +++ b/assets/benchmark_graph.svg @@ -1,12 +1,12 @@ - + - 2026-07-25T13:50:47.044407 + 2026-07-25T14:26:29.603684 image/svg+xml @@ -21,237 +21,107 @@ - - + + + + + + + + + - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + - + - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + - - - - - - - - - - + + + - - - + + + - - - - - - - - - - - - - - - - - - + + + + + + - - - - - + + + - - - + + + - - - - - + + + + + + + + + + + + + + + + + + - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - - - + + - - - + + + - - - - - - - - - - - - - - - - - - - - - - - + + - - - - + + - - - + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - - - + + - - - + + + - - - - - - - - - - - - - - - - - - - - - - - - - + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - + - - - - + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - + + + - - - - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + diff --git a/zthread-benchmark/benchmark.md b/zthread-benchmark/benchmark.md index f3b7f58..5f2fea2 100644 --- a/zthread-benchmark/benchmark.md +++ b/zthread-benchmark/benchmark.md @@ -4,24 +4,18 @@ ![Scaling](charts/scaling_throughput.png) ## Raw Data -| Framework | Concurrency | PayloadSize | Throughput (ops/s) | Error | -|:------------------------|:--------------|--------------:|---------------------:|--------:| -| ARRAY_BLOCKING_QUEUE | 8:1 | 64 | 2.8751e+06 | nan | -| LINKED_BLOCKING_QUEUE | 8:1 | 64 | 2.7059e+06 | nan | -| CONCURRENT_LINKED_QUEUE | 8:1 | 64 | 3.69265e+06 | nan | -| SYNCHRONOUS_QUEUE | 8:1 | 64 | 560335 | nan | -| THREAD_POOL | 8:1 | 64 | 2.97949e+06 | nan | -| FORK_JOIN | 8:1 | 64 | 3.95786e+06 | nan | -| VIRTUAL_THREADS | 8:1 | 64 | 1.33933e+06 | nan | -| REACTOR | 8:1 | 64 | 3.75569e+06 | nan | -| NETTY | 8:1 | 64 | 3.68406e+06 | nan | -| ZTHREAD | 1:1 | 64 | 3.26889e+06 | nan | -| ARRAY_BLOCKING_QUEUE | 1:1 | 64 | 2.81407e+06 | nan | -| LINKED_BLOCKING_QUEUE | 1:1 | 64 | 2.8929e+06 | nan | -| CONCURRENT_LINKED_QUEUE | 1:1 | 64 | 3.61304e+06 | nan | -| SYNCHRONOUS_QUEUE | 1:1 | 64 | 1.0634e+06 | nan | -| THREAD_POOL | 1:1 | 64 | 3.02172e+06 | nan | -| FORK_JOIN | 1:1 | 64 | 4.03807e+06 | nan | -| VIRTUAL_THREADS | 1:1 | 64 | 746053 | nan | -| REACTOR | 1:1 | 64 | 3.62314e+06 | nan | -| NETTY | 1:1 | 64 | 4.33965e+06 | nan | +| Framework | Concurrency | PayloadSize | Throughput (ops/s) | Error | +|:------------------|:--------------|--------------:|---------------------:|--------:| +| SYNCHRONOUS_QUEUE | 8:1 | 64 | 2.88712e+06 | nan | +| THREAD_POOL | 8:1 | 64 | 2.85072e+06 | nan | +| FORK_JOIN | 8:1 | 64 | 3.59033e+06 | nan | +| VIRTUAL_THREADS | 8:1 | 64 | 1.6e+06 | nan | +| REACTOR | 8:1 | 64 | 3.69897e+06 | nan | +| NETTY | 8:1 | 64 | 3.30496e+06 | nan | +| ZTHREAD | 1:1 | 64 | 3.07317e+06 | nan | +| SYNCHRONOUS_QUEUE | 1:1 | 64 | 1.15276e+06 | nan | +| THREAD_POOL | 1:1 | 64 | 2.48956e+06 | nan | +| FORK_JOIN | 1:1 | 64 | 2.91018e+06 | nan | +| VIRTUAL_THREADS | 1:1 | 64 | 1.31417e+06 | nan | +| REACTOR | 1:1 | 64 | 3.14313e+06 | nan | +| NETTY | 1:1 | 64 | 3.65113e+06 | nan | diff --git a/zthread-benchmark/charts/scaling_throughput.png b/zthread-benchmark/charts/scaling_throughput.png index aa391da0ff4936280dc3957e9e044e7b69bd541d..56e2183b93cc53e6bc13ba89713f30ce64aa407b 100644 GIT binary patch literal 74610 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%0ATM|_H3>NE6!EK-tio?))IuaWu}Bq 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 Ua7Y!=rM96i>tDiY-+A)C0JsaaC;$Ke literal 40815 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 Date: Tue, 28 Jul 2026 06:37:42 +0000 Subject: [PATCH 26/39] build(deps): bump actions/setup-java from 4 to 5 Bumps [actions/setup-java](https://github.com/actions/setup-java) from 4 to 5. - [Release notes](https://github.com/actions/setup-java/releases) - [Commits](https://github.com/actions/setup-java/compare/v4...v5) --- updated-dependencies: - dependency-name: actions/setup-java dependency-version: '5' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/benchmark.yml | 2 +- .github/workflows/ci.yml | 4 ++-- .github/workflows/release.yml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index 9c72f22..06b0bcc 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -29,7 +29,7 @@ jobs: uses: actions/checkout@v7 - name: Set up JDK 25 - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: java-version: '25' distribution: 'temurin' diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index dbfbc24..dd7d758 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,7 +23,7 @@ jobs: uses: actions/checkout@v7 - name: Set up JDK ${{ matrix.java }} - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: java-version: ${{ matrix.java }} distribution: 'temurin' @@ -50,7 +50,7 @@ jobs: uses: actions/checkout@v7 - name: Set up JDK 25 - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: java-version: '25' distribution: 'temurin' diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a902310..9e51931 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -19,7 +19,7 @@ jobs: uses: actions/checkout@v7 - name: Set up JDK 25 - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: java-version: '25' distribution: 'temurin' From 9302eccde9ae1d0092e37ec2ce1fef4043f4f64e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 31 Jul 2026 17:05:19 +0000 Subject: [PATCH 27/39] build(deps): bump org.sonatype.central:central-publishing-maven-plugin Bumps [org.sonatype.central:central-publishing-maven-plugin](https://github.com/sonatype/central-publishing-maven-plugin) from 0.5.0 to 0.11.0. - [Commits](https://github.com/sonatype/central-publishing-maven-plugin/commits) --- updated-dependencies: - dependency-name: org.sonatype.central:central-publishing-maven-plugin dependency-version: 0.11.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 107e061..f3e1826 100644 --- a/pom.xml +++ b/pom.xml @@ -351,7 +351,7 @@ org.sonatype.central central-publishing-maven-plugin - 0.5.0 + 0.11.0 true ossrh From 63da1af6740b4d6becdfbb380e51a06c3cc14d1f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 31 Jul 2026 17:05:30 +0000 Subject: [PATCH 28/39] build(deps): bump com.puppycrawl.tools:checkstyle from 10.21.4 to 13.9.0 Bumps [com.puppycrawl.tools:checkstyle](https://github.com/checkstyle/checkstyle) from 10.21.4 to 13.9.0. - [Release notes](https://github.com/checkstyle/checkstyle/releases) - [Commits](https://github.com/checkstyle/checkstyle/compare/checkstyle-10.21.4...checkstyle-13.9.0) --- updated-dependencies: - dependency-name: com.puppycrawl.tools:checkstyle dependency-version: 13.9.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 107e061..4497853 100644 --- a/pom.xml +++ b/pom.xml @@ -110,7 +110,7 @@ 3.8.0 3.6.0 - 10.21.4 + 13.9.0 3.28.0 7.26.0 4.10.3.0 From 04c618eb813717b1fc210bf9384d2e4da12836e4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 31 Jul 2026 17:05:43 +0000 Subject: [PATCH 29/39] build(deps): bump io.netty:netty-all from 4.1.107.Final to 4.2.16.Final Bumps [io.netty:netty-all](https://github.com/netty/netty) from 4.1.107.Final to 4.2.16.Final. - [Release notes](https://github.com/netty/netty/releases) - [Commits](https://github.com/netty/netty/compare/netty-4.1.107.Final...netty-4.2.16.Final) --- updated-dependencies: - dependency-name: io.netty:netty-all dependency-version: 4.2.16.Final dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 107e061..5b85e6e 100644 --- a/pom.xml +++ b/pom.xml @@ -87,7 +87,7 @@ 1.5.16 3.8.6 1.37 - 4.1.107.Final + 4.2.16.Final 4.5.27 From 46c91d2ca5f4e5d2d8793e947fdd34dbdcf4d271 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 31 Jul 2026 17:05:47 +0000 Subject: [PATCH 30/39] build(deps): bump org.apache.maven.plugins:maven-source-plugin Bumps [org.apache.maven.plugins:maven-source-plugin](https://github.com/apache/maven-source-plugin) from 3.3.1 to 3.4.0. - [Release notes](https://github.com/apache/maven-source-plugin/releases) - [Commits](https://github.com/apache/maven-source-plugin/compare/maven-source-plugin-3.3.1...maven-source-plugin-3.4.0) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-source-plugin dependency-version: 3.4.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 107e061..7c2a6ce 100644 --- a/pom.xml +++ b/pom.xml @@ -100,7 +100,7 @@ 3.5.6 3.5.3 3.5.1 - 3.3.1 + 3.4.0 3.12.0 3.2.7 3.1.3 From e07787a4f5da33469f6e1d196ab5edbd4224fa0f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 31 Jul 2026 17:05:52 +0000 Subject: [PATCH 31/39] build(deps): bump org.junit:junit-bom from 5.11.4 to 6.1.2 Bumps [org.junit:junit-bom](https://github.com/junit-team/junit-framework) from 5.11.4 to 6.1.2. - [Release notes](https://github.com/junit-team/junit-framework/releases) - [Commits](https://github.com/junit-team/junit-framework/compare/r5.11.4...r6.1.2) --- updated-dependencies: - dependency-name: org.junit:junit-bom dependency-version: 6.1.2 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 107e061..c7ffb12 100644 --- a/pom.xml +++ b/pom.xml @@ -91,7 +91,7 @@ 4.5.27 - 5.11.4 + 6.1.2 5.23.0 3.27.7 From 349bac59cfb835858e5864f6d878f0d6a96b2191 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 31 Jul 2026 17:05:53 +0000 Subject: [PATCH 32/39] build(deps): bump actions/download-artifact from 4 to 8 Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 4 to 8. - [Release notes](https://github.com/actions/download-artifact/releases) - [Commits](https://github.com/actions/download-artifact/compare/v4...v8) --- updated-dependencies: - dependency-name: actions/download-artifact dependency-version: '8' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/benchmark.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index 06b0bcc..b5f372b 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -76,7 +76,7 @@ jobs: run: pip install matplotlib pandas seaborn tabulate - name: Download All Results - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v8 with: path: benchmark-results pattern: benchmark-* From 34140ed44e4adff87475498e1291b5da7b00e2c9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 31 Jul 2026 17:05:56 +0000 Subject: [PATCH 33/39] build(deps-dev): bump org.awaitility:awaitility from 4.2.2 to 4.3.0 Bumps [org.awaitility:awaitility](https://github.com/awaitility/awaitility) from 4.2.2 to 4.3.0. - [Changelog](https://github.com/awaitility/awaitility/blob/master/changelog.txt) - [Commits](https://github.com/awaitility/awaitility/compare/awaitility-4.2.2...awaitility-4.3.0) --- updated-dependencies: - dependency-name: org.awaitility:awaitility dependency-version: 4.3.0 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- zthread-linux/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zthread-linux/pom.xml b/zthread-linux/pom.xml index 1003d5b..7e2cd2f 100644 --- a/zthread-linux/pom.xml +++ b/zthread-linux/pom.xml @@ -36,7 +36,7 @@ org.awaitility awaitility - 4.2.2 + 4.3.0 test From cb8003bfdad38761f7a6a7525a77f51dfec4a791 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 31 Jul 2026 17:06:00 +0000 Subject: [PATCH 34/39] build(deps-dev): bump org.apache.maven.plugins:maven-shade-plugin Bumps [org.apache.maven.plugins:maven-shade-plugin](https://github.com/apache/maven-shade-plugin) from 3.6.0 to 3.6.2. - [Release notes](https://github.com/apache/maven-shade-plugin/releases) - [Commits](https://github.com/apache/maven-shade-plugin/compare/maven-shade-plugin-3.6.0...maven-shade-plugin-3.6.2) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-shade-plugin dependency-version: 3.6.2 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- zthread-benchmark/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zthread-benchmark/pom.xml b/zthread-benchmark/pom.xml index af4ad3f..a65229e 100644 --- a/zthread-benchmark/pom.xml +++ b/zthread-benchmark/pom.xml @@ -60,7 +60,7 @@ org.apache.maven.plugins maven-shade-plugin - 3.6.0 + 3.6.2 package From 8e5b3b70808b42f832b4cb9ed89d98127bb5fb76 Mon Sep 17 00:00:00 2001 From: NAMAN JAIN Date: Mon, 3 Aug 2026 15:14:42 +0530 Subject: [PATCH 35/39] Update issue templates for features and bug reports --- .github/ISSUE_TEMPLATE/bug_report.md | 38 +++++++++++++++++++++++ .github/ISSUE_TEMPLATE/feature_request.md | 20 ++++++++++++ 2 files changed, 58 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..dd84ea7 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,38 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: '' +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Desktop (please complete the following information):** + - OS: [e.g. iOS] + - Browser [e.g. chrome, safari] + - Version [e.g. 22] + +**Smartphone (please complete the following information):** + - Device: [e.g. iPhone6] + - OS: [e.g. iOS8.1] + - Browser [e.g. stock browser, safari] + - Version [e.g. 22] + +**Additional context** +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..bbcbbe7 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: '' +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. From 1ce7903397e88b77c2fb928e54f7e22fc2d3e1a4 Mon Sep 17 00:00:00 2001 From: NAMAN JAIN Date: Mon, 3 Aug 2026 15:24:07 +0530 Subject: [PATCH 36/39] auto merge ci for bot --- .github/workflows/dependabot-auto-merge.yml | 25 +++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 .github/workflows/dependabot-auto-merge.yml diff --git a/.github/workflows/dependabot-auto-merge.yml b/.github/workflows/dependabot-auto-merge.yml new file mode 100644 index 0000000..978d445 --- /dev/null +++ b/.github/workflows/dependabot-auto-merge.yml @@ -0,0 +1,25 @@ +name: Dependabot Auto-Merge + +on: pull_request + +permissions: + contents: write + pull-requests: write + +jobs: + dependabot: + name: Enable Auto-Merge + runs-on: ubuntu-latest + if: github.actor == 'dependabot[bot]' + steps: + - name: Dependabot metadata + id: metadata + uses: dependabot/fetch-metadata@v2 + with: + github-token: "${{ secrets.GITHUB_TOKEN }}" + + - name: Enable auto-merge for Dependabot PRs + run: gh pr merge --auto --merge "$PR_URL" + env: + PR_URL: ${{github.event.pull_request.html_url}} + GH_TOKEN: ${{secrets.GITHUB_TOKEN}} From ed68f70d7fd96970eada45e7b8d1fe830308bfac Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Aug 2026 10:07:43 +0000 Subject: [PATCH 37/39] build(deps): bump actions/upload-artifact from 4 to 7 Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4 to 7. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/v4...v7) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-version: '7' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/benchmark.yml | 4 ++-- .github/workflows/ci.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index b5f372b..0c178dd 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -53,7 +53,7 @@ jobs: fi - name: Upload Results - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: benchmark-${{ matrix.name }} path: zthread-benchmark/target/benchmark-${{ matrix.name }}.json @@ -100,7 +100,7 @@ jobs: file_pattern: "README.md assets/benchmark_graph.svg zthread-benchmark/charts/* zthread-benchmark/benchmark.md" - name: Upload Final Report - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: benchmark-report path: | diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index dd7d758..ecb8a12 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -34,7 +34,7 @@ jobs: - name: Upload Coverage Report if: matrix.java == '25' - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: coverage-report path: '**/target/site/jacoco/' From c3c4227c35715f08ea0fcae42e32fe24fd0805e6 Mon Sep 17 00:00:00 2001 From: NAMAN JAIN Date: Mon, 3 Aug 2026 15:40:07 +0530 Subject: [PATCH 38/39] fix ci auto merge --- .github/workflows/dependabot-auto-merge.yml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/dependabot-auto-merge.yml b/.github/workflows/dependabot-auto-merge.yml index 978d445..ab66095 100644 --- a/.github/workflows/dependabot-auto-merge.yml +++ b/.github/workflows/dependabot-auto-merge.yml @@ -1,6 +1,6 @@ name: Dependabot Auto-Merge -on: pull_request +on: pull_request_target permissions: contents: write @@ -10,7 +10,7 @@ jobs: dependabot: name: Enable Auto-Merge runs-on: ubuntu-latest - if: github.actor == 'dependabot[bot]' + if: github.event.pull_request.user.login == 'dependabot[bot]' steps: - name: Dependabot metadata id: metadata @@ -19,7 +19,9 @@ jobs: github-token: "${{ secrets.GITHUB_TOKEN }}" - name: Enable auto-merge for Dependabot PRs - run: gh pr merge --auto --merge "$PR_URL" + run: | + gh pr review --approve "$PR_URL" || true + gh pr merge --auto --merge "$PR_URL" env: PR_URL: ${{github.event.pull_request.html_url}} GH_TOKEN: ${{secrets.GITHUB_TOKEN}} From 613ee7632e738e646f6c23c64667940f67000b3e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Aug 2026 10:15:40 +0000 Subject: [PATCH 39/39] build(deps): bump org.ow2.asm:asm from 9.7.1 to 9.10.1 Bumps org.ow2.asm:asm from 9.7.1 to 9.10.1. --- updated-dependencies: - dependency-name: org.ow2.asm:asm dependency-version: 9.10.1 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 927a6c5..5402c06 100644 --- a/pom.xml +++ b/pom.xml @@ -450,7 +450,7 @@ org.ow2.asm asm - 9.7.1 + 9.10.1