bcgit / bc-csharp

BouncyCastle.NET Cryptography Library (Mirror)
https://www.bouncycastle.org/csharp
MIT License
1.64k stars 547 forks source link

Benchmark of bc and Libsodium #348

Open ForkBug opened 2 years ago

ForkBug commented 2 years ago

I has done a little benchmark testing about BouncyCastle and Libsodium The result is

|                           Method |       Mean |    Error |   StdDev |
|--------------------------------- |-----------:|---------:|---------:|
|        BCChaCha20Poly1305Encrypt |   923.9 ns |  3.13 ns |  2.93 ns |
|               BCChaCha20Poly1305 | 2,143.0 ns |  5.65 ns |  5.29 ns |
|                  BCAESGCMEncrypt | 2,052.8 ns |  9.32 ns |  8.72 ns |
|                         BCAESGCM | 4,184.4 ns | 16.40 ns | 15.34 ns |
| LibsodiumChaCha20Poly1305Encrypt |   376.4 ns |  0.36 ns |  0.32 ns |
|        LibsodiumChaCha20Poly1305 |   766.6 ns |  0.97 ns |  0.86 ns |
|           LibsodiumAESGCMEncrypt |   182.5 ns |  0.19 ns |  0.18 ns |
|                  LibsodiumAESGCM |   354.6 ns |  1.20 ns |  1.12 ns |

Libsodium performances too well. C/C++ couldn't get the result over C#. Did I doing something wrong?

Testing code is

        byte[] data;
        byte[] Buffer;
        ulong n = 1;
        byte[] k = new byte[32];
        byte[] nonce = new byte[12];
        byte[] ad = new byte[32];
        byte[] tag = new byte[32];

        Org.BouncyCastle.Crypto.Modes.ChaCha20Poly1305 bccipher = new Org.BouncyCastle.Crypto.Modes.ChaCha20Poly1305();
        Org.BouncyCastle.Crypto.Modes.ChaCha20Poly1305 bccipher2 = new Org.BouncyCastle.Crypto.Modes.ChaCha20Poly1305();

        [Benchmark]
        public void BCChaCha20Poly1305Encrypt()
        {
            ++n;
            BinaryPrimitives.WriteUInt64LittleEndian(nonce.AsSpan().Slice(4), n);
            var parameters = new AeadParameters(new KeyParameter(k), 16 * 8, nonce);
            bccipher.Init(true, parameters);
            var len = bccipher.ProcessBytes(data, 0, data.Length, Buffer, 0);
            bccipher.DoFinal(Buffer, len);
        }
        [Benchmark]
        public void BCChaCha20Poly1305()
        {
            ++n;
            BinaryPrimitives.WriteUInt64LittleEndian(nonce.AsSpan().Slice(4), n);
            var parameters = new AeadParameters(new KeyParameter(k), 16 * 8, nonce);
            bccipher.Init(true, parameters);
            var len = bccipher.ProcessBytes(data, 0, data.Length, Buffer, 0);
            len += bccipher.DoFinal(Buffer, len);

            bccipher2.Init(false, parameters);
            var len2 = bccipher2.ProcessBytes(Buffer, 0, len, data, 0);
            bccipher2.DoFinal(Buffer, len2);
        }

        [Benchmark]
        public void LibsodiumChaCha20Poly1305Encrypt()
        {
            ++n;
            BinaryPrimitives.WriteUInt64LittleEndian(nonce.AsSpan().Slice(4), n);

            int result = Libsodium.crypto_aead_chacha20poly1305_ietf_encrypt(
                ref Buffer[0],
                out long length,
                 ref data[0],
                data.Length,
                ref ad[0],
                0,
                IntPtr.Zero,
                ref nonce[0],
                ref k[0]
            );

            if (result != 0)
            {
                throw new Exception("Encryption failed.");
            }
        }
aizuon commented 2 years ago

It is expected that C++ would perform better than C#. Another big reason in this difference arises from usage of hardware acceleration. As far as I know, libsodium uses SSE/AVX instruction sets to vectorize operations and also has CPU intrinsic operations like AES instruction.

ForkBug commented 2 years ago

A benchmark of C and C# has been ported from https://benchmarksgame-team.pages.debian.net/benchmarksgame/performance/simple.html to https://github.com/ForkBug/CAndCSharpBenchmark The result on my machine is:


BenchmarkDotNet=v0.13.1, .NET SDK=6.0.300
  [Host]     : .NET 6.0.5 (6.0.522.21309), X64 RyuJIT
  DefaultJob : .NET 6.0.5 (6.0.522.21309), X64 RyuJIT
Method Mean Error StdDev
DebianSimpleCpp 13.20 s 0.013 s 0.011 s
DebianSimpleCSharp 13.28 s 0.011 s 0.010 s

C++ would perform better than C#. But there shouldn't be a perceivable difference for most CPU-intensive tasks.

C# fx provides AVX and Aes intrinsics. I think the usage is easy than in C. https://docs.microsoft.com/en-us/dotnet/api/system.runtime.intrinsics.x86.aes?view=net-6.0&viewFallbackFrom=netstandard-2.0