Skip to content

Commit

Permalink
Benchmark structure for UInt classes (#553)
Browse files Browse the repository at this point in the history
* basic benchmark structure for UInt classes

* commented code2 from lights for now

* updated tests. all seem correct now

* Switch to using a benchmark method taking a method delegate to benchmark.

* Make pass.

* 1 million iterations.

* Switch to ulong for the 4th option, and it is still the same speed.

* fix test data for 50% equal data

* make test pass

* neo.UnitTests/UT_UIntBenchmarks.cs

* neo.UnitTests/UT_UIntBenchmarks.cs

* Base 20 - UInt160 tests

* neo.UnitTests/UT_UIntBenchmarks.cs

* inlined 160

* complete tests with UInt256 and UInt160

* neo.UnitTests/UT_UIntBenchmarks.cs

* Lights division calculation
  • Loading branch information
igormcoelho committed Jan 18, 2019
1 parent 54ff67d commit 6849ac2
Show file tree
Hide file tree
Showing 2 changed files with 334 additions and 0 deletions.
333 changes: 333 additions & 0 deletions neo.UnitTests/UT_UIntBenchmarks.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,333 @@
using FluentAssertions;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Neo.Cryptography.ECC;
using Neo.IO;
using Neo.Ledger;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Diagnostics;
using System;
//using System.Runtime.CompilerServices.Unsafe;

namespace Neo.UnitTests
{
[TestClass]
public class UT_UIntBenchmarks
{
int MAX_TESTS = 1000000; // 1 million

byte[][] base_32_1;
byte[][] base_32_2;
byte[][] base_20_1;
byte[][] base_20_2;

private Random random;

[TestInitialize]
public void TestSetup()
{
int SEED = 123456789;
random = new Random(SEED);

base_32_1 = new byte[MAX_TESTS][];
base_32_2 = new byte[MAX_TESTS][];
base_20_1 = new byte[MAX_TESTS][];
base_20_2 = new byte[MAX_TESTS][];

for(var i=0; i<MAX_TESTS; i++)
{
base_32_1[i] = RandomBytes(32);
base_20_1[i] = RandomBytes(20);
if (i % 2 == 0)
{
base_32_2[i] = RandomBytes(32);
base_20_2[i] = RandomBytes(20);
}
else
{
base_32_2[i] = new byte[32];
Buffer.BlockCopy(base_32_1[i], 0, base_32_2[i], 0, 32);
base_20_2[i] = new byte[20];
Buffer.BlockCopy(base_20_1[i], 0, base_20_2[i], 0, 20);
}
}
}

private byte[] RandomBytes(int count)
{
byte[] randomBytes = new byte[count];
random.NextBytes(randomBytes);
return randomBytes;
}

public delegate Object BenchmarkMethod();

public (TimeSpan, Object) Benchmark(BenchmarkMethod method)
{
Stopwatch sw0 = new Stopwatch();
sw0.Start();
var result = method();
sw0.Stop();
TimeSpan elapsed = sw0.Elapsed;
Console.WriteLine($"Elapsed={elapsed} Sum={result}");
return (elapsed, result);
}

/* Could do this also so just pass the method to benchmark, but overhead of delegate call might affect benchmark
public delegate int ComparisonMethod(byte[] b1, byte[] b2);
public int BechmarkComparisonMethod(ComparisonMethod compareMethod)
{
}
*/

[TestMethod]
public void Benchmark_CompareTo_UInt256()
{
// testing "official UInt256 version"
UInt256[] uut_32_1 = new UInt256[MAX_TESTS];
UInt256[] uut_32_2 = new UInt256[MAX_TESTS];

for(var i=0; i<MAX_TESTS; i++)
{
uut_32_1[i] = new UInt256(base_32_1[i]);
uut_32_2[i] = new UInt256(base_32_2[i]);
}

var checksum0 = Benchmark(() =>
{
var checksum = 0;
for(var i=0; i<MAX_TESTS; i++)
{
checksum += uut_32_1[i].CompareTo(uut_32_2[i]);
}
return checksum;
}).Item2;

var checksum1 = Benchmark(() =>
{
var checksum = 0;
for(var i=0; i<MAX_TESTS; i++)
{
checksum += code1_UInt256CompareTo(base_32_1[i], base_32_2[i]);
}
return checksum;
}).Item2;

var checksum2 = Benchmark(() =>
{
var checksum = 0;
for(var i=0; i<MAX_TESTS; i++)
{
checksum += code2_UInt256CompareTo(base_32_1[i], base_32_2[i]);
}
return checksum;
}).Item2;

var checksum3 = Benchmark(() =>
{
var checksum = 0;
for(var i=0; i<MAX_TESTS; i++)
{
checksum += code3_UInt256CompareTo(base_32_1[i], base_32_2[i]);
}
return checksum;
}).Item2;

checksum0.Should().Be(checksum1);
checksum0.Should().Be(checksum2);
checksum0.Should().Be(checksum3);
}

[TestMethod]
public void Benchmark_CompareTo_UInt160()
{
// testing "official UInt160 version"
UInt160[] uut_20_1 = new UInt160[MAX_TESTS];
UInt160[] uut_20_2 = new UInt160[MAX_TESTS];

for(var i=0; i<MAX_TESTS; i++)
{
uut_20_1[i] = new UInt160(base_20_1[i]);
uut_20_2[i] = new UInt160(base_20_2[i]);
}

var checksum0 = Benchmark(() =>
{
var checksum = 0;
for(var i=0; i<MAX_TESTS; i++)
{
checksum += uut_20_1[i].CompareTo(uut_20_2[i]);
}
return checksum;
}).Item2;

var checksum1 = Benchmark(() =>
{
var checksum = 0;
for(var i=0; i<MAX_TESTS; i++)
{
checksum += code1_UInt160CompareTo(base_20_1[i], base_20_2[i]);
}
return checksum;
}).Item2;

var checksum2 = Benchmark(() =>
{
var checksum = 0;
for(var i=0; i<MAX_TESTS; i++)
{
checksum += code2_UInt160CompareTo(base_20_1[i], base_20_2[i]);
}
return checksum;
}).Item2;

var checksum3 = Benchmark(() =>
{
var checksum = 0;
for(var i=0; i<MAX_TESTS; i++)
{
checksum += code3_UInt160CompareTo(base_20_1[i], base_20_2[i]);
}
return checksum;
}).Item2;

checksum0.Should().Be(checksum1);
checksum0.Should().Be(checksum2);
checksum0.Should().Be(checksum3);
}

[TestMethod]
public void Benchmark_UInt_IsCorrect_Self_CompareTo()
{
for(var i=0; i<MAX_TESTS; i++)
{
code1_UInt160CompareTo(base_20_1[i], base_20_1[i]).Should().Be(0);
code2_UInt160CompareTo(base_20_1[i], base_20_1[i]).Should().Be(0);
code3_UInt160CompareTo(base_20_1[i], base_20_1[i]).Should().Be(0);
code1_UInt256CompareTo(base_32_1[i], base_32_1[i]).Should().Be(0);
code2_UInt256CompareTo(base_32_1[i], base_32_1[i]).Should().Be(0);
code3_UInt256CompareTo(base_32_1[i], base_32_1[i]).Should().Be(0);
}
}

private int code1_UInt256CompareTo(byte[] b1, byte[] b2)
{
byte[] x = b1;
byte[] y = b2;
for (int i = x.Length - 1; i >= 0; i--)
{
if (x[i] > y[i])
return 1;
if (x[i] < y[i])
return -1;
}
return 0;
}

private unsafe int code2_UInt256CompareTo(byte[] b1, byte[] b2)
{
fixed (byte* px = b1, py = b2)
{
uint* lpx = (uint*)px;
uint* lpy = (uint*)py;
for (int i = 256/32-1; i >= 0; i--)
{
if (lpx[i] > lpy[i])
return 1;
if (lpx[i] < lpy[i])
return -1;
}
}
return 0;
}

private unsafe int code3_UInt256CompareTo(byte[] b1, byte[] b2)
{
fixed (byte* px = b1, py = b2)
{
ulong* lpx = (ulong*)px;
ulong* lpy = (ulong*)py;
for (int i = 256/64-1; i >= 0; i--)
{
if (lpx[i] > lpy[i])
return 1;
if (lpx[i] < lpy[i])
return -1;
}
}
return 0;
}
private int code1_UInt160CompareTo(byte[] b1, byte[] b2)
{
byte[] x = b1;
byte[] y = b2;
for (int i = x.Length - 1; i >= 0; i--)
{
if (x[i] > y[i])
return 1;
if (x[i] < y[i])
return -1;
}
return 0;
}

private unsafe int code2_UInt160CompareTo(byte[] b1, byte[] b2)
{
fixed (byte* px = b1, py = b2)
{
uint* lpx = (uint*)px;
uint* lpy = (uint*)py;
for (int i = 160/32-1; i >= 0; i--)
{
if (lpx[i] > lpy[i])
return 1;
if (lpx[i] < lpy[i])
return -1;
}
}
return 0;
}

private unsafe int code3_UInt160CompareTo(byte[] b1, byte[] b2)
{
// LSB -----------------> MSB
// --------------------------
// | 8B | 8B | 4B |
// --------------------------
// 0l 1l 4i
// --------------------------
fixed (byte* px = b1, py = b2)
{
uint* ipx = (uint*)px;
uint* ipy = (uint*)py;
if (ipx[4] > ipy[4])
return 1;
if (ipx[4] < ipy[4])
return -1;

ulong* lpx = (ulong*)px;
ulong* lpy = (ulong*)py;
if (lpx[1] > lpy[1])
return 1;
if (lpx[1] < lpy[1])
return -1;
if (lpx[0] > lpy[0])
return 1;
if (lpx[0] < lpy[0])
return -1;
}
return 0;
}

}
}
1 change: 1 addition & 0 deletions neo.UnitTests/neo.UnitTests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
<TargetFramework>netcoreapp2.0</TargetFramework>
<AssemblyName>Neo.UnitTests</AssemblyName>
<RootNamespace>Neo.UnitTests</RootNamespace>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>

<ItemGroup>
Expand Down

0 comments on commit 6849ac2

Please sign in to comment.