Skip to content

Alternative implemention of the Mod97-10 calculation#415

Open
Corniel wants to merge 2 commits into
skwasjer:mainfrom
Corniel:alt-mod97
Open

Alternative implemention of the Mod97-10 calculation#415
Corniel wants to merge 2 commits into
skwasjer:mainfrom
Corniel:alt-mod97

Conversation

@Corniel

@Corniel Corniel commented Jun 20, 2026

Copy link
Copy Markdown

Recently, I stumbled on your IBAN project when I was searching for other IBAN validators/implementations. While setting up a benchmark between your and mine implementation, I also got an idea to improve the Mod97-10 implementation. (Later, I saw you tried something a bit similar).

Anyhow, My implementation is about twice as fast. So feel free to update the implementation.

Method buffer Mean Error StdDev Ratio RatioSD
Alt 0123456789012345 8.358 ns 0.1446 ns 0.1208 ns 0.56 0.01
Test 0123456789012345 14.988 ns 0.3232 ns 0.3319 ns 1.00 0.03
Alt 01234567ABCDEFGH 13.671 ns 0.2255 ns 0.2109 ns 0.54 0.01
Test 01234567ABCDEFGH 25.146 ns 0.2365 ns 0.2212 ns 1.00 0.01
Alt ABCDEFGHIJKLMNOP 9.165 ns 0.0738 ns 0.0691 ns 0.49 0.01
Test ABCDEFGHIJKLMNOP 18.896 ns 0.2695 ns 0.2521 ns 1.00 0.02

So why is this faster?

  1. This code performs less (expensive) mod operations
  2. This code performs a cheaper way (shift right and equals zero) to check if mod is required
  3. This code has two loops to ensure the the last 4 digits are checked last (no branching, less operations)
  4. It assumes that all characters are either uppercase ASCI letters, or digits.

If I use the following code to overcome point 4, the instead of 0.5, the ratio is between 0.65 and 0.60.

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        static ulong Next(ulong num, char ch, int cursor) => ch switch
        {
            >= '0' and <= '9' => (num * 10) + ch - '0',
            >= 'A' and <= 'Z' => (num * 100) + ch - 'A' + 10,
            >= 'a' and <= 'z' => (num * 100) + ch - 'a' + 10,
            _ => throw new InvalidTokenException(cursor, ch),
        };

Qowaiv/Qowaiv#572

@skwasjer

skwasjer commented Jun 27, 2026

Copy link
Copy Markdown
Owner

IIRC, the refactor I did was limited by this class originally being public and used in several places where the input was not guaranteed, hence having to have length/buffer and character range safeguards. It only recently was deprecated and made internal. I'll have to check if the input can now be safely assumed base36 everywhere to your point 4).

I don't remember if I tried 64-bit (u)long, and if I did, what the impact was but it does make sense, eliminates quite a few modulo ops.

I will dive a bit deeper and integrate it in all various benchmarks (incl. older .NET) and check the outcome on my end and report back. Since there's several individual optimizations, I'd like to understand the effect/impact of each first ;)

Thanks

@Corniel

Corniel commented Jun 28, 2026

Copy link
Copy Markdown
Author

IIRC, the refactor I did was limited by this class originally being public and used in several places where the input was not guaranteed, hence having to have length/buffer and character range safeguards. It only recently was deprecated and made internal. I'll have to check if the input can now be safely assumed base36 everywhere to your point 4).

I assumed as much.

I don't remember if I tried 64-bit (u)long, and if I did, what the impact was but it does make sense, eliminates quite a few modulo ops.

Yep. My first implementation (way back) used a big integer. Then, when I ported it to JavaScript/TypeScript, I moved a way form that (also for my .NET implementation), but (as written) reviewing my code again, I thought: what do I gain by reducing the number of modulo's. In this PR, I speeded up my implementation by 76%.

I will dive a bit deeper and integrate it in all various benchmarks (incl. older .NET) and check the outcome on my end and report back. Since there's several individual optimizations, I'd like to understand the effect/impact of each first ;)

That is completely logical. Good luck with that!

@Corniel

Corniel commented Jul 3, 2026

Copy link
Copy Markdown
Author

I revisited my implementation again, and made it even faster. Feel inspired:

Qowaiv/Qowaiv#579

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants