Skip to content

Commit

Permalink
big improvements on Target type performance and memory allocaiton, yay.
Browse files Browse the repository at this point in the history
  • Loading branch information
MithrilMan committed Jun 11, 2020
1 parent 2bc0e3c commit ff81533
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 197 deletions.
25 changes: 0 additions & 25 deletions MithrilShards.P2P.Benchmark/Benchmarks/Target/Target_Study.cs
Original file line number Diff line number Diff line change
Expand Up @@ -78,31 +78,6 @@ public void Setup()
// return new NBitcoin_Target(scalar);
//}

//[Benchmark]
//public MS_Target MulScalar_Target()
//{
// return t1 * scalar;
//}

//[Benchmark]
//public NBitcoin_Target MulScalar_NBitcoinTarget()
//{
// return new NBitcoin_Target(nt1.ToBigInteger() * new BigInteger((long)scalar));
//}

//[Benchmark]
//public MS_Target DivScalar_Target()
//{
// return t1 / scalar;
//}

//[Benchmark]
//public NBitcoin_Target DivScalar_NBitcoinTarget()
//{
// return new NBitcoin_Target(nt1.ToBigInteger() / new BigInteger((long)scalar));
//}


[Benchmark]
public uint CalculateNextWorkRequired()
{
Expand Down
213 changes: 52 additions & 161 deletions src/MithrilShards.Chain.Bitcoin/DataTypes/Target.Operators.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,157 +32,81 @@ public partial class Target : UInt256
return new Target(MemoryMarshal.Cast<uint, byte>(result));
}

public static Target operator -(Target item)
private void ShiftLeft(int shiftAmount)
{
return new Target
{
part1 = ~item.part1,
part2 = ~item.part2,
part3 = ~item.part3,
part4 = ~item.part4,
};
}
const int ELEMENTS = EXPECTED_SIZE / sizeof(ulong);
const int SIZE = sizeof(ulong) * 8;

public static Target operator -(Target left, Target right)
{
return left + -right;
}
Span<ulong> data = MemoryMarshal.CreateSpan(ref Unsafe.As<ulong, ulong>(ref this.part1), ELEMENTS);

public static Target operator <<(Target left, int shiftAmount)
{
ReadOnlySpan<uint> leftBytes = MemoryMarshal.CreateReadOnlySpan(ref Unsafe.As<ulong, uint>(ref left.part1), UINT_ELEMENTS_COUNT);
Span<ulong> result = stackalloc ulong[ELEMENTS];
result.Clear();

Span<uint> result = stackalloc uint[UINT_ELEMENTS_COUNT];
result.Fill(0);

int k = shiftAmount / UINT_BIT_SIZE;
shiftAmount %= UINT_BIT_SIZE;
int k = shiftAmount / SIZE;
shiftAmount %= SIZE;

for (int i = 0; i < UINT_ELEMENTS_COUNT; i++)
for (int i = 0; i < ELEMENTS - k; i++)
{
if (i + k + 1 < UINT_ELEMENTS_COUNT && shiftAmount != 0)
result[i + k + 1] |= leftBytes[i] >> (UINT_BIT_SIZE - shiftAmount);
if (i + k + 1 < ELEMENTS && shiftAmount != 0)
{
result[i + k + 1] |= data[i] >> (SIZE - shiftAmount);
}

if (i + k < UINT_ELEMENTS_COUNT)
result[i + k] |= leftBytes[i] << shiftAmount;
result[i + k] |= data[i] << shiftAmount;
}

return new Target(MemoryMarshal.Cast<uint, byte>(result));
result.CopyTo(data);
}

public static Target operator >>(Target left, int shiftAmount)
private uint GetCompactMantissa(int shiftAmount)
{
ReadOnlySpan<uint> leftBytes = MemoryMarshal.CreateReadOnlySpan(ref Unsafe.As<ulong, uint>(ref left.part1), UINT_ELEMENTS_COUNT);

Span<uint> result = stackalloc uint[UINT_ELEMENTS_COUNT];
result.Fill(0);

int k = shiftAmount / UINT_BIT_SIZE;
shiftAmount %= UINT_BIT_SIZE;
const int ELEMENTS = EXPECTED_SIZE / sizeof(uint);
const int SIZE = sizeof(uint) * 8;

for (int i = 0; i < UINT_ELEMENTS_COUNT; i++)
{
if (i - k - 1 >= 0 && shiftAmount != 0)
result[i - k - 1] |= leftBytes[i] << (UINT_BIT_SIZE - shiftAmount);
ReadOnlySpan<uint> leftBytes = MemoryMarshal.CreateReadOnlySpan(ref Unsafe.As<ulong, uint>(ref this.part1), ELEMENTS);

if (i - k >= 0)
result[i - k] |= (leftBytes[i] >> shiftAmount);
}

return new Target(MemoryMarshal.Cast<uint, byte>(result));
}

