Skip to content

Repository files navigation

IBAN Commons Benchmarks

A performance comparison module using the Java Microbenchmark Harness (JMH) to evaluate throughput and memory allocation of different IBAN and BIC libraries in the Java ecosystem.

If you feel other libraries should be included, or have suggestions for this suite, please get in touch.

🎯 Benchmark Scope

IbanBenchmarks has three groups, covering validation of valid and invalid IBANs as well as IBAN creation from components:

Benchmark Library Description
bmv1 iban-commons (de.speedbanking) High-performance ASCII-math validation – valid IBANs
bmv2 iban4j (org.iban4j) Exception-based validation – valid IBANs
bmv3 Apache Commons Validator Regex-based IBAN validation – valid IBANs
bmv4 garvelink iban (nl.garvelink.oss) Object-oriented parsing – valid IBANs
bmv5 jbanking (fr.marcwrobel) Feature-rich banking toolkit – valid IBANs
bmi1 iban-commons Same as bmv1 – invalid IBANs (rejection cost)
bmi2 iban4j Same as bmv2 – invalid IBANs (rejection cost)
bmi3 Apache Commons Validator Same as bmv3 – invalid IBANs (rejection cost)
bmi4 garvelink iban Same as bmv4 – invalid IBANs (rejection cost)
bmi5 jbanking Same as bmv5 – invalid IBANs (rejection cost)
bmb1 iban-commons IBAN creation from bank code and account number (DE)
bmb2 iban4j Same as bmb1
bmb3 iban-commons IBAN creation with calculated national check digit (FR)

The builder group only covers iban-commons and iban4j, the other libraries offer no comparable API to build an IBAN from its components. bmb3 only covers iban-commons, since iban4j expects the national check digit to be passed in.

The test dataset uses compact (unformatted) IBAN strings across all supported countries, generated randomly per run to prevent JIT over-specialization. Each invalid IBAN is derived from a valid one via RandomIban.invalidString(), which applies one of six sabotage strategies with equal probability: incrementing a check digit (triggering a Mod-97 failure), replacing the country code with the non-registered code XY, substituting a valid but mismatched ISO 3166 country code, injecting a letter into the numeric BBAN section, swapping two adjacent characters (transposition), or truncating the string below the minimum structural length.

A note on -XX:-StackTraceInThrowable

All forks run with this JVM flag, which suppresses stack trace generation. This isolates the pure algorithmic cost of validation and makes the comparison fair for libraries that use exceptions for control flow (notably iban4j). It does not reflect default production behaviour. For a production-realistic measurement, remove the flag from @Fork and re-run.

BicBenchmarks compares BIC validation of iban-commons (bmv1/bmi1), iban4j (bmv2/bmi2) and jbanking (bmv3/bmi3), again on valid and invalid input.

⚙️ Requirements & Environment

🚀 Building and Execution

The project uses the maven-shade-plugin to produce a single executable JAR containing all dependencies including the JMH runner.

1. Build the Executable JAR

mvn clean package
# or simply (defaultGoal is clean package):
mvn

This produces target/iban-commons-benchmarks.jar and copies the run scripts to target/.

2. Run Automated Benchmarks

The provided scripts detect system information, configure SerialGC for minimal measurement noise, and – on Linux – pin execution to a single CPU core to minimise measurement jitter.

Linux:

./target/run-iban-benchmarks.sh
./target/run-bic-benchmarks.sh

Windows:

target\run-iban-benchmarks.cmd
target\run-bic-benchmarks.cmd

Or run the JAR directly with standard JMH options:

# Run all IBAN benchmarks
java -jar target/iban-commons-benchmarks.jar IbanBenchmarks

# Run only the valid-IBAN group with custom iteration settings
java -jar target/iban-commons-benchmarks.jar "IbanBenchmarks.*bmv" -i 10 -r 5s

# Run with GC profiling
java -jar target/iban-commons-benchmarks.jar IbanBenchmarks -prof gc

Results are written as .log and .json to target/ and automatically archived to benchmarks/history/ for regression tracking.

📊 Results & Visualization

To visualize results interactively:

  1. Go to JMH Visualizer.
  2. Drag and drop the .json file from target/ or benchmarks/history/.

📊 Latest Performance Snapshot (2026-10-25)

Measured with iban-commons 1.8.13 on Intel(R) Core(TM) i7-1165G7 @ 2.80GHz, OpenJDK 25.0.3, Linux, single core (taskset -c 0), SerialGC, -XX:-StackTraceInThrowable. 3 forks × 5 iterations × 2 s each. Competing libraries in the versions declared in pom.xml.

Valid IBANs (best-case / accept path)

# Library Throughput (ops/s) Memory (B/op) vs. iban-commons
bmv1 🌟 iban-commons 5,755,902 0 baseline
bmv5 jbanking 4,258,449 325 ~1.4× slower
bmv2 iban4j 4,207,195 506 ~1.4× slower
bmv3 Apache Commons 3,052,124 398 ~1.9× slower
bmv4 Garvelink 1,906,954 725 ~3.0× slower

Invalid IBANs (rejection path)

# Library Throughput (ops/s) Memory (B/op) vs. iban-commons
bmi1 🌟 iban-commons 10,883,875 0 baseline
bmi5 jbanking 7,322,453 171 ~1.5× slower
bmi3 Apache Commons 6,065,384 210 ~1.8× slower
bmi2 iban4j 2,089,403 972 ~5.2× slower
bmi4 Garvelink 1,980,023 559 ~5.5× slower

IBAN creation from components

# Library Throughput (ops/s) Memory (B/op)
bmb1 🌟 iban-commons (DE) 4,656,409 120
bmb2 iban4j (DE) 2,401,856 1,524
bmb3 🌟 iban-commons (FR, NCD) 3,664,765 152

BICs

# Library Valid (ops/s) Invalid (ops/s) Memory (B/op)
bmv1/bmi1 🌟 iban-commons 34,404,044 49,769,526 0
bmv3/bmi3 jbanking 32,826,481 30,565,190 46–48
bmv2/bmi2 iban4j 17,927,819 8,616,491 144–215

Memory figures (B/op) from JMH gc.alloc.rate.norm profiler.

Key Takeaways

iban-commons is consistently fastest across valid and invalid IBANs and BICs, and it is the only library that validates without any heap allocation. Its IBAN rejection path is nearly twice as fast as its accept path, because many invalid IBANs fail the length or country code check before the Mod-97 computation is reached.

Compared to 1.8.12, measured in the same session, IBAN validation got about 6% faster on valid and 9% faster on invalid input, since a single CountryValidator class now serves all countries.

jbanking remains the strongest challenger on IBANs and comes close on valid BICs. iban4j has caught up on valid IBANs and now ranks with jbanking, but still allocates heavily and falls back on the rejection path, where its exception-based API creates exception objects even though -XX:-StackTraceInThrowable removes the stack trace cost. The same applies to Garvelink.

Note: Earlier snapshots were taken on OpenJDK 21 and partly with ParallelGC and fewer forks. They are kept in benchmarks/history/ but are not directly comparable to the figures above.