Benchmark of bc and Libsodium
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.");
}
}
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.
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