public static Target operator *(Target left, Target right)
{
ReadOnlySpan<uint> leftBytes = MemoryMarshal.CreateReadOnlySpan(ref Unsafe.As<ulong, uint>(ref left.part1), UINT_ELEMENTS_COUNT);
ReadOnlySpan<uint> rightBytes = MemoryMarshal.CreateReadOnlySpan(ref Unsafe.As<ulong, uint>(ref right.part1), UINT_ELEMENTS_COUNT);
int k = shiftAmount / SIZE;
shiftAmount %= SIZE;

Target result = new Target();
Span<uint> resultSpan = MemoryMarshal.CreateSpan(ref Unsafe.As<ulong, uint>(ref result.part1), UINT_ELEMENTS_COUNT);
Span<uint> result = stackalloc uint[ELEMENTS - k];
result.Clear();

for (int j = 0; j < UINT_ELEMENTS_COUNT; j++)
for (int i = k; i < ELEMENTS; i++)
{
ulong carry = 0;
for (int i = 0; i + j < UINT_ELEMENTS_COUNT; i++)
if (i - k - 1 >= 0 && shiftAmount != 0)
{
ulong n = carry + resultSpan[i + j] + ((ulong)leftBytes[j] * rightBytes[i]);
resultSpan[i + j] = (uint)(n & 0xffffffff);
carry = n >> UINT_BIT_SIZE;
result[i - k - 1] |= leftBytes[i] << (SIZE - shiftAmount);
}
}

return result;
}

public static Target operator *(Target left, uint value)
{
Target result = new Target();
Span<uint> resultSpan = MemoryMarshal.CreateSpan(ref Unsafe.As<ulong, uint>(ref result.part1), UINT_ELEMENTS_COUNT);

ReadOnlySpan<uint> leftBytes = MemoryMarshal.CreateReadOnlySpan(ref Unsafe.As<ulong, uint>(ref left.part1), UINT_ELEMENTS_COUNT);
ulong carry = 0;
for (int i = 0; i < UINT_ELEMENTS_COUNT; i++)
{
ulong n = carry + ((ulong)value * leftBytes[i]);
resultSpan[i] = (uint)(n & 0xffffffff);
carry = n >> UINT_BIT_SIZE;
result[i - k] |= (leftBytes[i] >> shiftAmount);
}

return result;
return result[0];
}

public static Target operator /(Target dividend, Target divisor)
public static Target operator >>(Target left, int shiftAmount)
{
int dividendBits = dividend.Bits();
int divisorBits = divisor.Bits();

if (divisorBits == 0)
{
ThrowHelper.ThrowArgumentException("Division by zero");
}

// the result is certainly 0.
if (divisorBits > dividendBits)
{
return Zero;
}
ReadOnlySpan<uint> leftBytes = MemoryMarshal.CreateReadOnlySpan(ref Unsafe.As<ulong, uint>(ref left.part1), UINT_ELEMENTS_COUNT);

// the quotient.
Span<uint> result = stackalloc uint[UINT_ELEMENTS_COUNT];
//result.Fill(0); //allocated data is already zeroed
result.Fill(0);

int k = shiftAmount / UINT_BIT_SIZE;
shiftAmount %= UINT_BIT_SIZE;

int shiftAmount = dividendBits - divisorBits;
divisor <<= shiftAmount; // shift so that div and num align.
while (shiftAmount >= 0)
for (int i = k; i < UINT_ELEMENTS_COUNT; i++)
{
if (dividend >= divisor)
if (i - k - 1 >= 0 && shiftAmount != 0)
{
dividend -= divisor;
// set a bit of the result.
result[shiftAmount / UINT_BIT_SIZE] |= (uint)(1 << (shiftAmount & (UINT_BIT_SIZE - 1)));
result[i - k - 1] |= leftBytes[i] << (UINT_BIT_SIZE - shiftAmount);
}

divisor >>= 1; // shift back.
shiftAmount--;
result[i - k] |= (leftBytes[i] >> shiftAmount);
}

return new Target(MemoryMarshal.Cast<uint, byte>(result));
}

public static Target operator *(Target left, ulong right)
{
return left * FromRawValue(right);
}

public static Target operator /(Target left, ulong right)
{
return left / FromRawValue(right);
}





