planetarium / libplanet

Blockchain in C#/.NET for on-chain, decentralized gaming
https://docs.libplanet.io/
GNU Lesser General Public License v2.1
506 stars 139 forks source link

WTF is wrong with your perfomance?? #3588

Open kzorin52 opened 6 months ago

kzorin52 commented 6 months ago
Benchmark: Method Mean Error StdDev
BouncyCastlePub 484.89 us 7.248 us 6.780 us
NBitcoinSecp256K1Pub 123.40 us 0.765 us 0.716 us
NBitcoinPub 114.46 us 0.973 us 0.862 us
NativePub 36.64 us 0.432 us 0.404 us
EcdsaLibPub 80.81 us 0.792 us 0.702 us
NethermindPub 25.18 us 0.187 us 0.166 us
LibplanetPub 963.74 us 13.782 us 12.892 us

Code:

public class Secp256K1Tests
{
    private const int N = 32;
    private readonly byte[] _data;
    private readonly ReadOnlyMemory<byte> _dataSpan;
    private readonly Memory<byte> _dataMemory;

    private static readonly X9ECParameters Curve = SecNamedCurves.GetByName("secp256k1");
    private static readonly ECDomainParameters Params = new(Curve.Curve, Curve.G, Curve.N, Curve.H, Curve.GetSeed());
    private static readonly Secp256k1 Secp256K1 = new();

    public Secp256K1Tests()
    {
        _data = new byte[N];
        new Random(121).NextBytes(_data);
        _dataSpan = new ReadOnlyMemory<byte>(_data);
        _dataMemory = _data.AsMemory();
    }

    [Benchmark]
    public byte[] BouncyCastlePub()
    {
        var key = new ECPrivateKeyParameters(new BigInteger(1, _dataSpan.Span), Params);
        return key.Parameters.G.Multiply(key.D).GetEncoded(true);
    }

    [Benchmark]
    public byte[] NBitcoinSecp256K1Pub()
    {
        using var prv = NBitcoin.Secp256k1.ECPrivKey.Create(_dataSpan.Span);
        return prv.CreatePubKey().ToBytes(true);
    }

    [Benchmark]
    public byte[] NBitcoinPub()
    {
        return new NBitcoin.Key(_data).PubKey.ToBytes();
    }

    [Benchmark]
    public byte[] NativePub()
    {
        Span<byte> pub = stackalloc byte[64];
        Secp256K1.PublicKeyCreate(pub, _dataMemory.Span);

        Span<byte> pubCompr = stackalloc byte[33];
        Secp256K1.PublicKeySerialize(pubCompr, pub, Flags.SECP256K1_EC_COMPRESSED);

        return pubCompr.ToArray();
    }

    [Benchmark]
    public byte[] EcdsaLibPub()
    {
        return Cryptography.ECDSA.Secp256K1Manager.GetPublicKey(_data, true);
    }

    [Benchmark]
    public byte[] NethermindPub()
    {
        return Nethermind.Crypto.SecP256k1.GetPublicKey(_data, true)!;
    }

    [Benchmark]
    public byte[] LibplanetPub()
    {
        return [.. new Libplanet.Crypto.PrivateKey(_data).PublicKey.ToImmutableArray(true)];
    }
}
riemannulus commented 6 months ago

Thank you for reporting the issue. Since we are using Pub, which wraps BouncyCastle, there should not be much difference in performance from BouncyCastle. Please tell me which version of BouncyCastle you used in your testing.

kzorin52 commented 6 months ago

Hi, latest

riemannulus commented 6 months ago

@kzorin52 can you add this line for use BouncyCastle binding?

CryptoConfig.CryptoBackend = new Secp256k1CryptoBackend<SHA256>();

Maybe you need to import Libplanet.Crypto.Secp256k1 package too.