public void Multiply(uint value)
{
Span<uint> data = MemoryMarshal.CreateSpan(ref Unsafe.As<ulong, uint>(ref this.part1), UINT_ELEMENTS_COUNT);
Expand All @@ -194,59 +118,26 @@ public void Multiply(uint value)
data[i] = (uint)(n & 0xffffffff);
carry = n >> UINT_BIT_SIZE;
}

//this is slower
//BigInteger me = new BigInteger(this.GetBytes());
//Span<byte> data = MemoryMarshal.CreateSpan(ref Unsafe.As<ulong, byte>(ref this.part1), EXPECTED_SIZE);
//data.Clear();
//(me * value).TryWriteBytes(data, out _);
}

public void Divide(uint value)
public void Divide(uint divisor)
{
if (divisor == 0)
{
ThrowHelper.ThrowArgumentException("Division by zero");
}

BigInteger dividend = new BigInteger(this.GetBytes());

Span<byte> data = MemoryMarshal.CreateSpan(ref Unsafe.As<ulong, byte>(ref this.part1), EXPECTED_SIZE);
data.Fill(0);
(dividend / value).TryWriteBytes(data, out int writeBytes);



//Span<uint> divisorData = stackalloc uint[UINT_ELEMENTS_COUNT];
//divisorData[0] = divisor;

//Span<uint> data = MemoryMarshal.CreateSpan(ref Unsafe.As<ulong, uint>(ref this.part1), UINT_ELEMENTS_COUNT);

//ReadOnlySpan<uint> divisorBytes = MemoryMarshal.CreateReadOnlySpan(ref Unsafe.As<ulong, uint>(ref div.part1), UINT_ELEMENTS_COUNT);

//int dividendBits = this.Bits();
//int divisorBits = div.Bits();

//if (divisorBits == 0)
//{
// ThrowHelper.ThrowArgumentException("Division by zero");
//}

//// the result is certainly 0.
//if (divisorBits > dividendBits)
//{
// data.Fill(0);
// return;
//}

//// the quotient.
//Span<uint> result = stackalloc uint[UINT_ELEMENTS_COUNT];

//int shiftAmount = dividendBits - divisorBits;
//divisor <<= shiftAmount; // shift so that div and num align.
//while (shiftAmount >= 0)
//{
// if (dividend >= divisor)
// {
// dividend -= divisor;
// // set a bit of the result.
// result[shiftAmount / UINT_BIT_SIZE] |= (uint)(1 << (shiftAmount & (UINT_BIT_SIZE - 1)));
// }

// divisor >>= 1; // shift back.
// shiftAmount--;
//}

//return new Target(MemoryMarshal.Cast<uint, byte>(result));
data.Clear();
(dividend / divisor).TryWriteBytes(data, out _);
}
}
}
25 changes: 14 additions & 11 deletions src/MithrilShards.Chain.Bitcoin/DataTypes/Target.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
using System.Buffers.Binary;
using System.Diagnostics;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using MithrilShards.Core.DataTypes;

Expand All @@ -19,18 +18,17 @@ public partial class Target : UInt256
/// <returns></returns>
public static Target FromRawValue(ulong value)
{
return new Target
{
part1 = value,
//part1 = ((ulong)((uint)value) | (ulong)((uint)value >> 32))
};
return new Target { part1 = value };
}


private Target() { }

public Target(string hexString) : base(hexString) { }

/// <summary>
/// Initializes a new instance of the <see cref="Target"/> class from a raw byte array.
/// </summary>
/// <param name="input"></param>
public Target(ReadOnlySpan<byte> input) : base(input) { }

/// <summary>
Expand All @@ -52,8 +50,14 @@ public Target(uint compactValue)
}
else
{
Target temp = new Target { part1 = mantissa } << (8 * (exponent - 3));
temp.GetBytes().CopyTo(data);
//BigInteger n = new BigInteger(mantissa) << (8 * (exponent - 3));
//n.TryWriteBytes(data, out _);

//Target temp = new Target { part1 = mantissa } << (8 * (exponent - 3));
//temp.GetBytes().CopyTo(data);

this.part1 = mantissa;
this.ShiftLeft(8 * (exponent - 3));
}

/// 0x00800000 is the mask to use to obtain the sign, if needed.
Expand Down Expand Up @@ -110,7 +114,7 @@ public uint ToCompact(bool isNegative = false)
}
else
{
compact = (uint)(this >> 8 * (size - 3)).part1;
compact = this.GetCompactMantissa(8 * (size - 3));
}

// The 0x00800000 bit denotes the sign.
Expand All @@ -135,6 +139,5 @@ public BigInteger ToBigInteger()
var value = compact & 0x00FFFFFF;
return new BigInteger(value) << (8 * ((int)exp - 3));
}

}
}

0 comments on commit ff81533

Please sign in to comment.