From acfdada565f881c5b9d29a6f0c6c3422ea831355 Mon Sep 17 00:00:00 2001 From: Thom Kiesewetter Date: Tue, 23 Aug 2016 01:23:53 +0200 Subject: [PATCH 1/7] Initial Commit Benchmark2 --- DotNetCross.Memory.Copies.sln | 8 +- .../Msvcrt.cs | 13 +- .../Anderman.cs | 238 +++++++++ .../Anderman2.cs | 73 +++ ...otNetCross.Memory.Copies.Benchmarks2.xproj | 19 + .../Program.cs | 136 ++++++ .../Properties/AssemblyInfo.cs | 19 + .../UnsafeBufferMemmoveJamesqo2.cs | 459 ++++++++++++++++++ .../msvcrtMemove.cs | 23 + .../project.json | 24 + 10 files changed, 1003 insertions(+), 9 deletions(-) create mode 100644 src/DotNetCross.Memory.Copies.Benchmarks2/Anderman.cs create mode 100644 src/DotNetCross.Memory.Copies.Benchmarks2/Anderman2.cs create mode 100644 src/DotNetCross.Memory.Copies.Benchmarks2/DotNetCross.Memory.Copies.Benchmarks2.xproj create mode 100644 src/DotNetCross.Memory.Copies.Benchmarks2/Program.cs create mode 100644 src/DotNetCross.Memory.Copies.Benchmarks2/Properties/AssemblyInfo.cs create mode 100644 src/DotNetCross.Memory.Copies.Benchmarks2/UnsafeBufferMemmoveJamesqo2.cs create mode 100644 src/DotNetCross.Memory.Copies.Benchmarks2/msvcrtMemove.cs create mode 100644 src/DotNetCross.Memory.Copies.Benchmarks2/project.json diff --git a/DotNetCross.Memory.Copies.sln b/DotNetCross.Memory.Copies.sln index 6472262..71680e3 100644 --- a/DotNetCross.Memory.Copies.sln +++ b/DotNetCross.Memory.Copies.sln @@ -1,10 +1,12 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 14 -VisualStudioVersion = 14.0.24720.0 +VisualStudioVersion = 14.0.25420.1 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DotNetCross.Memory.Copies.Benchmarks", "src\DotNetCross.Memory.Copies.Benchmarks\DotNetCross.Memory.Copies.Benchmarks.csproj", "{DF33082A-C7BE-4305-BCAD-159D667C9420}" EndProject +Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "DotNetCross.Memory.Copies.Benchmarks2", "src\DotNetCross.Memory.Copies.Benchmarks2\DotNetCross.Memory.Copies.Benchmarks2.xproj", "{9396CFEF-F925-4202-98E7-1F0759DF0C5B}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -15,6 +17,10 @@ Global {DF33082A-C7BE-4305-BCAD-159D667C9420}.Debug|Any CPU.Build.0 = Debug|Any CPU {DF33082A-C7BE-4305-BCAD-159D667C9420}.Release|Any CPU.ActiveCfg = Release|Any CPU {DF33082A-C7BE-4305-BCAD-159D667C9420}.Release|Any CPU.Build.0 = Release|Any CPU + {9396CFEF-F925-4202-98E7-1F0759DF0C5B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9396CFEF-F925-4202-98E7-1F0759DF0C5B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9396CFEF-F925-4202-98E7-1F0759DF0C5B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9396CFEF-F925-4202-98E7-1F0759DF0C5B}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/DotNetCross.Memory.Copies.Benchmarks/Msvcrt.cs b/src/DotNetCross.Memory.Copies.Benchmarks/Msvcrt.cs index 615176a..c548286 100644 --- a/src/DotNetCross.Memory.Copies.Benchmarks/Msvcrt.cs +++ b/src/DotNetCross.Memory.Copies.Benchmarks/Msvcrt.cs @@ -12,17 +12,14 @@ public static unsafe void Memmove(byte[] src, int srcOffset, byte[] dst, int dst if (srcOffset + count > src.Length) throw new ArgumentException(nameof(src)); if (dstOffset + count > dst.Length) throw new ArgumentException(nameof(dst)); - fixed (byte* srcOrigin = src) - fixed (byte* dstOrigin = dst) + fixed (byte* pSrcOrigin = &src[srcOffset]) + fixed (byte* pDstOrigin = &dst[dstOffset]) { - var pSrc = srcOrigin + srcOffset; - var pDst = dstOrigin + dstOffset; - - memmove(pDst, pSrc, count); + memmove(pDstOrigin, pSrcOrigin, count); } } [DllImport("msvcrt.dll", SetLastError = false)] - public static unsafe extern IntPtr memmove(void* dest, void* src, int count); + public static extern unsafe IntPtr memmove(void* dest, void* src, int count); } -} +} \ No newline at end of file diff --git a/src/DotNetCross.Memory.Copies.Benchmarks2/Anderman.cs b/src/DotNetCross.Memory.Copies.Benchmarks2/Anderman.cs new file mode 100644 index 0000000..fd7f0a3 --- /dev/null +++ b/src/DotNetCross.Memory.Copies.Benchmarks2/Anderman.cs @@ -0,0 +1,238 @@ +using System; +using System.Numerics; +using System.Runtime.CompilerServices; + +namespace DotNetCross.Memory.Copies.Benchmarks +{ + // https://github.com/dotnet/coreclr/issues/2430#issuecomment-166566393 + public static class UnsafeAnderman + { + /// + /// Copies a specified number of bytes from a source array starting at a particular + /// offset to a destination array starting at a particular offset, not safe for overlapping data. + /// + /// The source buffer + /// The zero-based byte offset into src + /// The destination buffer + /// The zero-based byte offset into dst + /// The number of bytes to copy + /// or is null + /// , , or is less than 0 + /// + /// The number of bytes in src is less + /// than srcOffset plus count.-or- The number of bytes in dst is less than dstOffset + /// plus count. + /// + /// + /// Code must be optimized, in release mode and .IsHardwareAccelerated must be true for the performance benefits. + /// + public static unsafe void VectorizedCopy(byte[] src, int srcOffset, byte[] dst, int dstOffset, int count) + { + if (count > 512 + 64) + { + // TEST: Disable Array-Copy fall back + // In-built copy faster for large arrays (vs repeated bounds checks on Vector.ctor?) + //Array.Copy(src, srcOffset, dst, dstOffset, count); + //return; + } + var orgCount = count; + + if (src == null || dst == null) throw new ArgumentNullException(nameof(src)); + if (count < 0 || srcOffset < 0 || dstOffset < 0) throw new ArgumentOutOfRangeException(nameof(count)); + if (srcOffset + count > src.Length) throw new ArgumentException(nameof(src)); + if (dstOffset + count > dst.Length) throw new ArgumentException(nameof(dst)); + + fixed (byte* srcOrigin = src) + fixed (byte* dstOrigin = dst) + { + var pSrc = srcOrigin + srcOffset; + var pDst = dstOrigin + dstOffset; + while (count >= Vector.Count) + { + Unsafe.Write(pDst, Unsafe.Read>(pSrc)); + count -= Vector.Count; + pSrc += Vector.Count; + pDst += Vector.Count; + } + if (orgCount > Vector.Count) + { + // Is this right? What about offset? + //new Vector(src, orgCount - Vector.Count).CopyTo(dst, orgCount - Vector.Count); + var offset = orgCount - Vector.Count; + Unsafe.Write(dstOrigin + offset, Unsafe.Read>(srcOrigin + offset)); + return; + } + switch (count) + { + case 1: + pDst[0] = pSrc[0]; + return; + + case 2: + *((short*)pDst) = *((short*)pSrc); + return; + + case 3: + *((short*)pDst) = *((short*)pSrc); + pDst[2] = pSrc[2]; + return; + + case 4: + *((int*)pDst) = *((int*)pSrc); + return; + + case 5: + *((int*)pDst) = *((int*)pSrc); + pDst[4] = pSrc[4]; + return; + + case 6: + *((int*)pDst) = *((int*)pSrc); + *((short*)(pDst + 4)) = *((short*)(pSrc + 4)); + return; + + case 7: + *((int*)pDst) = *((int*)pSrc); + *((short*)(pDst + 4)) = *((short*)(pSrc + 4)); + pDst[6] = pSrc[6]; + return; + + case 8: + *((long*)pDst) = *((long*)pSrc); + return; + + case 9: + *((long*)pDst) = *((long*)pSrc); + pDst[8] = pSrc[8]; + return; + + case 10: + *((long*)pDst) = *((long*)pSrc); + *((short*)(pDst + 8)) = *((short*)(pSrc + 8)); + return; + + case 11: + *((long*)pDst) = *((long*)pSrc); + *((short*)(pDst + 8)) = *((short*)(pSrc + 8)); + pDst[10] = pSrc[10]; + return; + + case 12: + *((long*)pDst) = *((long*)pSrc); + *((int*)(pDst + 8)) = *((int*)(pSrc + 8)); + return; + + case 13: + *((long*)pDst) = *((long*)pSrc); + *((int*)(pDst + 8)) = *((int*)(pSrc + 8)); + pDst[12] = pSrc[12]; + return; + + case 14: + *((long*)pDst) = *((long*)pSrc); + *((int*)(pDst + 8)) = *((int*)(pSrc + 8)); + *((short*)(pDst + 12)) = *((short*)(pSrc + 12)); + return; + + case 15: + *((long*)pDst) = *((long*)pSrc); + *((int*)(pDst + 8)) = *((int*)(pSrc + 8)); + *((short*)(pDst + 12)) = *((short*)(pSrc + 12)); + pDst[14] = pSrc[14]; + return; + + } + } + } + public static unsafe void UnsafeVectorizedCopy2(byte* pDst, byte* pSrc, int count) + { + while (count >= Vector.Count) + { + Unsafe.Write(pDst, Unsafe.Read>(pSrc)); + count -= Vector.Count; + pSrc += Vector.Count; + pDst += Vector.Count; + } + switch (count) + { + case 1: + pDst[0] = pSrc[0]; + return; + + case 2: + *((short*)pDst) = *((short*)pSrc); + return; + + case 3: + *((short*)pDst) = *((short*)pSrc); + pDst[2] = pSrc[2]; + return; + + case 4: + *((int*)pDst) = *((int*)pSrc); + return; + + case 5: + *((int*)pDst) = *((int*)pSrc); + pDst[4] = pSrc[4]; + return; + + case 6: + *((int*)pDst) = *((int*)pSrc); + *((short*)(pDst + 4)) = *((short*)(pSrc + 4)); + return; + + case 7: + *((int*)pDst) = *((int*)pSrc); + *((short*)(pDst + 4)) = *((short*)(pSrc + 4)); + pDst[6] = pSrc[6]; + return; + + case 8: + *((long*)pDst) = *((long*)pSrc); + return; + + case 9: + *((long*)pDst) = *((long*)pSrc); + pDst[8] = pSrc[8]; + return; + + case 10: + *((long*)pDst) = *((long*)pSrc); + *((short*)(pDst + 8)) = *((short*)(pSrc + 8)); + return; + + case 11: + *((long*)pDst) = *((long*)pSrc); + *((short*)(pDst + 8)) = *((short*)(pSrc + 8)); + pDst[10] = pSrc[10]; + return; + + case 12: + *((long*)pDst) = *((long*)pSrc); + *((int*)(pDst + 8)) = *((int*)(pSrc + 8)); + return; + + case 13: + *((long*)pDst) = *((long*)pSrc); + *((int*)(pDst + 8)) = *((int*)(pSrc + 8)); + pDst[12] = pSrc[12]; + return; + + case 14: + *((long*)pDst) = *((long*)pSrc); + *((int*)(pDst + 8)) = *((int*)(pSrc + 8)); + *((short*)(pDst + 12)) = *((short*)(pSrc + 12)); + return; + + case 15: + *((long*)pDst) = *((long*)pSrc); + *((int*)(pDst + 8)) = *((int*)(pSrc + 8)); + *((short*)(pDst + 12)) = *((short*)(pSrc + 12)); + pDst[14] = pSrc[14]; + return; + + } + } + } +} diff --git a/src/DotNetCross.Memory.Copies.Benchmarks2/Anderman2.cs b/src/DotNetCross.Memory.Copies.Benchmarks2/Anderman2.cs new file mode 100644 index 0000000..ecef800 --- /dev/null +++ b/src/DotNetCross.Memory.Copies.Benchmarks2/Anderman2.cs @@ -0,0 +1,73 @@ +using System; +using System.Numerics; +using System.Runtime.CompilerServices; + +namespace test +{ + public static class Anderman2 { + public static unsafe void VectorizedCopyAnderman(byte[] src, int srcOffset, byte[] dst, int dstOffset, int count) + { + const int alignment = 0x10; + const int mask = alignment - 1; + if (src == null || dst == null) throw new ArgumentNullException(nameof(src)); + if (srcOffset + count > src.Length) throw new ArgumentException(nameof(src)); + if (count < 0 || srcOffset < 0 || dstOffset < 0) throw new ArgumentOutOfRangeException(nameof(count)); + if (dstOffset + count > dst.Length) throw new ArgumentException(nameof(dst)); + if (count == 0) return; + fixed (byte* pSrcOrigin = &src[srcOffset]) + fixed (byte* pDstOrigin = &dst[dstOffset]) + { + var pSrc = pSrcOrigin; + var pDst = pDstOrigin; + if (count >= 8) + { + *(long*) pDst = *(long*) pSrc; + *(long*) (pDst + (count & 0xf) - 8) = *(long*) (pSrc + (count & 0xf) - 8); + } + else if (count >= 4) + { + *(int*) pDst = *(int*) pSrc; + *(int*) (pDst + (count & 0xf) - 4) = *(int*) (pSrc + (count & 0xf) - 4); + } + else if (count >= 2) + { + *(short*) pDst = *(short*) pSrc; + *(short*) (pDst + (count & 0xf) - 2) = *(short*) (pSrc + (count & 0xf) - 2); + } + else + { + *(pDst + 0) = *(pSrc + 0); + } + if (count >= Vector.Count) + { + pSrc -= (long) pDst; + count += -alignment + (int) ((ulong) pDst & mask); + pDst += alignment - ((ulong) pDst & mask); + + while (count >= 4*Vector.Count) + { + var x1 = Unsafe.Read>(pSrc + (long) pDst); + var x2 = Unsafe.Read>(pSrc + (long) pDst + Vector.Count); + count -= 4*Vector.Count; + Unsafe.Write(pDst, x1); + Unsafe.Write(pDst + Vector.Count, x2); + pDst += 4*Vector.Count; + x1 = Unsafe.Read>(pSrc + (long) pDst - 2*Vector.Count); + x2 = Unsafe.Read>(pSrc + (long) pDst - Vector.Count); + Unsafe.Write(pDst - 2*Vector.Count, x1); + Unsafe.Write(pDst - Vector.Count, x2); + } + while (count >= 1*Vector.Count) + { + var x1 = Unsafe.Read>(pSrc + (long) pDst); + count -= Vector.Count; + Unsafe.Write(pDst, x1); + pDst += Vector.Count; + } + pDst += count - Vector.Count; + Unsafe.Write(pDst, Unsafe.Read>(pSrc + (long) pDst)); + } + } + } + } +} \ No newline at end of file diff --git a/src/DotNetCross.Memory.Copies.Benchmarks2/DotNetCross.Memory.Copies.Benchmarks2.xproj b/src/DotNetCross.Memory.Copies.Benchmarks2/DotNetCross.Memory.Copies.Benchmarks2.xproj new file mode 100644 index 0000000..84fc08c --- /dev/null +++ b/src/DotNetCross.Memory.Copies.Benchmarks2/DotNetCross.Memory.Copies.Benchmarks2.xproj @@ -0,0 +1,19 @@ + + + + 14.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + + 9396cfef-f925-4202-98e7-1f0759df0c5b + test + .\obj + .\bin\ + v4.6.1 + + + 2.0 + + + \ No newline at end of file diff --git a/src/DotNetCross.Memory.Copies.Benchmarks2/Program.cs b/src/DotNetCross.Memory.Copies.Benchmarks2/Program.cs new file mode 100644 index 0000000..d92c68b --- /dev/null +++ b/src/DotNetCross.Memory.Copies.Benchmarks2/Program.cs @@ -0,0 +1,136 @@ +using System; +using System.Diagnostics; + +namespace test +{ + public class Program + { + private const long _max = 30000000; + private const int BufferSize = 1024*1024*1024; + private static readonly byte[] _src = new byte[BufferSize]; + private static readonly byte[] _dst = new byte[BufferSize]; + + public static void Main(string[] args) + { + Console.WriteLine("Initalizing setup random"); + + Console.WriteLine("Test en compile functions"); + InitArray(Array.Copy); + InitArray(MsvcrtMemove.MsvcrtMemmove); + InitArray(Anderman2.VectorizedCopyAnderman); + InitArray(UnsafeBufferMemmoveJamesqo2.Memmove); + + + Console.WriteLine($"bytes\titerations\tarray\tmsmemmove\tanderman\tUnsafeBufferMemmoveJamesqo2"); + + foreach (var copyBytes in new[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, 64, 66, 68, 70, 72, 74, 76, 78, 80, 82, 84, 86, 88, 90, 92, 94, 96}) + //foreach (var copyBytes in new[] { 128,256,512,1024,2048,4096,8192,16384,32768,65536}) + //foreach (var copyBytes in new[] { 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16}) + //foreach (var copyBytes in new[] {103, 113, 122, 128, 135, 145, 154, 160, 167, 177, 186, 192, 199, 209, 218, 224, 231, 241, 250, 256, 263, 273, 282, 288, 295, 305, 314, 320, 327, 337, 346, 352, 359, 369, 378, 384, 391, 401, 410, 416, 423, 433, 442, 448, 455, 465, 474, 480, 487, 497, 506, 512, 519, 529, 538, 544, 551, 561, 570, 576, 583, 593, 602, 608, 615, 625, 634, 640, 647, 657, 666, 672, 679, 689, 698, 704, 711, 721, 730, 736, 743, 753, 762, 768, 775, 785, 794, 800, 807, 817, 826, 832, 839, 849, 858, 864, 871, 881, 890, 896, 903, 913, 922, 928, 935, 945, 954, 960, 967, 977, 986, 1024, 1086, 1155, 1223, 1280, 1342, 1411, 1479, 1536, 1598, 1667, 1735, 1792, 1854, 1923, 1991, 2048, 2110, 2179, 2247, 2304, 2366, 2435, 2503, 2560, 2622, 2691, 2759, 2816, 2878, 2947, 3015, 3072, 3134, 3203, 3271, 3328, 3390, 3459, 3527, 3584, 3646, 3715, 3783, 3840, 3902, 3971, 4039, 4096, 4158, 4227, 4295, 4352, 4414, 4483, 4551, 4608, 4670, 4739, 4807, 4864, 4926, 4995, 5063, 5120, 5182, 5251, 5319, 5376, 5438, 5507, 5575, 5632, 5694, 5763, 5831, 5888, 5950, 6019, 6087, 6144, 6206, 6275, 6343, 6400, 6462, 6531, 6599, 6656, 6718, 6787, 6855, 6912, 6974, 7043, 7111, 7168, 7230, 7299, 7367, 7424, 7486, 7555, 7623, 7680, 7742, 7811, 7879, 7936, 7998, 8067, 8135, 8192, 8254, 8323, 8391}) + { + Test(copyBytes); + } + Console.WriteLine("ready"); + Console.ReadKey(); + } + + private static void InitArray(Action copyAction) + { + for (var i = 1; i < 256; i++) + { + _src[i] = (byte) i; + } + for (var i = 0; i < 256; i++) + { + copyAction(_src, 0, _dst, 0, i); + for (var j = 0; i < 256; i++) + { + if (_src[j] != _dst[j]) + throw new Exception($"i={i} _src[j]({_src[j]})!= _dst[j]({_dst[j]})"); + } + } + } + + private static void Test(int copyBytes) + { + var iterations = (int) _max/(copyBytes == 0 ? 1 : copyBytes); + + var s0 = TestArrayCopy(copyBytes, iterations); + iterations = (int) (iterations*(2000.0/s0.ElapsedMilliseconds)); + + var s4 = TestUnsafeBufferMemmoveJamesqo2(copyBytes, iterations); + var s1 = TestArrayCopy(copyBytes, iterations); + var s2 = TestMsMemmove(copyBytes, iterations); + var s3 = TestVectorizedIftree(copyBytes, iterations); + + double baseline = s1.Elapsed.Ticks; + Console.Write($"{copyBytes}"); + Console.Write($"\t{iterations}"); + Console.Write($"\t{(double) s1.Elapsed.Ticks/baseline: 0.00}"); + Console.Write($"\t{(double) s2.Elapsed.Ticks/baseline: 0.00}"); + Console.Write($"\t{(double) s3.Elapsed.Ticks/baseline: 0.00}"); + Console.Write($"\t{(double) s4.Elapsed.Ticks/baseline: 0.00}"); + Console.Write($"\t{s1.Elapsed.TotalMilliseconds*1000000/iterations: 0.00}"); + Console.Write($"\t{s2.Elapsed.TotalMilliseconds*1000000/iterations: 0.00}"); + Console.Write($"\t{s3.Elapsed.TotalMilliseconds*1000000/iterations: 0.00}"); + Console.Write($"\t{s4.Elapsed.TotalMilliseconds*1000000/iterations: 0.00}"); + Console.WriteLine(); + } + + private static Stopwatch TestVectorizedIftree(int copyBytes, int interations) + { + var sw = Stopwatch.StartNew(); + var offset = 0; + for (long i = 0; i < interations; i++) + { + offset += 0x4731; + if (offset + copyBytes >= BufferSize) offset &= 0x3fff; + Anderman2.VectorizedCopyAnderman(_src, offset, _dst, offset, copyBytes); + } + sw.Stop(); + return sw; + } + + private static Stopwatch TestArrayCopy(int copyBytes, int interations) + { + var sw = Stopwatch.StartNew(); + var offset = 0; + for (long i = 0; i < interations; i++) + { + offset += 0x4731; + if (offset + copyBytes >= BufferSize) offset &= 0x3fff; + Array.Copy(_src, offset, _dst, offset, copyBytes); + } + sw.Stop(); + return sw; + } + + private static Stopwatch TestUnsafeBufferMemmoveJamesqo2(int copyBytes, int interations) + { + var sw = Stopwatch.StartNew(); + var offset = 0; + for (long i = 0; i < interations; i++) + { + offset += 0x4731; + if (offset + copyBytes >= BufferSize) offset &= 0x3fff; + UnsafeBufferMemmoveJamesqo2.Memmove(_src, offset, _dst, offset, copyBytes); + } + sw.Stop(); + return sw; + } + + private static Stopwatch TestMsMemmove(int copyBytes, int interations) + { + var sw = Stopwatch.StartNew(); + var offset = 0; + for (long i = 0; i < interations; i++) + { + offset += 0x4731; + if (offset + copyBytes >= BufferSize) offset &= 0x3fff; + MsvcrtMemove.MsvcrtMemmove(_src, offset, _dst, offset, copyBytes); + } + sw.Stop(); + return sw; + } + } +} \ No newline at end of file diff --git a/src/DotNetCross.Memory.Copies.Benchmarks2/Properties/AssemblyInfo.cs b/src/DotNetCross.Memory.Copies.Benchmarks2/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..2feeb81 --- /dev/null +++ b/src/DotNetCross.Memory.Copies.Benchmarks2/Properties/AssemblyInfo.cs @@ -0,0 +1,19 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("test")] +[assembly: AssemblyTrademark("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("dbae07cb-2303-4f98-953f-f4128a3c9680")] diff --git a/src/DotNetCross.Memory.Copies.Benchmarks2/UnsafeBufferMemmoveJamesqo2.cs b/src/DotNetCross.Memory.Copies.Benchmarks2/UnsafeBufferMemmoveJamesqo2.cs new file mode 100644 index 0000000..21eb199 --- /dev/null +++ b/src/DotNetCross.Memory.Copies.Benchmarks2/UnsafeBufferMemmoveJamesqo2.cs @@ -0,0 +1,459 @@ +#define BIT64 +#define AMD64 +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; +#if BIT64 +using nuint = System.UInt64; +#else // BIT64 +using nuint = System.UInt32; +#endif // BIT64 +namespace test +{ + public class UnsafeBufferMemmoveJamesqo2 + { + public static unsafe void Memmove(byte[] src, int srcOffset, byte[] dst, int dstOffset, int count) + { + if (src == null || dst == null) throw new ArgumentNullException(nameof(src)); + if (count < 0 || srcOffset < 0 || dstOffset < 0) throw new ArgumentOutOfRangeException(nameof(count)); + if (srcOffset + count > src.Length) throw new ArgumentException(nameof(src)); + if (dstOffset + count > dst.Length) throw new ArgumentException(nameof(dst)); + + fixed (byte* srcOrigin = src) + fixed (byte* dstOrigin = dst) + { + var pSrc = srcOrigin + srcOffset; + var pDst = dstOrigin + dstOffset; + + Memmove(pDst, pSrc, (nuint)count); + } + } + + [System.Security.SecurityCritical] + internal static unsafe void Memmove(byte* dest, byte* src, nuint len) + { + // P/Invoke into the native version when the buffers are overlapping and the copy needs to be performed backwards + // This check can produce false positives for lengths greater than int.MaxValue. It is fine because we want to use the P/Invoke path for the large lengths anyway. + + //if ((nuint)dest - (nuint)src < len) goto PInvoke; + + // This is portable version of memcpy. It mirrors what the hand optimized assembly versions of memcpy typically do. + // + // Ideally, we would just use the cpblk IL instruction here. Unfortunately, cpblk IL instruction is not as efficient as + // possible yet and so we have this implementation here for now. + + // Note: It's important that this switch handles lengths at least up to 15 for AMD64. + // We assume below len is at least 16 and make one 128-bit write without checking. + + // The switch will be very fast since it can be implemented using a jump + // table in assembly. See http://stackoverflow.com/a/449297/4077294 for more info. + + switch (len) + { + case 0: + return; + case 1: + *dest = *src; + return; + case 2: + *(short*)dest = *(short*)src; + return; + case 3: + *(short*)dest = *(short*)src; + *(dest + 2) = *(src + 2); + return; + case 4: + *(int*)dest = *(int*)src; + return; + case 5: + *(int*)dest = *(int*)src; + *(dest + 4) = *(src + 4); + return; + case 6: + *(int*)dest = *(int*)src; + *(short*)(dest + 4) = *(short*)(src + 4); + return; + case 7: + *(int*)dest = *(int*)src; + *(short*)(dest + 4) = *(short*)(src + 4); + *(dest + 6) = *(src + 6); + return; + case 8: +#if BIT64 + *(long*)dest = *(long*)src; +#else // BIT64 + *(int*)dest = *(int*)src; + *(int*)(dest + 4) = *(int*)(src + 4); +#endif // BIT64 + return; + case 9: +#if BIT64 + *(long*)dest = *(long*)src; +#else // BIT64 + *(int*)dest = *(int*)src; + *(int*)(dest + 4) = *(int*)(src + 4); +#endif // BIT64 + *(dest + 8) = *(src + 8); + return; + case 10: +#if BIT64 + *(long*)dest = *(long*)src; +#else // BIT64 + *(int*)dest = *(int*)src; + *(int*)(dest + 4) = *(int*)(src + 4); +#endif // BIT64 + *(short*)(dest + 8) = *(short*)(src + 8); + return; + case 11: +#if BIT64 + *(long*)dest = *(long*)src; +#else // BIT64 + *(int*)dest = *(int*)src; + *(int*)(dest + 4) = *(int*)(src + 4); +#endif // BIT64 + *(short*)(dest + 8) = *(short*)(src + 8); + *(dest + 10) = *(src + 10); + return; + case 12: +#if BIT64 + *(long*)dest = *(long*)src; +#else // BIT64 + *(int*)dest = *(int*)src; + *(int*)(dest + 4) = *(int*)(src + 4); +#endif // BIT64 + *(int*)(dest + 8) = *(int*)(src + 8); + return; + case 13: +#if BIT64 + *(long*)dest = *(long*)src; +#else // BIT64 + *(int*)dest = *(int*)src; + *(int*)(dest + 4) = *(int*)(src + 4); +#endif // BIT64 + *(int*)(dest + 8) = *(int*)(src + 8); + *(dest + 12) = *(src + 12); + return; + case 14: +#if BIT64 + *(long*)dest = *(long*)src; +#else // BIT64 + *(int*)dest = *(int*)src; + *(int*)(dest + 4) = *(int*)(src + 4); +#endif // BIT64 + *(int*)(dest + 8) = *(int*)(src + 8); + *(short*)(dest + 12) = *(short*)(src + 12); + return; + case 15: +#if BIT64 + *(long*)dest = *(long*)src; +#else // BIT64 + *(int*)dest = *(int*)src; + *(int*)(dest + 4) = *(int*)(src + 4); +#endif // BIT64 + *(int*)(dest + 8) = *(int*)(src + 8); + *(short*)(dest + 12) = *(short*)(src + 12); + *(dest + 14) = *(src + 14); + return; + } + + // P/Invoke into the native version for large lengths. + // Currently the threshold at which the native version is faster seems to be around 8192 + // on amd64 Windows, but this is subject to change if this implementation can be made faster. + //if (len >= 8192) goto PInvoke; + + // So far SIMD is only enabled for AMD64, so on that plaform we want + // to 16-byte align while on others (including arm64) we'll want to word-align +#if AMD64 + nuint alignment = 16u; +#else // AMD64 + nuint alignment = (nuint)sizeof(nuint); +#endif // AMD64 + + // (nuint)dest % alignment calculates how far we are from the previous aligned address + // Note that it's *very* important alignment is unsigned. + // (int)dest % (int)alignment for example will give different results if the lhs is negative. + + // If dest is aligned this will be 0. + nuint i = (nuint)dest % alignment; + + // We know from the above switch-case that len is at least 16, so here + // we subtract i from 16. This represents the furthest aligned address + // we know it's okay to write upto. + // To make it clearer, (dest + i) after this is equivalent to + // [previous aligned address] + 16. + i = 16u - i; + +#if AMD64 + // SIMD is enabled for AMD64, so take advantage of that and use movdqu + *(Buffer16*)dest = *(Buffer16*)src; +#elif ARM64 + // ARM64 has 64-bit words but no SIMD yet, so make 2 word writes + // First one isn't aligned, second one is (remember from earlier notes dest + i is 8-aligned) + *(long*)dest = *(long*)src; + *(long*)(dest + i - 8) = *(long*)(src + i - 8); +#else // AMD64, ARM64 + // i386 and ARM: 32-bit words, no SIMD (yet) + // make 1 unaligned word write, then 3 4-byte aligned ones + *(int*)dest = *(int*)src; + *(int*)(dest + i - 12) = *(int*)(src + i - 12); + *(int*)(dest + i - 8) = *(int*)(src + i - 8); + *(int*)(dest + i - 4) = *(int*)(src + i - 4); +#endif // AMD64, ARM64 + + // i now represents the number of bytes we've copied so far. + //Contract.Assert(i <= len && i > 0 && i <= 16); + //Contract.Assert((nuint)(dest + i) % alignment == 0); + + // chunk: bytes processed per iteration in unrolled loop + // Note: Not directly related to sizeof(nuint), e.g. sizeof(nuint) * 8 is not a valid substitution. + nuint chunk = sizeof(nuint) == 4 ? 32 : 64; + + // mask: represents how many bytes are left after alignment + // Since we copy the bytes in chunks of 2, mask will also have the lower few + // bits of mask (mask & (chunk - 1), but we don't explicitly calculate that) + // will represent how many bytes are left *after* the unrolled loop. + nuint mask = len - i; + + // Protect ourselves from unsigned overflow + if (len < chunk) + goto LoopCleanup; + + // end: point after which we stop the unrolled loop + // This is the end of the buffer, minus the space + // required for 1 iteration of the loop. + nuint end = len - chunk; + + // This can return false in the first iteration if the process of + // aligning the pointer for writes has not left enough space + // for this loop to run, so unfortunately this can't be a do-while loop. + while (i <= end) + { + // Some versions of this loop looks very costly since there appear + // to be a bunch of temporary values being created with the adds, + // but the jit (for x86 anyways) will convert each of these to + // use memory addressing operands. + + // So the only cost is a bit of code size, which is made up for by the fact that + // we save on writes to dest/src. + +#if AMD64 + // Write 64 bytes at a time, taking advantage of xmm register on AMD64 + // This will be translated to 4 movdqus (maybe movdqas in the future, dotnet/coreclr#2725) + *(Buffer64*)(dest + i) = *(Buffer64*)(src + i); +#elif ARM64 + // ARM64: Also unroll by 64 bytes, this time using longs since we don't + // take advantage of SIMD for that plaform yet. + *(long*)(dest + i) = *(long*)(src + i); + *(long*)(dest + i + 8) = *(long*)(src + i + 8); + *(long*)(dest + i + 16) = *(long*)(src + i + 16); + *(long*)(dest + i + 24) = *(long*)(src + i + 24); + *(long*)(dest + i + 32) = *(long*)(src + i + 32); + *(long*)(dest + i + 40) = *(long*)(src + i + 40); + *(long*)(dest + i + 48) = *(long*)(src + i + 48); + *(long*)(dest + i + 56) = *(long*)(src + i + 56); +#else // AMD64, ARM64 + // i386/ARM32: + // Write 32 bytes at a time, via 8 32-bit word writes + *(int*)(dest + i) = *(int*)(src + i); + *(int*)(dest + i + 4) = *(int*)(src + i + 4); + *(int*)(dest + i + 8) = *(int*)(src + i + 8); + *(int*)(dest + i + 12) = *(int*)(src + i + 12); + *(int*)(dest + i + 16) = *(int*)(src + i + 16); + *(int*)(dest + i + 20) = *(int*)(src + i + 20); + *(int*)(dest + i + 24) = *(int*)(src + i + 24); + *(int*)(dest + i + 28) = *(int*)(src + i + 28); +#endif // AMD64, ARM64 + + i += chunk; + } + + LoopCleanup: + // If we've reached this point, there are at most chunk - 1 bytes left + +#if BIT64 + // mask & 63 represents how many bytes there are left. + // if the mask & 32 bit is set that means this number + // will be >= 32. (same principle applies for other + // powers of 2 below) + if ((mask & 32) != 0) + { +#if AMD64 + *(Buffer32*)(dest + i) = *(Buffer32*)(src + i); +#else // AMD64 + *(long*)(dest + i) = *(long*)(src + i); + *(long*)(dest + i + 8) = *(long*)(src + i + 8); + *(long*)(dest + i + 16) = *(long*)(src + i + 16); + *(long*)(dest + i + 24) = *(long*)(src + i + 24); +#endif // AMD64 + + i += 32; + } +#endif // BIT64 + + // Now there can be at most 31 bytes left + + if ((mask & 16) != 0) + { +#if AMD64 + *(Buffer16*)(dest + i) = *(Buffer16*)(src + i); +#elif ARM64 + *(long*)(dest + i) = *(long*)(src + i); + *(long*)(dest + i + 8) = *(long*)(src + i + 8); +#else // AMD64, ARM64 + *(int*)(dest + i) = *(int*)(src + i); + *(int*)(dest + i + 4) = *(int*)(src + i + 4); + *(int*)(dest + i + 8) = *(int*)(src + i + 8); + *(int*)(dest + i + 12) = *(int*)(src + i + 12); +#endif // AMD64, ARM64 + + i += 16; + } + + // Now there can be at most 15 bytes left + // For AMD64 we just want to make 1 (potentially) unaligned xmm write and quit. + // For other platforms we have another switch-case for 0..15. + // Again, this is implemented with a jump table so it's very fast. + +#if AMD64 + i = len - 16; + *(Buffer16*)(dest + i) = *(Buffer16*)(src + i); +#else // AMD64 + + switch (mask & 15) + { + case 0: + // No-op: We already finished copying all the bytes. + return; + case 1: + *(dest + i) = *(src + i); + return; + case 2: + *(short*)(dest + i) = *(short*)(src + i); + return; + case 3: + *(short*)(dest + i) = *(short*)(src + i); + *(dest + i + 2) = *(src + i + 2); + return; + case 4: + *(int*)(dest + i) = *(int*)(src + i); + return; + case 5: + *(int*)(dest + i) = *(int*)(src + i); + *(dest + i + 4) = *(src + i + 4); + return; + case 6: + *(int*)(dest + i) = *(int*)(src + i); + *(short*)(dest + i + 4) = *(short*)(src + i + 4); + return; + case 7: + *(int*)(dest + i) = *(int*)(src + i); + *(short*)(dest + i + 4) = *(short*)(src + i + 4); + *(dest + i + 6) = *(src + i + 6); + return; + case 8: +#if BIT64 + *(long*)(dest + i) = *(long*)(src + i); +#else // BIT64 + *(int*)(dest + i) = *(int*)(src + i); + *(int*)(dest + i + 4) = *(int*)(src + i + 4); +#endif // BIT64 + return; + case 9: +#if BIT64 + *(long*)(dest + i) = *(long*)(src + i); +#else // BIT64 + *(int*)(dest + i) = *(int*)(src + i); + *(int*)(dest + i + 4) = *(int*)(src + i + 4); +#endif // BIT64 + *(dest + i + 8) = *(src + i + 8); + return; + case 10: +#if BIT64 + *(long*)(dest + i) = *(long*)(src + i); +#else // BIT64 + *(int*)(dest + i) = *(int*)(src + i); + *(int*)(dest + i + 4) = *(int*)(src + i + 4); +#endif // BIT64 + *(short*)(dest + i + 8) = *(short*)(src + i + 8); + return; + case 11: +#if BIT64 + *(long*)(dest + i) = *(long*)(src + i); +#else // BIT64 + *(int*)(dest + i) = *(int*)(src + i); + *(int*)(dest + i + 4) = *(int*)(src + i + 4); +#endif // BIT64 + *(short*)(dest + i + 8) = *(short*)(src + i + 8); + *(dest + i + 10) = *(src + i + 10); + return; + case 12: +#if BIT64 + *(long*)(dest + i) = *(long*)(src + i); +#else // BIT64 + *(int*)(dest + i) = *(int*)(src + i); + *(int*)(dest + i + 4) = *(int*)(src + i + 4); +#endif // BIT64 + *(int*)(dest + i + 8) = *(int*)(src + i + 8); + return; + case 13: +#if BIT64 + *(long*)(dest + i) = *(long*)(src + i); +#else // BIT64 + *(int*)(dest + i) = *(int*)(src + i); + *(int*)(dest + i + 4) = *(int*)(src + i + 4); +#endif // BIT64 + *(int*)(dest + i + 8) = *(int*)(src + i + 8); + *(dest + i + 12) = *(src + i + 12); + return; + case 14: +#if BIT64 + *(long*)(dest + i) = *(long*)(src + i); +#else // BIT64 + *(int*)(dest + i) = *(int*)(src + i); + *(int*)(dest + i + 4) = *(int*)(src + i + 4); +#endif // BIT64 + *(int*)(dest + i + 8) = *(int*)(src + i + 8); + *(short*)(dest + i + 12) = *(short*)(src + i + 12); + return; + case 15: +#if BIT64 + *(long*)(dest + i) = *(long*)(src + i); +#else // BIT64 + *(int*)(dest + i) = *(int*)(src + i); + *(int*)(dest + i + 4) = *(int*)(src + i + 4); +#endif // BIT64 + *(int*)(dest + i + 8) = *(int*)(src + i + 8); + *(short*)(dest + i + 12) = *(short*)(src + i + 12); + *(dest + i + 14) = *(src + i + 14); + return; + } + +#endif // AMD64 + + return; + + //PInvoke: + //_Memmove(dest, src, len); + + } + + [StructLayout(LayoutKind.Sequential, Size = 64)] + private struct Buffer64 + { + } + + [StructLayout(LayoutKind.Sequential, Size = 32)] + private struct Buffer32 + { + } + + [StructLayout(LayoutKind.Sequential, Size = 16)] + private struct Buffer16 + { + } + } +} \ No newline at end of file diff --git a/src/DotNetCross.Memory.Copies.Benchmarks2/msvcrtMemove.cs b/src/DotNetCross.Memory.Copies.Benchmarks2/msvcrtMemove.cs new file mode 100644 index 0000000..fca3da4 --- /dev/null +++ b/src/DotNetCross.Memory.Copies.Benchmarks2/msvcrtMemove.cs @@ -0,0 +1,23 @@ +using System; +using System.Runtime.InteropServices; + +namespace test +{ + public static class MsvcrtMemove { + + public static unsafe void MsvcrtMemmove(byte[] src, int srcOffset, byte[] dst, int dstOffset, int count) + { + fixed (byte* srcOrigin = src) + fixed (byte* dstOrigin = dst) + { + var pSrc = srcOrigin + srcOffset; + var pDst = dstOrigin + dstOffset; + + memmove(pDst, pSrc, count); + } + } + + [DllImport("msvcrt.dll", SetLastError = false)] + public static extern unsafe IntPtr memmove(void* dest, void* src, int count); + } +} \ No newline at end of file diff --git a/src/DotNetCross.Memory.Copies.Benchmarks2/project.json b/src/DotNetCross.Memory.Copies.Benchmarks2/project.json new file mode 100644 index 0000000..8e1d30e --- /dev/null +++ b/src/DotNetCross.Memory.Copies.Benchmarks2/project.json @@ -0,0 +1,24 @@ +{ + "version": "1.0.0-*", + "buildOptions": { + "emitEntryPoint": true, + "optimize": true, + "allowUnsafe": true + }, + "dependencies": { + "Microsoft.NETCore.App": { + "type": "platform", + "version": "1.0.0" + }, + "runtime.win7-x64.Microsoft.NETCore.Runtime.CoreCLR": "1.0.4", + "System.Runtime.CompilerServices.Unsafe": "4.0.0", + }, + "runtimes": { + "win8-x64": {} + }, + "frameworks": { + "netcoreapp1.0": { + "imports": "dnxcore50" + } + } +} From f2ebca8fca9e5e421cfda0de1b31087b5cccab51 Mon Sep 17 00:00:00 2001 From: Thom Kiesewetter Date: Tue, 23 Aug 2016 01:35:07 +0200 Subject: [PATCH 2/7] typos --- src/DotNetCross.Memory.Copies.Benchmarks2/Program.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/DotNetCross.Memory.Copies.Benchmarks2/Program.cs b/src/DotNetCross.Memory.Copies.Benchmarks2/Program.cs index d92c68b..1ec9485 100644 --- a/src/DotNetCross.Memory.Copies.Benchmarks2/Program.cs +++ b/src/DotNetCross.Memory.Copies.Benchmarks2/Program.cs @@ -12,7 +12,7 @@ public class Program public static void Main(string[] args) { - Console.WriteLine("Initalizing setup random"); + Console.WriteLine("Initalizing "); Console.WriteLine("Test en compile functions"); InitArray(Array.Copy); @@ -39,6 +39,7 @@ private static void InitArray(Action copyAction) for (var i = 1; i < 256; i++) { _src[i] = (byte) i; + _dst[i] = 0; } for (var i = 0; i < 256; i++) { From 0113786f524ee4bdfe1db2d71c6a5c30209b30a8 Mon Sep 17 00:00:00 2001 From: Thom Kiesewetter Date: Tue, 23 Aug 2016 03:57:50 +0200 Subject: [PATCH 3/7] Add test constant and calculations --- .../Program.cs | 26 ++++++++++++++----- 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/src/DotNetCross.Memory.Copies.Benchmarks2/Program.cs b/src/DotNetCross.Memory.Copies.Benchmarks2/Program.cs index 1ec9485..c7c3d4f 100644 --- a/src/DotNetCross.Memory.Copies.Benchmarks2/Program.cs +++ b/src/DotNetCross.Memory.Copies.Benchmarks2/Program.cs @@ -6,7 +6,15 @@ namespace test public class Program { private const long _max = 30000000; + private const double TestTimeInMs = 1000.0; private const int BufferSize = 1024*1024*1024; + + //Use a contant to change the test. Vars has huge impact on the performance + private const int Randomizor = 0x13751; //Pseudo Random Test + private const int AlignmentTest = 1; //Alignment test + private const int Prefetecher = -1; //read seq through array + private const int Arraystep = Prefetecher; //Alignment test + private static readonly byte[] _src = new byte[BufferSize]; private static readonly byte[] _dst = new byte[BufferSize]; @@ -21,7 +29,7 @@ public static void Main(string[] args) InitArray(UnsafeBufferMemmoveJamesqo2.Memmove); - Console.WriteLine($"bytes\titerations\tarray\tmsmemmove\tanderman\tUnsafeBufferMemmoveJamesqo2"); + Console.WriteLine($"bytes\titerations\tarray\tmsmemmove\tanderman\tUnsafeBufferMemmoveJamesqo2\tns\tns\tns\tns\tGB\tGB/s\tGB/s\tGB/s\tGB/s"); foreach (var copyBytes in new[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, 64, 66, 68, 70, 72, 74, 76, 78, 80, 82, 84, 86, 88, 90, 92, 94, 96}) //foreach (var copyBytes in new[] { 128,256,512,1024,2048,4096,8192,16384,32768,65536}) @@ -54,10 +62,11 @@ private static void InitArray(Action copyAction) private static void Test(int copyBytes) { + const double GB = 1024*1024*1024; var iterations = (int) _max/(copyBytes == 0 ? 1 : copyBytes); var s0 = TestArrayCopy(copyBytes, iterations); - iterations = (int) (iterations*(2000.0/s0.ElapsedMilliseconds)); + iterations = (int) (iterations*(TestTimeInMs/s0.ElapsedMilliseconds)); var s4 = TestUnsafeBufferMemmoveJamesqo2(copyBytes, iterations); var s1 = TestArrayCopy(copyBytes, iterations); @@ -75,6 +84,11 @@ private static void Test(int copyBytes) Console.Write($"\t{s2.Elapsed.TotalMilliseconds*1000000/iterations: 0.00}"); Console.Write($"\t{s3.Elapsed.TotalMilliseconds*1000000/iterations: 0.00}"); Console.Write($"\t{s4.Elapsed.TotalMilliseconds*1000000/iterations: 0.00}"); + Console.Write($"\t{(copyBytes * (double)iterations/GB): 0.00} "); + Console.Write($"\t{(copyBytes * (double)iterations /GB)/ (double)(s1.Elapsed.TotalMilliseconds/1000) : 0.00}"); + Console.Write($"\t{(copyBytes * (double)iterations /GB)/ (double)(s2.Elapsed.TotalMilliseconds/1000) : 0.00}"); + Console.Write($"\t{(copyBytes * (double)iterations /GB)/ (double)(s3.Elapsed.TotalMilliseconds/1000) : 0.00}"); + Console.Write($"\t{(copyBytes * (double)iterations /GB)/ (double)(s4.Elapsed.TotalMilliseconds/1000) : 0.00}"); Console.WriteLine(); } @@ -84,7 +98,7 @@ private static Stopwatch TestVectorizedIftree(int copyBytes, int interations) var offset = 0; for (long i = 0; i < interations; i++) { - offset += 0x4731; + offset += Arraystep==-1? copyBytes:Arraystep; if (offset + copyBytes >= BufferSize) offset &= 0x3fff; Anderman2.VectorizedCopyAnderman(_src, offset, _dst, offset, copyBytes); } @@ -98,7 +112,7 @@ private static Stopwatch TestArrayCopy(int copyBytes, int interations) var offset = 0; for (long i = 0; i < interations; i++) { - offset += 0x4731; + offset += Arraystep == -1 ? copyBytes : Arraystep; if (offset + copyBytes >= BufferSize) offset &= 0x3fff; Array.Copy(_src, offset, _dst, offset, copyBytes); } @@ -112,7 +126,7 @@ private static Stopwatch TestUnsafeBufferMemmoveJamesqo2(int copyBytes, int inte var offset = 0; for (long i = 0; i < interations; i++) { - offset += 0x4731; + offset += Arraystep == -1 ? copyBytes : Arraystep; if (offset + copyBytes >= BufferSize) offset &= 0x3fff; UnsafeBufferMemmoveJamesqo2.Memmove(_src, offset, _dst, offset, copyBytes); } @@ -126,7 +140,7 @@ private static Stopwatch TestMsMemmove(int copyBytes, int interations) var offset = 0; for (long i = 0; i < interations; i++) { - offset += 0x4731; + offset += Arraystep == -1 ? copyBytes : Arraystep; if (offset + copyBytes >= BufferSize) offset &= 0x3fff; MsvcrtMemove.MsvcrtMemmove(_src, offset, _dst, offset, copyBytes); } From eda77fd9f473b676f6ec7f723eae49ac953fcffc Mon Sep 17 00:00:00 2001 From: Thom Kiesewetter Date: Tue, 23 Aug 2016 14:32:18 +0200 Subject: [PATCH 4/7] Fix execption with fast test<1ms --- .../Program.cs | 80 ++++++++++++------- .../project.json | 1 + 2 files changed, 52 insertions(+), 29 deletions(-) diff --git a/src/DotNetCross.Memory.Copies.Benchmarks2/Program.cs b/src/DotNetCross.Memory.Copies.Benchmarks2/Program.cs index c7c3d4f..eb7dab4 100644 --- a/src/DotNetCross.Memory.Copies.Benchmarks2/Program.cs +++ b/src/DotNetCross.Memory.Copies.Benchmarks2/Program.cs @@ -1,6 +1,6 @@ using System; using System.Diagnostics; - +using System.Linq; namespace test { public class Program @@ -10,10 +10,11 @@ public class Program private const int BufferSize = 1024*1024*1024; //Use a contant to change the test. Vars has huge impact on the performance + private const int Cached = 0; //Pseudo Random Test private const int Randomizor = 0x13751; //Pseudo Random Test - private const int AlignmentTest = 1; //Alignment test - private const int Prefetecher = -1; //read seq through array - private const int Arraystep = Prefetecher; //Alignment test + private const int Alignment = 1; //Alignment test + private const int Sequence = -1; //read seq through array + private const int TestMode = Alignment; //Alignment test private static readonly byte[] _src = new byte[BufferSize]; private static readonly byte[] _dst = new byte[BufferSize]; @@ -31,15 +32,24 @@ public static void Main(string[] args) Console.WriteLine($"bytes\titerations\tarray\tmsmemmove\tanderman\tUnsafeBufferMemmoveJamesqo2\tns\tns\tns\tns\tGB\tGB/s\tGB/s\tGB/s\tGB/s"); - foreach (var copyBytes in new[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, 64, 66, 68, 70, 72, 74, 76, 78, 80, 82, 84, 86, 88, 90, 92, 94, 96}) - //foreach (var copyBytes in new[] { 128,256,512,1024,2048,4096,8192,16384,32768,65536}) - //foreach (var copyBytes in new[] { 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16}) - //foreach (var copyBytes in new[] {103, 113, 122, 128, 135, 145, 154, 160, 167, 177, 186, 192, 199, 209, 218, 224, 231, 241, 250, 256, 263, 273, 282, 288, 295, 305, 314, 320, 327, 337, 346, 352, 359, 369, 378, 384, 391, 401, 410, 416, 423, 433, 442, 448, 455, 465, 474, 480, 487, 497, 506, 512, 519, 529, 538, 544, 551, 561, 570, 576, 583, 593, 602, 608, 615, 625, 634, 640, 647, 657, 666, 672, 679, 689, 698, 704, 711, 721, 730, 736, 743, 753, 762, 768, 775, 785, 794, 800, 807, 817, 826, 832, 839, 849, 858, 864, 871, 881, 890, 896, 903, 913, 922, 928, 935, 945, 954, 960, 967, 977, 986, 1024, 1086, 1155, 1223, 1280, 1342, 1411, 1479, 1536, 1598, 1667, 1735, 1792, 1854, 1923, 1991, 2048, 2110, 2179, 2247, 2304, 2366, 2435, 2503, 2560, 2622, 2691, 2759, 2816, 2878, 2947, 3015, 3072, 3134, 3203, 3271, 3328, 3390, 3459, 3527, 3584, 3646, 3715, 3783, 3840, 3902, 3971, 4039, 4096, 4158, 4227, 4295, 4352, 4414, 4483, 4551, 4608, 4670, 4739, 4807, 4864, 4926, 4995, 5063, 5120, 5182, 5251, 5319, 5376, 5438, 5507, 5575, 5632, 5694, 5763, 5831, 5888, 5950, 6019, 6087, 6144, 6206, 6275, 6343, 6400, 6462, 6531, 6599, 6656, 6718, 6787, 6855, 6912, 6974, 7043, 7111, 7168, 7230, 7299, 7367, 7424, 7486, 7555, 7623, 7680, 7742, 7811, 7879, 7936, 7998, 8067, 8135, 8192, 8254, 8323, 8391}) + //foreach (var copyBytes in new[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, 64, 66, 68, 70, 72, 74, 76, 78, 80, 82, 84, 86, 88, 90, 92, 94, 96}) + //foreach (var copyBytes in new[] { 128,256,512,1024,2048,4096,8192,16384,32768,65536}) + //foreach (var copyBytes in new[] { 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16}) + foreach ( + var copyBytes in + new[] + { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, 64, 66, 68, 70, 72, 74, 76, 78, 80, 82, 84, 86, 88, 90, 92, 94, 96, 103, 113, 122, 128, 135, 145, + 154, 160, 167, 177, 186, 192, 199, 209, 218, 224, 231, 241, 250, 256, 263, 273, 282, 288, 295, 305, 314, 320, 327, 337, 346, 352, 359, 369, 378, 384, 391, 401, 410, 416, 423, 433, 442, 448, 455, 465, 474, 480, 487, 497, 506, 512, 519, 529, 538, 544, 551, 561, 570, 576, 583, + 593, 602, 608, 615, 625, 634, 640, 647, 657, 666, 672, 679, 689, 698, 704, 711, 721, 730, 736, 743, 753, 762, 768, 775, 785, 794, 800, 807, 817, 826, 832, 839, 849, 858, 864, 871, 881, 890, 896, 903, 913, 922, 928, 935, 945, 954, 960, 967, 977, 986, 1024, 1086, 1155, 1223, + 1280, 1342, 1411, 1479, 1536, 1598, 1667, 1735, 1792, 1854, 1923, 1991, 2048, 2110, 2179, 2247, 2304, 2366, 2435, 2503, 2560, 2622, 2691, 2759, 2816, 2878, 2947, 3015, 3072, 3134, 3203, 3271, 3328, 3390, 3459, 3527, 3584, 3646, 3715, 3783, 3840, 3902, 3971, 4039, 4096, 4158, + 4227, 4295, 4352, 4414, 4483, 4551, 4608, 4670, 4739, 4807, 4864, 4926, 4995, 5063, 5120, 5182, 5251, 5319, 5376, 5438, 5507, 5575, 5632, 5694, 5763, 5831, 5888, 5950, 6019, 6087, 6144, 6206, 6275, 6343, 6400, 6462, 6531, 6599, 6656, 6718, 6787, 6855, 6912, 6974, 7043, 7111, + 7168, 7230, 7299, 7367, 7424, 7486, 7555, 7623, 7680, 7742, 7811, 7879, 7936, 7998, 8067, 8135, 8192, 8254, 8323, 8391 + }.Where(x=>x>=0 && x<10000) ) { Test(copyBytes); } Console.WriteLine("ready"); - Console.ReadKey(); } private static void InitArray(Action copyAction) @@ -63,19 +73,15 @@ private static void InitArray(Action copyAction) private static void Test(int copyBytes) { const double GB = 1024*1024*1024; - var iterations = (int) _max/(copyBytes == 0 ? 1 : copyBytes); - - var s0 = TestArrayCopy(copyBytes, iterations); - iterations = (int) (iterations*(TestTimeInMs/s0.ElapsedMilliseconds)); - + var iterations = GetMinIterations(copyBytes); var s4 = TestUnsafeBufferMemmoveJamesqo2(copyBytes, iterations); var s1 = TestArrayCopy(copyBytes, iterations); var s2 = TestMsMemmove(copyBytes, iterations); var s3 = TestVectorizedIftree(copyBytes, iterations); double baseline = s1.Elapsed.Ticks; - Console.Write($"{copyBytes}"); - Console.Write($"\t{iterations}"); + Console.Write($"{copyBytes:#######0}"); + Console.Write($"\t{iterations:##########0}"); Console.Write($"\t{(double) s1.Elapsed.Ticks/baseline: 0.00}"); Console.Write($"\t{(double) s2.Elapsed.Ticks/baseline: 0.00}"); Console.Write($"\t{(double) s3.Elapsed.Ticks/baseline: 0.00}"); @@ -84,21 +90,36 @@ private static void Test(int copyBytes) Console.Write($"\t{s2.Elapsed.TotalMilliseconds*1000000/iterations: 0.00}"); Console.Write($"\t{s3.Elapsed.TotalMilliseconds*1000000/iterations: 0.00}"); Console.Write($"\t{s4.Elapsed.TotalMilliseconds*1000000/iterations: 0.00}"); - Console.Write($"\t{(copyBytes * (double)iterations/GB): 0.00} "); - Console.Write($"\t{(copyBytes * (double)iterations /GB)/ (double)(s1.Elapsed.TotalMilliseconds/1000) : 0.00}"); - Console.Write($"\t{(copyBytes * (double)iterations /GB)/ (double)(s2.Elapsed.TotalMilliseconds/1000) : 0.00}"); - Console.Write($"\t{(copyBytes * (double)iterations /GB)/ (double)(s3.Elapsed.TotalMilliseconds/1000) : 0.00}"); - Console.Write($"\t{(copyBytes * (double)iterations /GB)/ (double)(s4.Elapsed.TotalMilliseconds/1000) : 0.00}"); + Console.Write($"\t{copyBytes*(double) iterations/GB: 0.00} "); + Console.Write($"\t{copyBytes*(double) iterations/GB/(s1.Elapsed.TotalMilliseconds/1000): 0.00}"); + Console.Write($"\t{copyBytes*(double) iterations/GB/(s2.Elapsed.TotalMilliseconds/1000): 0.00}"); + Console.Write($"\t{copyBytes*(double) iterations/GB/(s3.Elapsed.TotalMilliseconds/1000): 0.00}"); + Console.Write($"\t{copyBytes*(double) iterations/GB/(s4.Elapsed.TotalMilliseconds/1000): 0.00}"); Console.WriteLine(); } - private static Stopwatch TestVectorizedIftree(int copyBytes, int interations) + private static long GetMinIterations(int copyBytes) + { + var iterations = _max/(copyBytes == 0 ? 1 : copyBytes); + var s0 = new Stopwatch(); + do + { + s0 = TestArrayCopy(copyBytes, iterations); + if (s0.ElapsedMilliseconds <= 100) + iterations *= 10; + } while (s0.ElapsedMilliseconds <= 100); + iterations = (long) (iterations*(TestTimeInMs/s0.ElapsedMilliseconds)); + + return iterations; + } + + private static Stopwatch TestVectorizedIftree(int copyBytes, long interations) { var sw = Stopwatch.StartNew(); var offset = 0; for (long i = 0; i < interations; i++) { - offset += Arraystep==-1? copyBytes:Arraystep; + offset += TestMode == -1 ? copyBytes : TestMode; if (offset + copyBytes >= BufferSize) offset &= 0x3fff; Anderman2.VectorizedCopyAnderman(_src, offset, _dst, offset, copyBytes); } @@ -106,13 +127,13 @@ private static Stopwatch TestVectorizedIftree(int copyBytes, int interations) return sw; } - private static Stopwatch TestArrayCopy(int copyBytes, int interations) + private static Stopwatch TestArrayCopy(int copyBytes, long interations) { var sw = Stopwatch.StartNew(); var offset = 0; for (long i = 0; i < interations; i++) { - offset += Arraystep == -1 ? copyBytes : Arraystep; + offset += TestMode == -1 ? copyBytes : TestMode; if (offset + copyBytes >= BufferSize) offset &= 0x3fff; Array.Copy(_src, offset, _dst, offset, copyBytes); } @@ -120,13 +141,14 @@ private static Stopwatch TestArrayCopy(int copyBytes, int interations) return sw; } - private static Stopwatch TestUnsafeBufferMemmoveJamesqo2(int copyBytes, int interations) + + private static Stopwatch TestUnsafeBufferMemmoveJamesqo2(int copyBytes, long interations) { var sw = Stopwatch.StartNew(); var offset = 0; for (long i = 0; i < interations; i++) { - offset += Arraystep == -1 ? copyBytes : Arraystep; + offset += TestMode == -1 ? copyBytes : TestMode; if (offset + copyBytes >= BufferSize) offset &= 0x3fff; UnsafeBufferMemmoveJamesqo2.Memmove(_src, offset, _dst, offset, copyBytes); } @@ -134,13 +156,13 @@ private static Stopwatch TestUnsafeBufferMemmoveJamesqo2(int copyBytes, int inte return sw; } - private static Stopwatch TestMsMemmove(int copyBytes, int interations) + private static Stopwatch TestMsMemmove(int copyBytes, long interations) { var sw = Stopwatch.StartNew(); var offset = 0; for (long i = 0; i < interations; i++) { - offset += Arraystep == -1 ? copyBytes : Arraystep; + offset += TestMode == -1 ? copyBytes : TestMode; if (offset + copyBytes >= BufferSize) offset &= 0x3fff; MsvcrtMemove.MsvcrtMemmove(_src, offset, _dst, offset, copyBytes); } diff --git a/src/DotNetCross.Memory.Copies.Benchmarks2/project.json b/src/DotNetCross.Memory.Copies.Benchmarks2/project.json index 8e1d30e..342c551 100644 --- a/src/DotNetCross.Memory.Copies.Benchmarks2/project.json +++ b/src/DotNetCross.Memory.Copies.Benchmarks2/project.json @@ -12,6 +12,7 @@ }, "runtime.win7-x64.Microsoft.NETCore.Runtime.CoreCLR": "1.0.4", "System.Runtime.CompilerServices.Unsafe": "4.0.0", + "System.Linq": "4.1.0" }, "runtimes": { "win8-x64": {} From a8e4bdfd9cb552315fbebe398f3017bc0f0a5c2d Mon Sep 17 00:00:00 2001 From: Thom Kiesewetter Date: Fri, 26 Aug 2016 23:43:22 +0200 Subject: [PATCH 5/7] Add cpu count and Google graph and faster test results --- .gitignore | 3 + .../UnsafeAnderman2.cs | 194 +++++------- .../Anderman.cs | 238 -------------- .../Anderman2.cs | 73 ----- .../AndermanMovsb.cs | 121 ++++++++ .../AndermanOptimized.cs | 104 +++++++ ...otNetCross.Memory.Copies.Benchmarks2.xproj | 2 +- .../GoogleChart/C.cs | 8 + .../GoogleChart/Col.cs | 9 + .../GoogleChart/GoogleChart.cs | 8 + .../GoogleChart/Row.cs | 7 + .../Program.cs | 252 ++++++++------- .../Rdtsc.cs | 292 ++++++++++++++++++ .../StopwatchTests.cs | 112 +++++++ .../Tests.cs | 233 ++++++++++++++ .../UnsafeBufferMemmoveJamesqo2.cs | 40 +-- .../chart.html | 52 ++++ .../msvcrtMemove.cs | 5 +- .../project.json | 7 +- src/mov_repsb/AndermanMovsb.cs | 101 ++++++ src/mov_repsb/Program.cs | 14 + src/mov_repsb/Properties/AssemblyInfo.cs | 19 ++ src/mov_repsb/mov_repsb.xproj | 21 ++ src/mov_repsb/project.json | 21 ++ 24 files changed, 1352 insertions(+), 584 deletions(-) delete mode 100644 src/DotNetCross.Memory.Copies.Benchmarks2/Anderman.cs delete mode 100644 src/DotNetCross.Memory.Copies.Benchmarks2/Anderman2.cs create mode 100644 src/DotNetCross.Memory.Copies.Benchmarks2/AndermanMovsb.cs create mode 100644 src/DotNetCross.Memory.Copies.Benchmarks2/AndermanOptimized.cs create mode 100644 src/DotNetCross.Memory.Copies.Benchmarks2/GoogleChart/C.cs create mode 100644 src/DotNetCross.Memory.Copies.Benchmarks2/GoogleChart/Col.cs create mode 100644 src/DotNetCross.Memory.Copies.Benchmarks2/GoogleChart/GoogleChart.cs create mode 100644 src/DotNetCross.Memory.Copies.Benchmarks2/GoogleChart/Row.cs create mode 100644 src/DotNetCross.Memory.Copies.Benchmarks2/Rdtsc.cs create mode 100644 src/DotNetCross.Memory.Copies.Benchmarks2/StopwatchTests.cs create mode 100644 src/DotNetCross.Memory.Copies.Benchmarks2/Tests.cs create mode 100644 src/DotNetCross.Memory.Copies.Benchmarks2/chart.html create mode 100644 src/mov_repsb/AndermanMovsb.cs create mode 100644 src/mov_repsb/Program.cs create mode 100644 src/mov_repsb/Properties/AssemblyInfo.cs create mode 100644 src/mov_repsb/mov_repsb.xproj create mode 100644 src/mov_repsb/project.json diff --git a/.gitignore b/.gitignore index 94420dc..a83e2a5 100644 --- a/.gitignore +++ b/.gitignore @@ -234,3 +234,6 @@ _Pvt_Extensions # FAKE - F# Make .fake/ +/src/DotNetCross.Memory.Copies.Benchmarks2/chart.json +/I5-4590.xlsx +/I7-3610QM.xlsx diff --git a/src/DotNetCross.Memory.Copies.Benchmarks/UnsafeAnderman2.cs b/src/DotNetCross.Memory.Copies.Benchmarks/UnsafeAnderman2.cs index 29cbc55..9a18b82 100644 --- a/src/DotNetCross.Memory.Copies.Benchmarks/UnsafeAnderman2.cs +++ b/src/DotNetCross.Memory.Copies.Benchmarks/UnsafeAnderman2.cs @@ -29,134 +29,92 @@ public static class UnsafeAnderman2 /// Code must be optimized, in release mode and .IsHardwareAccelerated must be true for the /// performance benefits. /// - public static unsafe void VectorizedCopy2(byte[] src, int srcOffset, byte[] dst, int dstOffset, int count) { - if (count > 512 + 64) - { - // TEST: Disable Array-Copy fall back - // In-built copy faster for large arrays (vs repeated bounds checks on Vector.ctor?) - //Array.Copy(src, srcOffset, dst, dstOffset, count); - //return; - } + const int alignment = 0x10; + const int mask = alignment - 1; + + if (count == 0) return; if (src == null || dst == null) throw new ArgumentNullException(nameof(src)); if (srcOffset + count > src.Length) throw new ArgumentException(nameof(src)); - if (dstOffset + count > dst.Length) throw new ArgumentException(nameof(dst)); if (count < 0 || srcOffset < 0 || dstOffset < 0) throw new ArgumentOutOfRangeException(nameof(count)); + if (dstOffset + count > dst.Length) throw new ArgumentException(nameof(dst)); + fixed (byte* pSrcOrigin = &src[srcOffset]) + fixed (byte* pDstOrigin = &dst[dstOffset]) { var pSrc = pSrcOrigin; - fixed (byte* pDstOrigin = &dst[dstOffset]) + var pDst = pDstOrigin; + if (count < 16) { - var pDst = pDstOrigin; + if (count < 8) { - if (count >= Vector.Count) + if (count < 4) { - while (count > Vector.Count) + if (count < 2) + { + *(pDst + 0) = *(pSrc + 0); + } + else { - Unsafe.Write(pDst, Unsafe.Read>(pSrc)); - count -= Vector.Count; - pSrc += Vector.Count; - pDst += Vector.Count; + *(short*) pDst = *(short*) pSrc; + *(short*) (pDst + (count & 0xf) - 2) = *(short*) (pSrc + (count & 0xf) - 2); } - pDst += count - Vector.Count; - pSrc += count - Vector.Count; - Unsafe.Write(pDst, Unsafe.Read>(pSrc)); - return; } - - switch (count) + else { - case 15: - { - *(long*)pDst = *(long*)pSrc; - *(long*)(pDst + 7) = *(long*)(pSrc + 7); - return; - } - case 14: - { - *(long*)pDst = *(long*)pSrc; - *(long*)(pDst + 6) = *(long*)(pSrc + 6); - return; - } - case 13: - { - *(long*)pDst = *(long*)pSrc; - *(long*)(pDst + 5) = *(long*)(pSrc + 5); - return; - } - case 12: - { - *(long*)pDst = *(long*)pSrc; - *(int*)(pDst + 8) = *(int*)(pSrc + 8); - return; - } - case 11: - { - *(long*)pDst = *(long*)pSrc; - *(int*)(pDst + 7) = *(int*)(pSrc + 7); - return; - } - case 10: - { - *(long*)pDst = *(long*)pSrc; - *(short*)(pDst + 8) = *(short*)(pSrc + 8); - return; - } - case 9: - { - *(long*)pDst = *(long*)pSrc; - *(pDst + 8) = *(pSrc + 8); - return; - } - case 8: - { - *(long*)pDst = *(long*)pSrc; - return; - } - case 7: - { - *(int*)pDst = *(int*)pSrc; - *(int*)(pDst + 3) = *(int*)(pSrc + 3); - return; - } - case 6: - { - *(int*)pDst = *(int*)pSrc; - *(short*)(pDst + 4) = *(short*)(pSrc + 4); - return; - } - case 5: - { - *(int*)pDst = *(int*)pSrc; - *(pDst + 4) = *(pSrc + 4); - return; - } - case 4: - { - *(int*)pDst = *(int*)pSrc; - return; - } - case 3: - { - *(short*)pDst = *(short*)pSrc; - *(pDst + 2) = *(pSrc + 2); - return; - } - case 2: - { - *(short*)pDst = *(short*)pSrc; - return; - } - case 1: - { - *pDst = *pSrc; - return; - } - case 0: - return; + *(int*) pDst = *(int*) pSrc; + *(int*) (pDst + (count & 0xf) - 4) = *(int*) (pSrc + (count & 0xf) - 4); } } + else + { + *(long*) pDst = *(long*) pSrc; + *(long*) (pDst + (count & 0xf) - 8) = *(long*) (pSrc + (count & 0xf) - 8); + } + return; + } + + pSrc -= (long) pDst; + Unsafe.Write(pDst, Unsafe.Read>(pSrc + (long) pDst)); + + var offset = (int) ((ulong) pDst & mask); + count += offset - alignment; + pDst += alignment - offset; + + while (count >= 4*Vector.Count) + { + var x1 = Unsafe.Read>(pSrc + (long) pDst); + var x2 = Unsafe.Read>(pSrc + (long) pDst + Vector.Count); + count -= 4*Vector.Count; + Unsafe.Write(pDst, x1); + Unsafe.Write(pDst + Vector.Count, x2); + pDst += 4*Vector.Count; + x1 = Unsafe.Read>(pSrc + (long) pDst - 2*Vector.Count); + x2 = Unsafe.Read>(pSrc + (long) pDst - Vector.Count); + Unsafe.Write(pDst - 2*Vector.Count, x1); + Unsafe.Write(pDst - Vector.Count, x2); + } + while (count >= 2*Vector.Count) + { + var x1 = Unsafe.Read>(pSrc + (long) pDst); + var x2 = Unsafe.Read>(pSrc + (long) pDst + Vector.Count); + count -= 2*Vector.Count; + Unsafe.Write(pDst, x1); + Unsafe.Write(pDst + Vector.Count, x2); + pDst += 2*Vector.Count; + } + while (count >= 1*Vector.Count) + { + var x1 = Unsafe.Read>(pSrc + (long) pDst); + count -= Vector.Count; + Unsafe.Write(pDst, x1); + pDst += Vector.Count; + } + if (count > 0) + { + pDst += count - Vector.Count; + Unsafe.Write(pDst, Unsafe.Read>(pSrc + (long) pDst)); } } } @@ -261,14 +219,14 @@ public static unsafe void UnsafeVectorizedCopy2(byte* pDst, byte* pSrc, int coun return; } case 1: - { - *pDst = *pSrc; - return; - } + { + *pDst = *pSrc; + return; + } case 0: - { - return; - } + { + return; + } } } } diff --git a/src/DotNetCross.Memory.Copies.Benchmarks2/Anderman.cs b/src/DotNetCross.Memory.Copies.Benchmarks2/Anderman.cs deleted file mode 100644 index fd7f0a3..0000000 --- a/src/DotNetCross.Memory.Copies.Benchmarks2/Anderman.cs +++ /dev/null @@ -1,238 +0,0 @@ -using System; -using System.Numerics; -using System.Runtime.CompilerServices; - -namespace DotNetCross.Memory.Copies.Benchmarks -{ - // https://github.com/dotnet/coreclr/issues/2430#issuecomment-166566393 - public static class UnsafeAnderman - { - /// - /// Copies a specified number of bytes from a source array starting at a particular - /// offset to a destination array starting at a particular offset, not safe for overlapping data. - /// - /// The source buffer - /// The zero-based byte offset into src - /// The destination buffer - /// The zero-based byte offset into dst - /// The number of bytes to copy - /// or is null - /// , , or is less than 0 - /// - /// The number of bytes in src is less - /// than srcOffset plus count.-or- The number of bytes in dst is less than dstOffset - /// plus count. - /// - /// - /// Code must be optimized, in release mode and .IsHardwareAccelerated must be true for the performance benefits. - /// - public static unsafe void VectorizedCopy(byte[] src, int srcOffset, byte[] dst, int dstOffset, int count) - { - if (count > 512 + 64) - { - // TEST: Disable Array-Copy fall back - // In-built copy faster for large arrays (vs repeated bounds checks on Vector.ctor?) - //Array.Copy(src, srcOffset, dst, dstOffset, count); - //return; - } - var orgCount = count; - - if (src == null || dst == null) throw new ArgumentNullException(nameof(src)); - if (count < 0 || srcOffset < 0 || dstOffset < 0) throw new ArgumentOutOfRangeException(nameof(count)); - if (srcOffset + count > src.Length) throw new ArgumentException(nameof(src)); - if (dstOffset + count > dst.Length) throw new ArgumentException(nameof(dst)); - - fixed (byte* srcOrigin = src) - fixed (byte* dstOrigin = dst) - { - var pSrc = srcOrigin + srcOffset; - var pDst = dstOrigin + dstOffset; - while (count >= Vector.Count) - { - Unsafe.Write(pDst, Unsafe.Read>(pSrc)); - count -= Vector.Count; - pSrc += Vector.Count; - pDst += Vector.Count; - } - if (orgCount > Vector.Count) - { - // Is this right? What about offset? - //new Vector(src, orgCount - Vector.Count).CopyTo(dst, orgCount - Vector.Count); - var offset = orgCount - Vector.Count; - Unsafe.Write(dstOrigin + offset, Unsafe.Read>(srcOrigin + offset)); - return; - } - switch (count) - { - case 1: - pDst[0] = pSrc[0]; - return; - - case 2: - *((short*)pDst) = *((short*)pSrc); - return; - - case 3: - *((short*)pDst) = *((short*)pSrc); - pDst[2] = pSrc[2]; - return; - - case 4: - *((int*)pDst) = *((int*)pSrc); - return; - - case 5: - *((int*)pDst) = *((int*)pSrc); - pDst[4] = pSrc[4]; - return; - - case 6: - *((int*)pDst) = *((int*)pSrc); - *((short*)(pDst + 4)) = *((short*)(pSrc + 4)); - return; - - case 7: - *((int*)pDst) = *((int*)pSrc); - *((short*)(pDst + 4)) = *((short*)(pSrc + 4)); - pDst[6] = pSrc[6]; - return; - - case 8: - *((long*)pDst) = *((long*)pSrc); - return; - - case 9: - *((long*)pDst) = *((long*)pSrc); - pDst[8] = pSrc[8]; - return; - - case 10: - *((long*)pDst) = *((long*)pSrc); - *((short*)(pDst + 8)) = *((short*)(pSrc + 8)); - return; - - case 11: - *((long*)pDst) = *((long*)pSrc); - *((short*)(pDst + 8)) = *((short*)(pSrc + 8)); - pDst[10] = pSrc[10]; - return; - - case 12: - *((long*)pDst) = *((long*)pSrc); - *((int*)(pDst + 8)) = *((int*)(pSrc + 8)); - return; - - case 13: - *((long*)pDst) = *((long*)pSrc); - *((int*)(pDst + 8)) = *((int*)(pSrc + 8)); - pDst[12] = pSrc[12]; - return; - - case 14: - *((long*)pDst) = *((long*)pSrc); - *((int*)(pDst + 8)) = *((int*)(pSrc + 8)); - *((short*)(pDst + 12)) = *((short*)(pSrc + 12)); - return; - - case 15: - *((long*)pDst) = *((long*)pSrc); - *((int*)(pDst + 8)) = *((int*)(pSrc + 8)); - *((short*)(pDst + 12)) = *((short*)(pSrc + 12)); - pDst[14] = pSrc[14]; - return; - - } - } - } - public static unsafe void UnsafeVectorizedCopy2(byte* pDst, byte* pSrc, int count) - { - while (count >= Vector.Count) - { - Unsafe.Write(pDst, Unsafe.Read>(pSrc)); - count -= Vector.Count; - pSrc += Vector.Count; - pDst += Vector.Count; - } - switch (count) - { - case 1: - pDst[0] = pSrc[0]; - return; - - case 2: - *((short*)pDst) = *((short*)pSrc); - return; - - case 3: - *((short*)pDst) = *((short*)pSrc); - pDst[2] = pSrc[2]; - return; - - case 4: - *((int*)pDst) = *((int*)pSrc); - return; - - case 5: - *((int*)pDst) = *((int*)pSrc); - pDst[4] = pSrc[4]; - return; - - case 6: - *((int*)pDst) = *((int*)pSrc); - *((short*)(pDst + 4)) = *((short*)(pSrc + 4)); - return; - - case 7: - *((int*)pDst) = *((int*)pSrc); - *((short*)(pDst + 4)) = *((short*)(pSrc + 4)); - pDst[6] = pSrc[6]; - return; - - case 8: - *((long*)pDst) = *((long*)pSrc); - return; - - case 9: - *((long*)pDst) = *((long*)pSrc); - pDst[8] = pSrc[8]; - return; - - case 10: - *((long*)pDst) = *((long*)pSrc); - *((short*)(pDst + 8)) = *((short*)(pSrc + 8)); - return; - - case 11: - *((long*)pDst) = *((long*)pSrc); - *((short*)(pDst + 8)) = *((short*)(pSrc + 8)); - pDst[10] = pSrc[10]; - return; - - case 12: - *((long*)pDst) = *((long*)pSrc); - *((int*)(pDst + 8)) = *((int*)(pSrc + 8)); - return; - - case 13: - *((long*)pDst) = *((long*)pSrc); - *((int*)(pDst + 8)) = *((int*)(pSrc + 8)); - pDst[12] = pSrc[12]; - return; - - case 14: - *((long*)pDst) = *((long*)pSrc); - *((int*)(pDst + 8)) = *((int*)(pSrc + 8)); - *((short*)(pDst + 12)) = *((short*)(pSrc + 12)); - return; - - case 15: - *((long*)pDst) = *((long*)pSrc); - *((int*)(pDst + 8)) = *((int*)(pSrc + 8)); - *((short*)(pDst + 12)) = *((short*)(pSrc + 12)); - pDst[14] = pSrc[14]; - return; - - } - } - } -} diff --git a/src/DotNetCross.Memory.Copies.Benchmarks2/Anderman2.cs b/src/DotNetCross.Memory.Copies.Benchmarks2/Anderman2.cs deleted file mode 100644 index ecef800..0000000 --- a/src/DotNetCross.Memory.Copies.Benchmarks2/Anderman2.cs +++ /dev/null @@ -1,73 +0,0 @@ -using System; -using System.Numerics; -using System.Runtime.CompilerServices; - -namespace test -{ - public static class Anderman2 { - public static unsafe void VectorizedCopyAnderman(byte[] src, int srcOffset, byte[] dst, int dstOffset, int count) - { - const int alignment = 0x10; - const int mask = alignment - 1; - if (src == null || dst == null) throw new ArgumentNullException(nameof(src)); - if (srcOffset + count > src.Length) throw new ArgumentException(nameof(src)); - if (count < 0 || srcOffset < 0 || dstOffset < 0) throw new ArgumentOutOfRangeException(nameof(count)); - if (dstOffset + count > dst.Length) throw new ArgumentException(nameof(dst)); - if (count == 0) return; - fixed (byte* pSrcOrigin = &src[srcOffset]) - fixed (byte* pDstOrigin = &dst[dstOffset]) - { - var pSrc = pSrcOrigin; - var pDst = pDstOrigin; - if (count >= 8) - { - *(long*) pDst = *(long*) pSrc; - *(long*) (pDst + (count & 0xf) - 8) = *(long*) (pSrc + (count & 0xf) - 8); - } - else if (count >= 4) - { - *(int*) pDst = *(int*) pSrc; - *(int*) (pDst + (count & 0xf) - 4) = *(int*) (pSrc + (count & 0xf) - 4); - } - else if (count >= 2) - { - *(short*) pDst = *(short*) pSrc; - *(short*) (pDst + (count & 0xf) - 2) = *(short*) (pSrc + (count & 0xf) - 2); - } - else - { - *(pDst + 0) = *(pSrc + 0); - } - if (count >= Vector.Count) - { - pSrc -= (long) pDst; - count += -alignment + (int) ((ulong) pDst & mask); - pDst += alignment - ((ulong) pDst & mask); - - while (count >= 4*Vector.Count) - { - var x1 = Unsafe.Read>(pSrc + (long) pDst); - var x2 = Unsafe.Read>(pSrc + (long) pDst + Vector.Count); - count -= 4*Vector.Count; - Unsafe.Write(pDst, x1); - Unsafe.Write(pDst + Vector.Count, x2); - pDst += 4*Vector.Count; - x1 = Unsafe.Read>(pSrc + (long) pDst - 2*Vector.Count); - x2 = Unsafe.Read>(pSrc + (long) pDst - Vector.Count); - Unsafe.Write(pDst - 2*Vector.Count, x1); - Unsafe.Write(pDst - Vector.Count, x2); - } - while (count >= 1*Vector.Count) - { - var x1 = Unsafe.Read>(pSrc + (long) pDst); - count -= Vector.Count; - Unsafe.Write(pDst, x1); - pDst += Vector.Count; - } - pDst += count - Vector.Count; - Unsafe.Write(pDst, Unsafe.Read>(pSrc + (long) pDst)); - } - } - } - } -} \ No newline at end of file diff --git a/src/DotNetCross.Memory.Copies.Benchmarks2/AndermanMovsb.cs b/src/DotNetCross.Memory.Copies.Benchmarks2/AndermanMovsb.cs new file mode 100644 index 0000000..7cf690c --- /dev/null +++ b/src/DotNetCross.Memory.Copies.Benchmarks2/AndermanMovsb.cs @@ -0,0 +1,121 @@ +using System; +using System.ComponentModel; +using System.Runtime.InteropServices; + +namespace DotNetCross.Memory.Copies.Benchmarks2 +{ + //Used this code only for testing. This code works only on 64bit + public static class AndermanMovsb + { + private const uint PAGE_READWRITE = 0x04; + private const uint PAGE_EXECUTE = 0x10; + private const uint PAGE_EXECUTE_READWRITE = 0x40; + private const uint MEM_COMMIT = 0x1000; + private const uint MEM_RELEASE = 0x8000; + + [DllImport("kernel32.dll", ExactSpelling = true, SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + private static extern bool VirtualProtect(IntPtr lpAddress, IntPtr dwSize, uint flAllocationType, out uint lpflOldProtect); + + [DllImport("kernel32.dll", ExactSpelling = true, SetLastError = true)] + private static extern IntPtr VirtualAlloc(IntPtr lpAddress, IntPtr dwSize, uint flAllocationType, uint flProtect); + + [DllImport("kernel32.dll", ExactSpelling = true, SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + private static extern bool VirtualFree(IntPtr lpAddress, IntPtr dwSize, uint dwFreeType); + + public delegate int MyMoveSb(ulong RCX, ulong RDX, ulong R8); + + public static MyMoveSb movsb; + public static MyMoveSb pinvoke; + + static AndermanMovsb() + { + //RAX�=�0000008377040000 RBX�=�0000000000000005 RCX�=�0000000000000004 RDX�=�0000000000000005 RSI�=�0000008300018350 RDI�=�0000000000000004 R8 �=�0000000000000006 R9 �=�000007F9C2460F84 R10�=�0000000000000000 R11�=�0000000000000000 R12�=�00000083754BDF70 R13�=�0000000000000004 R14�=�0000000000000006 R15�=�000000837555A4D0 RIP�=�0000008377040000 RSP�=�00000083754BDDF8 RBP�=�00000083754BDEA0 EFL�=�00000246 + /* + 0: 49 89 fb mov r11,rdi + 3: 49 89 f2 mov r10,rsi + 6: 48 89 ce mov rsi,rcx + 9: 48 89 d7 mov rdi,rdx + c: 4c 89 c1 mov rcx,r8 + f: f3 a4 rep movs BYTE PTR es:[rdi],BYTE PTR ds:[rsi] + 11: 4c 89 d6 mov rsi,r10 + 14: 4c 89 df mov rdi,r11 + 17: c3 ret + */ + byte[] assemblyCode = { 0x49, 0x89, 0xFB, 0x49, 0x89, 0xF2, 0x48, 0x89, 0xCE, 0x48, 0x89, 0xD7, 0x4C, 0x89, 0xC1, 0xF3, 0xA4, 0x4C, 0x89, 0xD6, 0x4C, 0x89, 0xDF, 0xC3 }; + byte[] assemblyCode2 = { 0xC3 }; + + + // We need to push the code bytes into a native buffer + + var bufPtr = IntPtr.Zero; + + try + { + // Put the sourcecode in a native buffer + bufPtr = VirtualAlloc(IntPtr.Zero, (IntPtr)4096, MEM_COMMIT, PAGE_EXECUTE_READWRITE); + + Marshal.Copy(assemblyCode, 0, bufPtr, assemblyCode.Length); + Marshal.Copy(assemblyCode2, 0, bufPtr+64, assemblyCode2.Length); + + uint oldProtection; + var result = VirtualProtect(bufPtr, (IntPtr)assemblyCode.Length, PAGE_EXECUTE, out oldProtection); + + if (!result) + { + throw new Win32Exception(); + } + movsb = Marshal.GetDelegateForFunctionPointer(bufPtr); + pinvoke = Marshal.GetDelegateForFunctionPointer(bufPtr+64); + bufPtr = IntPtr.Zero; + } + finally + { + // Free the native buffer + if (bufPtr != IntPtr.Zero) + { + var result = VirtualFree(bufPtr, IntPtr.Zero, MEM_RELEASE); + if (!result) + { + throw new Win32Exception(); + } + } + } + } + + + public static unsafe void Memmove(byte[] src, int srcOffset, byte[] dst, int dstOffset, int count) + { + if (count == 0) return; + if (src == null || dst == null) throw new ArgumentNullException(nameof(src)); + if (srcOffset + count > src.Length) throw new ArgumentException(nameof(src)); + if (count < 0 || srcOffset < 0 || dstOffset < 0) throw new ArgumentOutOfRangeException(nameof(count)); + if (dstOffset + count > dst.Length) throw new ArgumentException(nameof(dst)); + + fixed (byte* pSrcOrigin = &src[srcOffset]) + fixed (byte* pDstOrigin = &dst[dstOffset]) + { + var pSrc = pSrcOrigin; + var pDst = pDstOrigin; + var result1 = movsb((ulong)pSrc, (ulong)pDst,(ulong)count); + } + } + public static unsafe void Pinvoke(byte[] src, int srcOffset, byte[] dst, int dstOffset, int count) + { + if (count == 0) return; + if (src == null || dst == null) throw new ArgumentNullException(nameof(src)); + if (srcOffset + count > src.Length) throw new ArgumentException(nameof(src)); + if (count < 0 || srcOffset < 0 || dstOffset < 0) throw new ArgumentOutOfRangeException(nameof(count)); + if (dstOffset + count > dst.Length) throw new ArgumentException(nameof(dst)); + + fixed (byte* pSrcOrigin = &src[srcOffset]) + fixed (byte* pDstOrigin = &dst[dstOffset]) + { + var pSrc = pSrcOrigin; + var pDst = pDstOrigin; + var result1 = pinvoke((ulong)pSrc, (ulong)pDst, (ulong)count); + } + } + } +} \ No newline at end of file diff --git a/src/DotNetCross.Memory.Copies.Benchmarks2/AndermanOptimized.cs b/src/DotNetCross.Memory.Copies.Benchmarks2/AndermanOptimized.cs new file mode 100644 index 0000000..2666b07 --- /dev/null +++ b/src/DotNetCross.Memory.Copies.Benchmarks2/AndermanOptimized.cs @@ -0,0 +1,104 @@ +using System; +using System.Diagnostics; +using System.Numerics; +using System.Runtime.CompilerServices; + +namespace DotNetCross.Memory.Copies.Benchmarks2 +{ + public static class AndermanOptimized + { + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void Memmove(byte[] src, int srcOffset, byte[] dst, int dstOffset, int count) + { + const int alignment = 0x10; + const int mask = alignment - 1; + + if (count == 0) return; + if (src == null || dst == null) throw new ArgumentNullException(nameof(src)); + if (srcOffset + count > src.Length) throw new ArgumentException(nameof(src)); + if (count < 0 || srcOffset < 0 || dstOffset < 0) throw new ArgumentOutOfRangeException(nameof(count)); + if (dstOffset + count > dst.Length) throw new ArgumentException(nameof(dst)); + + fixed (byte* pSrcOrigin = &src[srcOffset]) + fixed (byte* pDstOrigin = &dst[dstOffset]) + { + var pSrc = pSrcOrigin; + var pDst = pDstOrigin; + if (count < 16) + { + if (count < 8) + { + if (count < 4) + { + if (count < 2) + { + *(pDst + 0) = *(pSrc + 0); + } + else + { + *(short*)pDst = *(short*)pSrc; + *(short*)(pDst + (count & 0xf) - 2) = *(short*)(pSrc + (count & 0xf) - 2); + } + } + else + { + *(int*)pDst = *(int*)pSrc; + *(int*)(pDst + (count & 0xf) - 4) = *(int*)(pSrc + (count & 0xf) - 4); + } + + } + else + { + *(long*)pDst = *(long*)pSrc; + *(long*)(pDst + (count & 0xf) - 8) = *(long*)(pSrc + (count & 0xf) - 8); + } + return; + } + + pSrc -= (long)pDst; + Unsafe.Write(pDst, Unsafe.Read>(pSrc + (long)pDst)); + + var offset = (int)((ulong)pDst & mask); + count += offset - alignment; + pDst += alignment - offset; + + while (count >= 4 * Vector.Count) + { + var x1 = Unsafe.Read>(pSrc + (long)pDst); + var x2 = Unsafe.Read>(pSrc + (long)pDst + Vector.Count); + count -= 4 * Vector.Count; + Unsafe.Write(pDst, x1); + Unsafe.Write(pDst + Vector.Count, x2); + pDst += 4 * Vector.Count; + x1 = Unsafe.Read>(pSrc + (long)pDst - 2 * Vector.Count); + x2 = Unsafe.Read>(pSrc + (long)pDst - Vector.Count); + Unsafe.Write(pDst - 2 * Vector.Count, x1); + Unsafe.Write(pDst - Vector.Count, x2); + } + while (count >= 2 * Vector.Count) + { + var x1 = Unsafe.Read>(pSrc + (long)pDst); + var x2 = Unsafe.Read>(pSrc + (long)pDst + Vector.Count); + count -= 2 * Vector.Count; + Unsafe.Write(pDst, x1); + Unsafe.Write(pDst + Vector.Count, x2); + pDst += 2 * Vector.Count; + } + while (count >= 1 * Vector.Count) + { + var x1 = Unsafe.Read>(pSrc + (long)pDst); + count -= Vector.Count; + Unsafe.Write(pDst, x1); + pDst += Vector.Count; + } + if (count > 0) + { + pDst += count - Vector.Count; + Unsafe.Write(pDst, Unsafe.Read>(pSrc + (long) pDst)); + } + } + } + } + +} \ No newline at end of file diff --git a/src/DotNetCross.Memory.Copies.Benchmarks2/DotNetCross.Memory.Copies.Benchmarks2.xproj b/src/DotNetCross.Memory.Copies.Benchmarks2/DotNetCross.Memory.Copies.Benchmarks2.xproj index 84fc08c..93c0858 100644 --- a/src/DotNetCross.Memory.Copies.Benchmarks2/DotNetCross.Memory.Copies.Benchmarks2.xproj +++ b/src/DotNetCross.Memory.Copies.Benchmarks2/DotNetCross.Memory.Copies.Benchmarks2.xproj @@ -7,7 +7,7 @@ 9396cfef-f925-4202-98e7-1f0759df0c5b - test + DotNetCross.Memory.Copies.Benchmarks2 .\obj .\bin\ v4.6.1 diff --git a/src/DotNetCross.Memory.Copies.Benchmarks2/GoogleChart/C.cs b/src/DotNetCross.Memory.Copies.Benchmarks2/GoogleChart/C.cs new file mode 100644 index 0000000..c43b042 --- /dev/null +++ b/src/DotNetCross.Memory.Copies.Benchmarks2/GoogleChart/C.cs @@ -0,0 +1,8 @@ +namespace DotNetCross.Memory.Copies.Benchmarks2 +{ + public class C + { + public object v { get; set; } + public string f { get; set; } + } +} \ No newline at end of file diff --git a/src/DotNetCross.Memory.Copies.Benchmarks2/GoogleChart/Col.cs b/src/DotNetCross.Memory.Copies.Benchmarks2/GoogleChart/Col.cs new file mode 100644 index 0000000..bb813aa --- /dev/null +++ b/src/DotNetCross.Memory.Copies.Benchmarks2/GoogleChart/Col.cs @@ -0,0 +1,9 @@ +namespace DotNetCross.Memory.Copies.Benchmarks2 +{ + public class Col + { + public string id { get; set; } + public string label { get; set; } + public string type { get; set; } + } +} \ No newline at end of file diff --git a/src/DotNetCross.Memory.Copies.Benchmarks2/GoogleChart/GoogleChart.cs b/src/DotNetCross.Memory.Copies.Benchmarks2/GoogleChart/GoogleChart.cs new file mode 100644 index 0000000..707e39c --- /dev/null +++ b/src/DotNetCross.Memory.Copies.Benchmarks2/GoogleChart/GoogleChart.cs @@ -0,0 +1,8 @@ +namespace DotNetCross.Memory.Copies.Benchmarks2 +{ + public class GoogleChart + { + public Col[] cols { get; set; } + public Row[] rows { get; set; } + } +} \ No newline at end of file diff --git a/src/DotNetCross.Memory.Copies.Benchmarks2/GoogleChart/Row.cs b/src/DotNetCross.Memory.Copies.Benchmarks2/GoogleChart/Row.cs new file mode 100644 index 0000000..3c431e8 --- /dev/null +++ b/src/DotNetCross.Memory.Copies.Benchmarks2/GoogleChart/Row.cs @@ -0,0 +1,7 @@ +namespace DotNetCross.Memory.Copies.Benchmarks2 +{ + public class Row + { + public C[] c { get; set; } + } +} \ No newline at end of file diff --git a/src/DotNetCross.Memory.Copies.Benchmarks2/Program.cs b/src/DotNetCross.Memory.Copies.Benchmarks2/Program.cs index eb7dab4..38d617e 100644 --- a/src/DotNetCross.Memory.Copies.Benchmarks2/Program.cs +++ b/src/DotNetCross.Memory.Copies.Benchmarks2/Program.cs @@ -1,33 +1,105 @@ using System; +using System.ComponentModel; using System.Diagnostics; using System.Linq; -namespace test +using System.Reflection; +using System.Runtime.InteropServices; +using System.Threading; +using Newtonsoft.Json; + +namespace DotNetCross.Memory.Copies.Benchmarks2 { public class Program { - private const long _max = 30000000; - private const double TestTimeInMs = 1000.0; - private const int BufferSize = 1024*1024*1024; - - //Use a contant to change the test. Vars has huge impact on the performance - private const int Cached = 0; //Pseudo Random Test - private const int Randomizor = 0x13751; //Pseudo Random Test - private const int Alignment = 1; //Alignment test - private const int Sequence = -1; //read seq through array - private const int TestMode = Alignment; //Alignment test - - private static readonly byte[] _src = new byte[BufferSize]; - private static readonly byte[] _dst = new byte[BufferSize]; + //private static extern bool QueryThreadCycleTime(IntPtr hThread, out ulong cycles); + //private static readonly IntPtr PseudoHandle = (IntPtr)(-2); + public static double loopOverhead = 0; + public static ulong CyclesPerSecond = 0; + public static double nsPerCycle = 0; + private const ulong TestDuration = 100; public static void Main(string[] args) { - Console.WriteLine("Initalizing "); + Console.WriteLine($"Warmup..."); + //Tests.Warmup(); + CyclesPerSecond = GetCyclesPerSeond(); + nsPerCycle = (1000 * 1000 * 1000.0) / CyclesPerSecond; + Tests.TestDuration = TestDuration * CyclesPerSecond / 1000; + loopOverhead = Tests.TestOverhead(); + + Console.WriteLine($"offset src: {Tests.GetOffsetSrc():X04}"); + Console.WriteLine($"offset dst: {Tests.GetOffsetDst():X04} "); + Console.WriteLine($"CyclesPerSecond: {CyclesPerSecond,5:0} "); + Console.WriteLine($"nsPerCycle: {nsPerCycle,5:0.0000} "); + Console.WriteLine($"loopOverhead: {loopOverhead,5:0.0000} Cycles"); + Console.WriteLine($" {loopOverhead * nsPerCycle,5:0.0000} ns "); + Console.WriteLine($"Starting..."); + + + + + do + { + var GoogleChart = new GoogleChart + { + cols = new[] + { + new Col() { label = "X" ,type="number"}, + new Col() { label = "ArrayCopy" ,type="number"}, + new Col() { label = "MsvcrtMemmove" ,type="number"}, + new Col() { label = "Anderman",type="number" }, + new Col() { label = "AndermanMovsb" ,type="number"}, + } + }; + var sizes = new[] + { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, 64, 66, 68, 70, 72, 74, 76, 78, 80, 82, 84, 86, 88, 90, 92, 94, 96, 103, 113, 122, 128, 135, + 145, + 154, 160, 167, 177, 186, 192, 199, 209, 218, 224, 231, 241, 250, 256, 263, 273, 282, 288, 295, 305, 314, 320, 327, 337, 346, 352, 359, 369, 378, 384, 391, 401, 410, 416, 423, 433, 442, 448, 455, 465, 474, 480, 487, 497, 506, 512, 519, 529, 538, 544, 551, 561, 570, 576, 583, + 593, 602, 608, 615, 625, 634, 640, 647, 657, 666, 672, 679, 689, 698, 704, 711, 721, 730, 736, 743, 753, 762, 768, 775, 785, 794, 800, 807, 817, 826, 832, 839, 849, 858, 864, 871, 881, 890, 896, 903, 913, 922, 928, 935, 945, 954, 960, 967, 977, 986, 1024, 1086, 1155, 1223, + 1280, 1342, 1411, 1479, 1536, 1598, 1667, 1735, 1792, 1854, 1923, 1991, 2048, 2110, 2179, 2247, 2304, 2366, 2435, 2503, 2560, 2622, 2691, 2759, 2816, 2878, 2947, 3015, 3072, 3134, 3203, 3271, 3328, 3390, 3459, 3527, 3584, 3646, 3715, 3783, 3840, 3902, 3971, 4039, 4096, 4158, + 4227, 4295, 4352, 4414, 4483, 4551, 4608, 4670, 4739, 4807, 4864, 4926, 4995, 5063, 5120, 5182, 5251, 5319, 5376, 5438, 5507, 5575, 5632, 5694, 5763, 5831, 5888, 5950, 6019, 6087, 6144, 6206, 6275, 6343, 6400, 6462, 6531, 6599, 6656, 6718, 6787, 6855, 6912, 6974, 7043, 7111, + 7168, 7230, 7299, 7367, 7424, 7486, 7555, 7623, 7680, 7742, 7811, 7879, 7936, 7998, 8067, 8135, 8192, 8254, 8323, 8391, 16384, 32768, 65536, 131072, 262144, 524288, 1048576, 2*1048576, 4*1048576, 8*1048576 + }; + var selectedSizes = sizes.Where(x => x >= 16374 ).ToArray(); + GoogleChart.rows = new Row[selectedSizes.Count()]; + var index = 0; + foreach (var size in selectedSizes) + { + double cycles = Tests.TestArray(0, 0, size)-loopOverhead; + double cycles1 = Tests.TestMsvcrtMemmove(3, 0, size) - loopOverhead; + double cycles2 = Tests.TestJames(0, 0, size)-loopOverhead; + double cycles3 = Tests.TestAnderman(3, 3, size) - loopOverhead; + GoogleChart.rows[index++] = new Row + { + c = new[] + { + new C() {v = size}, + new C() {v = cycles}, + new C() {v = cycles1}, + new C() {v = cycles2}, + new C() {v = cycles3} + } + }; + + //double cycles3 = TestCode(TestAnderman); + Console.WriteLine($"{size:0} {(cycles),8:0.00} {(cycles1),8:0.00} {cycles2,8:0.00} {(cycles3),8:0.00} "); + } + Console.WriteLine("ready"); + System.IO.File.WriteAllText(@"chart.json", "chartData="+JsonConvert.SerializeObject(GoogleChart)); + Console.ReadKey(); + Tests.Warmup(); + } while (false); + } + + public static void MainOld() + { Console.WriteLine("Test en compile functions"); - InitArray(Array.Copy); - InitArray(MsvcrtMemove.MsvcrtMemmove); - InitArray(Anderman2.VectorizedCopyAnderman); - InitArray(UnsafeBufferMemmoveJamesqo2.Memmove); + //OldTests.InitArray(Array.Copy); + //OldTests.InitArray(MsvcrtMemove.MsvcrtMemmove); + //OldTests.InitArray(Anderman2.VectorizedCopyAnderman); + //OldTests.InitArray(UnsafeCpblk.Copy); Console.WriteLine($"bytes\titerations\tarray\tmsmemmove\tanderman\tUnsafeBufferMemmoveJamesqo2\tns\tns\tns\tns\tGB\tGB/s\tGB/s\tGB/s\tGB/s"); @@ -39,135 +111,57 @@ public static void Main(string[] args) var copyBytes in new[] { - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, 64, 66, 68, 70, 72, 74, 76, 78, 80, 82, 84, 86, 88, 90, 92, 94, 96, 103, 113, 122, 128, 135, 145, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, 64, 66, 68, 70, 72, 74, 76, 78, 80, 82, 84, 86, 88, 90, 92, 94, 96, 103, 113, 122, 128, 135, + 145, 154, 160, 167, 177, 186, 192, 199, 209, 218, 224, 231, 241, 250, 256, 263, 273, 282, 288, 295, 305, 314, 320, 327, 337, 346, 352, 359, 369, 378, 384, 391, 401, 410, 416, 423, 433, 442, 448, 455, 465, 474, 480, 487, 497, 506, 512, 519, 529, 538, 544, 551, 561, 570, 576, 583, 593, 602, 608, 615, 625, 634, 640, 647, 657, 666, 672, 679, 689, 698, 704, 711, 721, 730, 736, 743, 753, 762, 768, 775, 785, 794, 800, 807, 817, 826, 832, 839, 849, 858, 864, 871, 881, 890, 896, 903, 913, 922, 928, 935, 945, 954, 960, 967, 977, 986, 1024, 1086, 1155, 1223, 1280, 1342, 1411, 1479, 1536, 1598, 1667, 1735, 1792, 1854, 1923, 1991, 2048, 2110, 2179, 2247, 2304, 2366, 2435, 2503, 2560, 2622, 2691, 2759, 2816, 2878, 2947, 3015, 3072, 3134, 3203, 3271, 3328, 3390, 3459, 3527, 3584, 3646, 3715, 3783, 3840, 3902, 3971, 4039, 4096, 4158, 4227, 4295, 4352, 4414, 4483, 4551, 4608, 4670, 4739, 4807, 4864, 4926, 4995, 5063, 5120, 5182, 5251, 5319, 5376, 5438, 5507, 5575, 5632, 5694, 5763, 5831, 5888, 5950, 6019, 6087, 6144, 6206, 6275, 6343, 6400, 6462, 6531, 6599, 6656, 6718, 6787, 6855, 6912, 6974, 7043, 7111, - 7168, 7230, 7299, 7367, 7424, 7486, 7555, 7623, 7680, 7742, 7811, 7879, 7936, 7998, 8067, 8135, 8192, 8254, 8323, 8391 - }.Where(x=>x>=0 && x<10000) ) + 7168, 7230, 7299, 7367, 7424, 7486, 7555, 7623, 7680, 7742, 7811, 7879, 7936, 7998, 8067, 8135, 8192, 8254, 8323, 8391, 16384, 32768, 65536, 131072, 262144, 524288, 1048576, 2*1048576, 4*1048576, 8*1048576 + }.Where(x => x >= 8192)) { Test(copyBytes); } - Console.WriteLine("ready"); - } - private static void InitArray(Action copyAction) + } + private static ulong GetCyclesPerSeond() { - for (var i = 1; i < 256; i++) - { - _src[i] = (byte) i; - _dst[i] = 0; - } - for (var i = 0; i < 256; i++) + var sw = Stopwatch.StartNew(); + var startms = Rdtsc.TimestampP(); + do { - copyAction(_src, 0, _dst, 0, i); - for (var j = 0; i < 256; i++) - { - if (_src[j] != _dst[j]) - throw new Exception($"i={i} _src[j]({_src[j]})!= _dst[j]({_dst[j]})"); - } - } + } while (sw.ElapsedMilliseconds < 1000); + var endms = Rdtsc.TimestampP(); + + return endms - startms; } private static void Test(int copyBytes) { - const double GB = 1024*1024*1024; - var iterations = GetMinIterations(copyBytes); - var s4 = TestUnsafeBufferMemmoveJamesqo2(copyBytes, iterations); - var s1 = TestArrayCopy(copyBytes, iterations); - var s2 = TestMsMemmove(copyBytes, iterations); - var s3 = TestVectorizedIftree(copyBytes, iterations); + const double GB = 1024 * 1024 * 1024; + var iterations = StopwatchTests.GetMinIterations(copyBytes); + var s4 = StopwatchTests.TestUnsafeBufferMemmoveJamesqo2(copyBytes, iterations); + var s1 = StopwatchTests.TestArrayCopy(copyBytes, iterations); + var s2 = StopwatchTests.TestMsMemmove(copyBytes, iterations); + var s3 = StopwatchTests.TestVectorizedIftree(copyBytes, iterations); double baseline = s1.Elapsed.Ticks; Console.Write($"{copyBytes:#######0}"); Console.Write($"\t{iterations:##########0}"); - Console.Write($"\t{(double) s1.Elapsed.Ticks/baseline: 0.00}"); - Console.Write($"\t{(double) s2.Elapsed.Ticks/baseline: 0.00}"); - Console.Write($"\t{(double) s3.Elapsed.Ticks/baseline: 0.00}"); - Console.Write($"\t{(double) s4.Elapsed.Ticks/baseline: 0.00}"); - Console.Write($"\t{s1.Elapsed.TotalMilliseconds*1000000/iterations: 0.00}"); - Console.Write($"\t{s2.Elapsed.TotalMilliseconds*1000000/iterations: 0.00}"); - Console.Write($"\t{s3.Elapsed.TotalMilliseconds*1000000/iterations: 0.00}"); - Console.Write($"\t{s4.Elapsed.TotalMilliseconds*1000000/iterations: 0.00}"); - Console.Write($"\t{copyBytes*(double) iterations/GB: 0.00} "); - Console.Write($"\t{copyBytes*(double) iterations/GB/(s1.Elapsed.TotalMilliseconds/1000): 0.00}"); - Console.Write($"\t{copyBytes*(double) iterations/GB/(s2.Elapsed.TotalMilliseconds/1000): 0.00}"); - Console.Write($"\t{copyBytes*(double) iterations/GB/(s3.Elapsed.TotalMilliseconds/1000): 0.00}"); - Console.Write($"\t{copyBytes*(double) iterations/GB/(s4.Elapsed.TotalMilliseconds/1000): 0.00}"); + Console.Write($"\t{(double)s1.Elapsed.Ticks / baseline: 0.00}"); + Console.Write($"\t{(double)s2.Elapsed.Ticks / baseline: 0.00}"); + Console.Write($"\t{(double)s3.Elapsed.Ticks / baseline: 0.00}"); + Console.Write($"\t{(double)s4.Elapsed.Ticks / baseline: 0.00}"); + Console.Write($"\t{s1.Elapsed.TotalMilliseconds * 1000000 / iterations: 0.00}"); + Console.Write($"\t{s2.Elapsed.TotalMilliseconds * 1000000 / iterations: 0.00}"); + Console.Write($"\t{s3.Elapsed.TotalMilliseconds * 1000000 / iterations: 0.00}"); + Console.Write($"\t{s4.Elapsed.TotalMilliseconds * 1000000 / iterations: 0.00}"); + Console.Write($"\t{copyBytes * (double)iterations / GB: 0.00} "); + Console.Write($"\t{copyBytes * (double)iterations / GB / (s1.Elapsed.TotalMilliseconds / 1000): 0.00}"); + Console.Write($"\t{copyBytes * (double)iterations / GB / (s2.Elapsed.TotalMilliseconds / 1000): 0.00}"); + Console.Write($"\t{copyBytes * (double)iterations / GB / (s3.Elapsed.TotalMilliseconds / 1000): 0.00}"); + Console.Write($"\t{copyBytes * (double)iterations / GB / (s4.Elapsed.TotalMilliseconds / 1000): 0.00}"); Console.WriteLine(); } - - private static long GetMinIterations(int copyBytes) - { - var iterations = _max/(copyBytes == 0 ? 1 : copyBytes); - var s0 = new Stopwatch(); - do - { - s0 = TestArrayCopy(copyBytes, iterations); - if (s0.ElapsedMilliseconds <= 100) - iterations *= 10; - } while (s0.ElapsedMilliseconds <= 100); - iterations = (long) (iterations*(TestTimeInMs/s0.ElapsedMilliseconds)); - - return iterations; - } - - private static Stopwatch TestVectorizedIftree(int copyBytes, long interations) - { - var sw = Stopwatch.StartNew(); - var offset = 0; - for (long i = 0; i < interations; i++) - { - offset += TestMode == -1 ? copyBytes : TestMode; - if (offset + copyBytes >= BufferSize) offset &= 0x3fff; - Anderman2.VectorizedCopyAnderman(_src, offset, _dst, offset, copyBytes); - } - sw.Stop(); - return sw; - } - - private static Stopwatch TestArrayCopy(int copyBytes, long interations) - { - var sw = Stopwatch.StartNew(); - var offset = 0; - for (long i = 0; i < interations; i++) - { - offset += TestMode == -1 ? copyBytes : TestMode; - if (offset + copyBytes >= BufferSize) offset &= 0x3fff; - Array.Copy(_src, offset, _dst, offset, copyBytes); - } - sw.Stop(); - return sw; - } - - - private static Stopwatch TestUnsafeBufferMemmoveJamesqo2(int copyBytes, long interations) - { - var sw = Stopwatch.StartNew(); - var offset = 0; - for (long i = 0; i < interations; i++) - { - offset += TestMode == -1 ? copyBytes : TestMode; - if (offset + copyBytes >= BufferSize) offset &= 0x3fff; - UnsafeBufferMemmoveJamesqo2.Memmove(_src, offset, _dst, offset, copyBytes); - } - sw.Stop(); - return sw; - } - - private static Stopwatch TestMsMemmove(int copyBytes, long interations) - { - var sw = Stopwatch.StartNew(); - var offset = 0; - for (long i = 0; i < interations; i++) - { - offset += TestMode == -1 ? copyBytes : TestMode; - if (offset + copyBytes >= BufferSize) offset &= 0x3fff; - MsvcrtMemove.MsvcrtMemmove(_src, offset, _dst, offset, copyBytes); - } - sw.Stop(); - return sw; - } } } \ No newline at end of file diff --git a/src/DotNetCross.Memory.Copies.Benchmarks2/Rdtsc.cs b/src/DotNetCross.Memory.Copies.Benchmarks2/Rdtsc.cs new file mode 100644 index 0000000..50f3eba --- /dev/null +++ b/src/DotNetCross.Memory.Copies.Benchmarks2/Rdtsc.cs @@ -0,0 +1,292 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Diagnostics; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; + +namespace DotNetCross.Memory.Copies.Benchmarks2 +{ + public static class Rdtsc + { + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + public delegate ulong FuncUInt64(); + + private const uint PAGE_READWRITE = 0x04; + private const uint PAGE_EXECUTE = 0x10; + private const uint PAGE_EXECUTE_READWRITE = 0x40; + private const uint MEM_COMMIT = 0x1000; + private const uint MEM_RELEASE = 0x8000; + + /// + /// Uses rdtsc. On non-Intel uses Stopwatch.GetTimestamp. + /// + public static readonly FuncUInt64 Timestamp; + + /// + /// Uses rdtscp if present. Otherwise uses cpuid + rdtsc. On + /// non-Intel uses Stopwatch.GetTimestamp. + /// + public static readonly FuncUInt64 TimestampP; + + public static readonly bool IsRdtscSupported; + public static readonly bool IsRdtscPSupported; + + static Rdtsc() + { + SystemInfo systemInfo; + GetNativeSystemInfo(out systemInfo); + + if (systemInfo.wProcessorArchitecture != 0 /* PROCESSOR_ARCHITECTURE_INTEL */&& + systemInfo.wProcessorArchitecture != 9 /* PROCESSOR_ARCHITECTURE_AMD64 */) + { + // Fallback for ARM/IA64/... + Timestamp = StopwatchGetTimestamp; + TimestampP = StopwatchGetTimestamp; + IsRdtscSupported = false; + IsRdtscPSupported = false; + return; + } + + byte[] cpuid, rdtsc, rdtscp, rdtsccpuid, dummy; + + IsRdtscSupported = true; + + // Assembly generated with https://defuse.ca/online-x86-assembler.htm + + /* CPUID x64: + push rbx; + mov eax, 0x80000000; + cpuid; + mov ebx, 0x80000001; + cmp eax, ebx; + jb Error; + mov eax, ebx; + cpuid; + mov eax, ecx; + shl rax, 0x20; + or rax, rdx + jmp End; + Error: + xor rax, rax; + End: + pop rbx; + ret; + + 0: 53 push rbx + 1: b8 00 00 00 80 mov eax,0x80000000 + 6: 0f a2 cpuid + 8: bb 01 00 00 80 mov ebx,0x80000001 + d: 39 d8 cmp eax,ebx + f: 72 0f jb 20 + 11: 89 d8 mov eax,ebx + 13: 0f a2 cpuid + 15: 89 c8 mov eax,ecx + 17: 48 c1 e0 20 shl rax,0x20 + 1b: 48 09 d0 or rax,rdx + 1e: eb 03 jmp 23 + 0000000000000020 : + 20: 48 31 c0 xor rax,rax + 0000000000000023 : + 23: 5b pop rbx + 24: c3 ret + */ + cpuid = new byte[] { 0x53, 0xB8, 0x00, 0x00, 0x00, 0x80, 0x0F, 0xA2, 0xBB, 0x01, 0x00, 0x00, 0x80, 0x39, 0xD8, 0x72, 0x16, 0x89, 0xD8, 0x48, 0xC7, 0xC2, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, 0xA2, 0x89, 0xC8, 0x48, 0xC1, 0xE0, 0x20, 0x48, 0x09, 0xD0, 0xEB, 0x03, 0x48, 0x31, 0xC0, 0x5B, 0xC3 }; + + /* RDTSC x64: + rdtsc; + shl rdx, 0x20; + or rax,rdx; + ret; + + 0: 0f 31 rdtsc + 2: 48 c1 e2 20 shl rdx,0x20 + 6: 48 09 d0 or rax,rdx + 9: c3 ret + */ + rdtsc = new byte[] { 0x0F, 0x31, 0x48, 0xC1, 0xE2, 0x20, 0x48, 0x09, 0xD0, 0xC3 }; + + /* RDTSCP x64 + rdtscp; + shl rdx, 0x20; + or rax, rdx; + ret; + + 0: 0f 01 f9 rdtscp + 3: 48 c1 e2 20 shl rdx,0x20 + 7: 48 09 d0 or rax,rdx + a: c3 ret + */ + rdtscp = new byte[] { 0x0F, 0x01, 0xF9, 0x48, 0xC1, 0xE2, 0x20, 0x48, 0x09, 0xD0, 0xC3 }; + + /* RDTSC + CPUID x64 + push rbx; + xor eax, eax; + cpuid; + rdtsc; + shl rdx, 0x20; + or rax, rdx; + pop rbx; + ret; + + 0: 53 push rbx + 1: 31 c0 xor eax,eax + 3: 0f a2 cpuid + 5: 0f 31 rdtsc + 7: 48 c1 e2 20 shl rdx,0x20 + b: 48 09 d0 or rax,rdx + e: 5b pop rbx + f: c3 ret + */ + rdtsccpuid = new byte[] { 0x53, 0x31, 0xC0, 0x0F, 0xA2, 0x0F, 0x31, 0x48, 0xC1, 0xE2, 0x20, 0x48, 0x09, 0xD0, 0x5B, 0xC3 }; + dummy = new byte[] {0xC3}; + + var buf = IntPtr.Zero; + + try + { + // We pad the functions to 64 bytes (the length of a cache + // line on the Intel processors) + var cpuidLength = (cpuid.Length & 63) != 0 ? (cpuid.Length | 63) + 1 : cpuid.Length; + var rdtscLength = (rdtsc.Length & 63) != 0 ? (rdtsc.Length | 63) + 1 : rdtsc.Length; + var rdtscpLength = (rdtscp.Length & 63) != 0 ? (rdtscp.Length | 63) + 1 : rdtscp.Length; + var rdtsccpuidLength = (rdtsccpuid.Length & 63) != 0 ? (rdtsccpuid.Length | 63) + 1 : rdtsccpuid.Length; + var dummyLength = (dummy.Length & 63) != 0 ? (dummy.Length | 63) + 1 : dummy.Length; + + // We don't know which one of rdtscp or rdtsccpuid we will + // use, so we calculate space for the biggest one. + // Note that it is very unlikely that we will go over 4096 + // bytes (the minimum size of memory allocated by + // VirtualAlloc) + var totalLength = cpuidLength + rdtscLength + Math.Max(rdtscpLength, rdtsccpuidLength)+dummyLength; + + // We VirtualAlloc totalLength bytes, with R/W access + // Note that from what I've read, MEM_RESERVE is useless + // if the first parameter is IntPtr.Zero + buf = VirtualAlloc(IntPtr.Zero, (IntPtr)totalLength, MEM_COMMIT, PAGE_EXECUTE_READWRITE); + + if (buf == IntPtr.Zero) + { + throw new Win32Exception(); + } + + // Copy cpuid instructions in the buf + Marshal.Copy(cpuid, 0, buf, cpuid.Length); + + for (var i = cpuid.Length; i < cpuidLength; i++) + { + Marshal.WriteByte(buf, i, 0x90); // nop + } + + // Copy rdtsc instructions in the buf + Marshal.Copy(rdtsc, 0, buf + cpuidLength, rdtsc.Length); + + for (var i = rdtsc.Length; i < rdtscLength; i++) + { + Marshal.WriteByte(buf, cpuidLength + i, 0x90); // nop + } + var cpuidFunc = Marshal.GetDelegateForFunctionPointer(buf); + + // We use cpuid, EAX=0x80000001 to check for the rdtscp + var supportedFeatures = cpuidFunc(); + + byte[] rdtscpSelected; + int rdtscpSelectedLength; + + // Check the rdtscp flag + if ((supportedFeatures & (1L << 27)) != 0) + { + // rdtscp supported + rdtscpSelected = rdtscp; + rdtscpSelectedLength = rdtscpLength; + IsRdtscPSupported = true; + } + else + { + // rdtscp not supported. We use cpuid + rdtsc + rdtscpSelected = rdtsccpuid; + rdtscpSelectedLength = rdtsccpuidLength; + IsRdtscPSupported = false; + } + + // Copy rdtscp/rdtsccpuid instructions in the buf + Marshal.Copy(rdtscpSelected, 0, buf + cpuidLength + rdtscLength, rdtscpSelected.Length); + + for (var i = rdtscpSelected.Length; i < rdtscpSelectedLength; i++) + { + Marshal.WriteByte(buf, cpuidLength + rdtscLength + i, 0x90); // nop + } + + + // Change the access of the allocated memory from R/W to Execute + uint oldProtection; + var result = VirtualProtect(buf, (IntPtr)totalLength, PAGE_EXECUTE, out oldProtection); + + if (!result) + { + throw new Win32Exception(); + } + + // Create a delegate to the "function" + Timestamp = Marshal.GetDelegateForFunctionPointer(buf + cpuidLength); + TimestampP = Marshal.GetDelegateForFunctionPointer(buf + cpuidLength + rdtscLength); + buf = IntPtr.Zero; + } + finally + { + // There was an error! + if (buf != IntPtr.Zero) + { + // Free the allocated memory + var result = VirtualFree(buf, IntPtr.Zero, MEM_RELEASE); + + if (!result) + { + throw new Win32Exception(); + } + } + } + } + + public static FuncUInt64 TestMovsb { get; set; } + + [DllImport("kernel32.dll", ExactSpelling = true)] + private static extern void GetNativeSystemInfo(out SystemInfo lpSystemInfo); + + [DllImport("kernel32.dll", ExactSpelling = true, SetLastError = true)] + private static extern IntPtr VirtualAlloc(IntPtr lpAddress, IntPtr dwSize, uint flAllocationType, uint flProtect); + + [DllImport("kernel32.dll", ExactSpelling = true, SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + private static extern bool VirtualProtect(IntPtr lpAddress, IntPtr dwSize, uint flAllocationType, out uint lpflOldProtect); + + [DllImport("kernel32.dll", ExactSpelling = true, SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + private static extern bool VirtualFree(IntPtr lpAddress, IntPtr dwSize, uint dwFreeType); + + // Fallback if rdtsc isn't available. We can't use directly + // Stopwatch.GetTimestamp() because the return type is different. + private static ulong StopwatchGetTimestamp() + { + return unchecked((ulong)Stopwatch.GetTimestamp()); + } + + [StructLayout(LayoutKind.Sequential)] + private struct SystemInfo + { + public readonly ushort wProcessorArchitecture; + public readonly ushort wReserved; + public readonly uint dwPageSize; + public readonly IntPtr lpMinimumApplicationAddress; + public readonly IntPtr lpMaximumApplicationAddress; + public readonly IntPtr dwActiveProcessorMask; + public readonly uint dwNumberOfProcessors; + public readonly uint dwProcessorType; + public readonly uint dwAllocationGranularity; + public readonly ushort wProcessorLevel; + public readonly ushort wProcessorRevision; + } + } +} diff --git a/src/DotNetCross.Memory.Copies.Benchmarks2/StopwatchTests.cs b/src/DotNetCross.Memory.Copies.Benchmarks2/StopwatchTests.cs new file mode 100644 index 0000000..9599723 --- /dev/null +++ b/src/DotNetCross.Memory.Copies.Benchmarks2/StopwatchTests.cs @@ -0,0 +1,112 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace DotNetCross.Memory.Copies.Benchmarks2 +{ + public static class StopwatchTests + { + private const int BufferSize = 1024 * 1024 * 1024; + private static readonly byte[] _src = new byte[BufferSize]; + private static readonly byte[] _dst = new byte[BufferSize]; + private static int _copySize; + private const long _max = 30000000; + private const double TestTimeInMs = 1000.0; + + //Use a contant to change the test. Vars has huge impact on the performance + private const int Cached = 0; //Pseudo Random Test + private const int Randomizor = 0x13751; //Pseudo Random Test + private const int Alignment = 1; //Alignment test + private const int Sequence = -1; //read seq through array + private const int TestMode = Cached; //Alignment test + + public static long GetMinIterations(int copyBytes) + { + var iterations = _max / (copyBytes == 0 ? 1 : copyBytes); + var s0 = new Stopwatch(); + do + { + s0 = TestArrayCopy(copyBytes, iterations); + if (s0.ElapsedMilliseconds <= 100) + iterations *= 10; + } while (s0.ElapsedMilliseconds <= 100); + iterations = (long)(iterations * (TestTimeInMs / s0.ElapsedMilliseconds)); + + return iterations; + } + + public static Stopwatch TestVectorizedIftree(int copyBytes, long interations) + { + var sw = Stopwatch.StartNew(); + var offset = 0; + for (long i = 0; i < interations; i++) + { + offset += TestMode == -1 ? copyBytes : TestMode; + if (offset + copyBytes >= BufferSize) offset &= 0x3fff; + AndermanOptimized.Memmove(_src, offset, _dst, offset, copyBytes); + } + sw.Stop(); + return sw; + } + + public static Stopwatch TestArrayCopy(int copyBytes, long interations) + { + var sw = Stopwatch.StartNew(); + var offset = 0; + for (long i = 0; i < interations; i++) + { + offset += TestMode == -1 ? copyBytes : TestMode; + if (offset + copyBytes >= BufferSize) offset &= 0x3fff; + Array.Copy(_src, offset, _dst, offset, copyBytes); + } + sw.Stop(); + return sw; + } + + public static Stopwatch TestUnsafeBufferMemmoveJamesqo2(int copyBytes, long interations) + { + var sw = Stopwatch.StartNew(); + var offset = 0; + for (long i = 0; i < interations; i++) + { + offset += TestMode == -1 ? copyBytes : TestMode; + if (offset + copyBytes >= BufferSize) offset &= 0x3fff; + UnsafeBufferMemmoveJamesqo2.Memmove(_src, offset, _dst, offset, copyBytes); + } + sw.Stop(); + return sw; + } + + public static Stopwatch TestMsMemmove(int copyBytes, long interations) + { + var sw = Stopwatch.StartNew(); + var offset = 0; + for (long i = 0; i < interations; i++) + { + offset += TestMode == -1 ? copyBytes : TestMode; + if (offset + copyBytes >= BufferSize) offset &= 0x3fff; + MsvcrtMemove.Memmove(_src, offset, _dst, offset, copyBytes); + } + sw.Stop(); + return sw; + } + + private static Stopwatch TestUnsafeCpblk(int copyBytes, long interations) + { + var sw = Stopwatch.StartNew(); + var offset = 0; + for (long i = 0; i < interations; i++) + { + offset += TestMode == -1 ? copyBytes : TestMode; + if (offset + copyBytes >= BufferSize) offset &= 0x3fff; + UnsafeCpblk.Copy(_src, offset, _dst, offset, copyBytes); + } + sw.Stop(); + return sw; + } + + } +} diff --git a/src/DotNetCross.Memory.Copies.Benchmarks2/Tests.cs b/src/DotNetCross.Memory.Copies.Benchmarks2/Tests.cs new file mode 100644 index 0000000..6197c38 --- /dev/null +++ b/src/DotNetCross.Memory.Copies.Benchmarks2/Tests.cs @@ -0,0 +1,233 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace DotNetCross.Memory.Copies.Benchmarks2 +{ + public static class Tests + { + private const int BufferSize = 1024 * 1024 * 1024; + private static readonly byte[] _src = new byte[BufferSize]; + private static readonly byte[] _dst = new byte[BufferSize]; + + const int MinIterations = 50; + public static ulong TestDuration = 1000000; + + public static unsafe int GetOffsetDst() + { + fixed (byte* pSrcOrigin = &_dst[0]) + { + return 0x10 - (int)((ulong)pSrcOrigin & 0xF); + } + } + + public static unsafe int GetOffsetSrc() + { + fixed (byte* pSrcOrigin = &_src[0]) + { + return 0x10 - (int)((ulong)pSrcOrigin & 0xF); + } + } + + public static void Warmup() + { + for (var i = 1; i < 256; i++) + { + _src[i] = (byte)i; + _dst[i] = 0; + } + for (var size = 0; size < 32768; size++) + { + AndermanOptimized.Memmove(_src, 0, _dst, 0, size); + for (var j = 0; j < size + 0; j++) + { + if (_src[j] != _dst[j]) + throw new Exception($"i={size}, j={j} _src[j]({_src[j]}) != _dst[j]({_dst[j]})"); + _dst[j] = 0; + } + } + } + + public static double TestOverhead() + { + var mincycles = ulong.MaxValue; + var startTest = Rdtsc.TimestampP(); + var testCycles = 0UL; + + int dummy = 10; + do + { + ulong cycles = 0; + for (var j = 1; j < 1000; j++) + { + var start = Rdtsc.TimestampP(); + for (var h = 1; h < MinIterations; h++) + { + dummy = h; + } + var end = Rdtsc.TimestampP(); + cycles += end - start; + } + + if (cycles < mincycles) mincycles = cycles; + + testCycles = Rdtsc.TimestampP() - startTest; + } while (testCycles < TestDuration && testCycles > 0); + return dummy > 0 ? (mincycles / 1000.0) / (double)MinIterations : 0; + } + + + public static double TestAnderman(int srcOffset, int dstOffset, int size) + { + var mincycles = ulong.MaxValue; + var startTest = Rdtsc.TimestampP(); + var testCycles = 0UL; + + do + { + var start = Rdtsc.TimestampP(); + for (var j = 1; j < MinIterations; j++) + { + AndermanOptimized.Memmove(_src, srcOffset, _dst, dstOffset, size); + } + var end = Rdtsc.TimestampP(); + var cycles = end - start; + if (cycles <= mincycles) + { + mincycles = cycles; + } + testCycles = Rdtsc.TimestampP() - startTest; + } while (testCycles < TestDuration && testCycles > 0); + return (mincycles / (double)MinIterations); + } + + public static double TestJames(int srcOffset, int dstOffset, int size) + { + var mincycles = ulong.MaxValue; + var startTest = Rdtsc.TimestampP(); + var testCycles = 0UL; + + do + { + var start = Rdtsc.TimestampP(); + for (var j = 1; j < MinIterations; j++) + { + UnsafeBufferMemmoveJamesqo2.Memmove(_src, srcOffset, _dst, dstOffset, size); + } + var end = Rdtsc.TimestampP(); + var cycles = end - start; + if (cycles <= mincycles) + { + mincycles = cycles; + } + testCycles = Rdtsc.TimestampP() - startTest; + } while (testCycles < TestDuration && testCycles > 0); + return (mincycles / (double)MinIterations); + } + + public static double TestMsvcrtMemmove(int srcOffset, int dstOffset, int size) + { + var mincycles = ulong.MaxValue; + var startTest = Rdtsc.TimestampP(); + var testCycles = 0UL; + + do + { + var start = Rdtsc.TimestampP(); + for (var j = 1; j < MinIterations; j++) + { + MsvcrtMemove.Memmove(_src, srcOffset, _dst, dstOffset, size); + } + var end = Rdtsc.TimestampP(); + var cycles = end - start; + if (cycles <= mincycles) + { + mincycles = cycles; + } + testCycles = Rdtsc.TimestampP() - startTest; + } while (testCycles < TestDuration && testCycles > 0); + return (mincycles / (double)MinIterations); + } + + public static double TestArray(int srcOffset, int dstOffset, int size) + { + var mincycles = ulong.MaxValue; + var startTest = Rdtsc.TimestampP(); + var testCycles = 0UL; + + do + { + var start = Rdtsc.TimestampP(); + for (var j = 1; j < MinIterations; j++) + { + Array.Copy(_src, srcOffset, _dst, dstOffset, size); + } + var end = Rdtsc.TimestampP(); + var cycles = end - start; + if (cycles <= mincycles) + { + mincycles = cycles; + } + testCycles = Rdtsc.TimestampP() - startTest; + } while (testCycles < TestDuration && testCycles > 0); + return (mincycles / (double)MinIterations); + } + + public static double TestMovSb(int srcOffset, int dstOffset, int size) + { + var mincycles = ulong.MaxValue; + var startTest = Rdtsc.TimestampP(); + var testCycles = 0UL; + + do + { + var start = Rdtsc.TimestampP(); + for (var j = 1; j < MinIterations; j++) + { + AndermanMovsb.Memmove(_src, srcOffset, _dst, dstOffset, size); + } + var end = Rdtsc.TimestampP(); + var cycles = end - start; + if (cycles <= mincycles) + { + mincycles = cycles; + } + testCycles = Rdtsc.TimestampP() - startTest; + } while (testCycles < TestDuration && testCycles > 0); + return (mincycles / (double)MinIterations); + } + + public static double TestDelegate(Func copyAction, int srcOffset, int dstOffset, int size) + { + var mincycles = ulong.MaxValue; + var startTest = Rdtsc.TimestampP(); + var testCycles = 0UL; + + do + { + var cycles = copyAction(srcOffset, dstOffset, size); + if (cycles <= mincycles) + { + mincycles = cycles; + } + testCycles = Rdtsc.TimestampP() - startTest; + } while (testCycles < TestDuration && testCycles > 0); + + return (mincycles / (double)MinIterations); + } + + public static ulong TestAndermanDelegate(int srcOffset, int dstOffset, int size) + { + var start = Rdtsc.TimestampP(); + for (var j = 1; j < MinIterations; j++) + { + AndermanOptimized.Memmove(_src, srcOffset, _dst, dstOffset, size); + } + var end = Rdtsc.TimestampP(); + return end - start; + } + } +} diff --git a/src/DotNetCross.Memory.Copies.Benchmarks2/UnsafeBufferMemmoveJamesqo2.cs b/src/DotNetCross.Memory.Copies.Benchmarks2/UnsafeBufferMemmoveJamesqo2.cs index 21eb199..afe583f 100644 --- a/src/DotNetCross.Memory.Copies.Benchmarks2/UnsafeBufferMemmoveJamesqo2.cs +++ b/src/DotNetCross.Memory.Copies.Benchmarks2/UnsafeBufferMemmoveJamesqo2.cs @@ -1,6 +1,8 @@ #define BIT64 #define AMD64 using System; +using System.Numerics; + using System.Collections.Generic; using System.Diagnostics; using System.Linq; @@ -12,28 +14,27 @@ #else // BIT64 using nuint = System.UInt32; #endif // BIT64 -namespace test +namespace DotNetCross.Memory.Copies.Benchmarks2 { public class UnsafeBufferMemmoveJamesqo2 { - public static unsafe void Memmove(byte[] src, int srcOffset, byte[] dst, int dstOffset, int count) + public static unsafe void Memmove(byte[] src, int srcOffset, byte[] dst, int dstOffset, int len) { if (src == null || dst == null) throw new ArgumentNullException(nameof(src)); - if (count < 0 || srcOffset < 0 || dstOffset < 0) throw new ArgumentOutOfRangeException(nameof(count)); - if (srcOffset + count > src.Length) throw new ArgumentException(nameof(src)); - if (dstOffset + count > dst.Length) throw new ArgumentException(nameof(dst)); + if (len < 0 || srcOffset < 0 || dstOffset < 0) throw new ArgumentOutOfRangeException(nameof(len)); + if (srcOffset + len > src.Length) throw new ArgumentException(nameof(src)); + if (dstOffset + len > dst.Length) throw new ArgumentException(nameof(dst)); - fixed (byte* srcOrigin = src) - fixed (byte* dstOrigin = dst) + fixed (byte* pSrcOrigin = &src[srcOffset]) + fixed (byte* pDstOrigin = &dst[dstOffset]) { - var pSrc = srcOrigin + srcOffset; - var pDst = dstOrigin + dstOffset; - - Memmove(pDst, pSrc, (nuint)count); + var srcOrigin = pSrcOrigin; + var dest = pDstOrigin; + Memmove(dest, srcOrigin, (nuint)len); } } - [System.Security.SecurityCritical] + //[System.Security.SecurityCritical] internal static unsafe void Memmove(byte* dest, byte* src, nuint len) { // P/Invoke into the native version when the buffers are overlapping and the copy needs to be performed backwards @@ -51,7 +52,6 @@ internal static unsafe void Memmove(byte* dest, byte* src, nuint len) // The switch will be very fast since it can be implemented using a jump // table in assembly. See http://stackoverflow.com/a/449297/4077294 for more info. - switch (len) { case 0: @@ -188,7 +188,7 @@ internal static unsafe void Memmove(byte* dest, byte* src, nuint len) i = 16u - i; #if AMD64 - // SIMD is enabled for AMD64, so take advantage of that and use movdqu + // SIMD is enabled for AMD64, so take advantage of that and use movdqu *(Buffer16*)dest = *(Buffer16*)src; #elif ARM64 // ARM64 has 64-bit words but no SIMD yet, so make 2 word writes @@ -241,8 +241,8 @@ internal static unsafe void Memmove(byte* dest, byte* src, nuint len) // we save on writes to dest/src. #if AMD64 - // Write 64 bytes at a time, taking advantage of xmm register on AMD64 - // This will be translated to 4 movdqus (maybe movdqas in the future, dotnet/coreclr#2725) + // Write 64 bytes at a time, taking advantage of xmm register on AMD64 + // This will be translated to 4 movdqus (maybe movdqas in the future, dotnet/coreclr#2725) *(Buffer64*)(dest + i) = *(Buffer64*)(src + i); #elif ARM64 // ARM64: Also unroll by 64 bytes, this time using longs since we don't @@ -275,10 +275,10 @@ internal static unsafe void Memmove(byte* dest, byte* src, nuint len) // If we've reached this point, there are at most chunk - 1 bytes left #if BIT64 - // mask & 63 represents how many bytes there are left. - // if the mask & 32 bit is set that means this number - // will be >= 32. (same principle applies for other - // powers of 2 below) + // mask & 63 represents how many bytes there are left. + // if the mask & 32 bit is set that means this number + // will be >= 32. (same principle applies for other + // powers of 2 below) if ((mask & 32) != 0) { #if AMD64 diff --git a/src/DotNetCross.Memory.Copies.Benchmarks2/chart.html b/src/DotNetCross.Memory.Copies.Benchmarks2/chart.html new file mode 100644 index 0000000..1447ad5 --- /dev/null +++ b/src/DotNetCross.Memory.Copies.Benchmarks2/chart.html @@ -0,0 +1,52 @@ + + + + chart + + + + + + + +
+ + \ No newline at end of file diff --git a/src/DotNetCross.Memory.Copies.Benchmarks2/msvcrtMemove.cs b/src/DotNetCross.Memory.Copies.Benchmarks2/msvcrtMemove.cs index fca3da4..8bf7543 100644 --- a/src/DotNetCross.Memory.Copies.Benchmarks2/msvcrtMemove.cs +++ b/src/DotNetCross.Memory.Copies.Benchmarks2/msvcrtMemove.cs @@ -1,11 +1,12 @@ using System; +using System.Diagnostics; using System.Runtime.InteropServices; -namespace test +namespace DotNetCross.Memory.Copies.Benchmarks2 { public static class MsvcrtMemove { - public static unsafe void MsvcrtMemmove(byte[] src, int srcOffset, byte[] dst, int dstOffset, int count) + public static unsafe void Memmove(byte[] src, int srcOffset, byte[] dst, int dstOffset, int count) { fixed (byte* srcOrigin = src) fixed (byte* dstOrigin = dst) diff --git a/src/DotNetCross.Memory.Copies.Benchmarks2/project.json b/src/DotNetCross.Memory.Copies.Benchmarks2/project.json index 342c551..db8beba 100644 --- a/src/DotNetCross.Memory.Copies.Benchmarks2/project.json +++ b/src/DotNetCross.Memory.Copies.Benchmarks2/project.json @@ -1,4 +1,4 @@ -{ +{ "version": "1.0.0-*", "buildOptions": { "emitEntryPoint": true, @@ -10,9 +10,10 @@ "type": "platform", "version": "1.0.0" }, - "runtime.win7-x64.Microsoft.NETCore.Runtime.CoreCLR": "1.0.4", "System.Runtime.CompilerServices.Unsafe": "4.0.0", - "System.Linq": "4.1.0" + "System.Linq": "4.1.0", + "System.Reflection": "4.1.0", + "Newtonsoft.Json": "9.0.1", }, "runtimes": { "win8-x64": {} diff --git a/src/mov_repsb/AndermanMovsb.cs b/src/mov_repsb/AndermanMovsb.cs new file mode 100644 index 0000000..461242b --- /dev/null +++ b/src/mov_repsb/AndermanMovsb.cs @@ -0,0 +1,101 @@ +using System; +using System.ComponentModel; +using System.Runtime.InteropServices; + +namespace mov_repsb +{ + public static class AndermanMovsb + { + private const uint PAGE_READWRITE = 0x04; + private const uint PAGE_EXECUTE = 0x10; + private const uint PAGE_EXECUTE_READWRITE = 0x40; + private const uint MEM_COMMIT = 0x1000; + private const uint MEM_RELEASE = 0x8000; + + [DllImport("kernel32.dll", ExactSpelling = true, SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + private static extern bool VirtualProtect(IntPtr lpAddress, IntPtr dwSize, uint flAllocationType, out uint lpflOldProtect); + + [DllImport("kernel32.dll", ExactSpelling = true, SetLastError = true)] + private static extern IntPtr VirtualAlloc(IntPtr lpAddress, IntPtr dwSize, uint flAllocationType, uint flProtect); + + [DllImport("kernel32.dll", ExactSpelling = true, SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + private static extern bool VirtualFree(IntPtr lpAddress, IntPtr dwSize, uint dwFreeType); + + public delegate int MyMoveSb(ulong RCX, ulong RDX, ulong R8); + + public static MyMoveSb MyAdd; + + static AndermanMovsb() + { + //RAX�=�0000008377040000 RBX�=�0000000000000005 RCX�=�0000000000000004 RDX�=�0000000000000005 RSI�=�0000008300018350 RDI�=�0000000000000004 R8 �=�0000000000000006 R9 �=�000007F9C2460F84 R10�=�0000000000000000 R11�=�0000000000000000 R12�=�00000083754BDF70 R13�=�0000000000000004 R14�=�0000000000000006 R15�=�000000837555A4D0 RIP�=�0000008377040000 RSP�=�00000083754BDDF8 RBP�=�00000083754BDEA0 EFL�=�00000246 + /* + 0: 49 89 fb mov r11,rdi + 3: 49 89 f2 mov r10,rsi + 6: 48 89 ce mov rsi,rcx + 9: 48 89 d7 mov rdi,rdx + c: 4c 89 c1 mov rcx,r8 + f: f3 a4 rep movs BYTE PTR es:[rdi],BYTE PTR ds:[rsi] + 11: 4c 89 d6 mov rsi,r10 + 14: 4c 89 df mov rdi,r11 + 17: c3 ret + */ + byte[] assemblyCode = { 0x49, 0x89, 0xFB, 0x49, 0x89, 0xF2, 0x48, 0x89, 0xCE, 0x48, 0x89, 0xD7, 0x4C, 0x89, 0xC1, 0xF3, 0xA4, 0x4C, 0x89, 0xD6, 0x4C, 0x89, 0xDF, 0xC3 }; + + + // We need to push the code bytes into a native buffer + + var bufPtr = IntPtr.Zero; + + try + { + // Put the sourcecode in a native buffer + bufPtr = VirtualAlloc(IntPtr.Zero, (IntPtr)assemblyCode.Length, MEM_COMMIT, PAGE_EXECUTE_READWRITE); + + Marshal.Copy(assemblyCode, 0, bufPtr, assemblyCode.Length); + uint oldProtection; + var result = VirtualProtect(bufPtr, (IntPtr)assemblyCode.Length, PAGE_EXECUTE, out oldProtection); + + if (!result) + { + throw new Win32Exception(); + } + MyAdd = Marshal.GetDelegateForFunctionPointer(bufPtr); + bufPtr = IntPtr.Zero; + } + finally + { + // Free the native buffer + if (bufPtr != IntPtr.Zero) + { + var result = VirtualFree(bufPtr, IntPtr.Zero, MEM_RELEASE); + if (!result) + { + throw new Win32Exception(); + } + } + } + } + private const int BufferSize = 1024 * 1024 * 1024; + private static readonly byte[] _src = new byte[BufferSize]; + private static readonly byte[] _dst = new byte[BufferSize]; + + public static unsafe void VectorizedCopyAnderman() + { + for (var i = 0; i < 256; i++) + _src[i] = (byte)i; + fixed (byte* pSrcOrigin = &_src[0]) + fixed (byte* pDstOrigin = &_dst[0]) + { + var pSrc = pSrcOrigin; + var pDst = pDstOrigin; + var result1 = MyAdd((ulong)pSrc, (ulong)pDst,256); + for (var i = 0; i < 256; i++) + Console.WriteLine("Result: {0}", _dst[i]); + Console.ReadKey(); + + } + } + } +} \ No newline at end of file diff --git a/src/mov_repsb/Program.cs b/src/mov_repsb/Program.cs new file mode 100644 index 0000000..c6c7101 --- /dev/null +++ b/src/mov_repsb/Program.cs @@ -0,0 +1,14 @@ +using System.Diagnostics; + +namespace mov_repsb +{ + internal class Program + + { + private static void Main() + + { + AndermanMovsb.VectorizedCopyAnderman(); + } + } +} \ No newline at end of file diff --git a/src/mov_repsb/Properties/AssemblyInfo.cs b/src/mov_repsb/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..1c454eb --- /dev/null +++ b/src/mov_repsb/Properties/AssemblyInfo.cs @@ -0,0 +1,19 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("mov_repsb")] +[assembly: AssemblyTrademark("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("7720672a-d8a5-4db9-a4f6-810fab98e3cc")] diff --git a/src/mov_repsb/mov_repsb.xproj b/src/mov_repsb/mov_repsb.xproj new file mode 100644 index 0000000..80bbe97 --- /dev/null +++ b/src/mov_repsb/mov_repsb.xproj @@ -0,0 +1,21 @@ + + + + 14.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + + + 7720672a-d8a5-4db9-a4f6-810fab98e3cc + mov_repsb + .\obj + .\bin\ + v4.6.1 + + + + 2.0 + + + diff --git a/src/mov_repsb/project.json b/src/mov_repsb/project.json new file mode 100644 index 0000000..c66185f --- /dev/null +++ b/src/mov_repsb/project.json @@ -0,0 +1,21 @@ +{ + "version": "1.0.0-*", + "buildOptions": { + "emitEntryPoint": true, + "optimize": true, + "allowUnsafe": true + }, + + "dependencies": { + "Microsoft.NETCore.App": { + "type": "platform", + "version": "1.0.0" + } + }, + + "frameworks": { + "netcoreapp1.0": { + "imports": "dnxcore50" + } + } +} From ca2329cd33f2e280873324e4d87d24aead0a6a01 Mon Sep 17 00:00:00 2001 From: Thom Kiesewetter Date: Sat, 27 Aug 2016 17:14:07 +0200 Subject: [PATCH 6/7] Remove old tests --- .gitignore | 1 + .../Program.cs | 138 +++++------------- .../StopwatchTests.cs | 112 -------------- .../Tests.cs | 84 ++++++----- .../chart.html | 22 +-- .../msvcrtMemove.cs | 9 +- 6 files changed, 93 insertions(+), 273 deletions(-) delete mode 100644 src/DotNetCross.Memory.Copies.Benchmarks2/StopwatchTests.cs diff --git a/.gitignore b/.gitignore index a83e2a5..c5b5bf6 100644 --- a/.gitignore +++ b/.gitignore @@ -237,3 +237,4 @@ _Pvt_Extensions /src/DotNetCross.Memory.Copies.Benchmarks2/chart.json /I5-4590.xlsx /I7-3610QM.xlsx +/src/DotNetCross.Memory.Copies.Benchmarks2/chart0_8400.json diff --git a/src/DotNetCross.Memory.Copies.Benchmarks2/Program.cs b/src/DotNetCross.Memory.Copies.Benchmarks2/Program.cs index 38d617e..845b517 100644 --- a/src/DotNetCross.Memory.Copies.Benchmarks2/Program.cs +++ b/src/DotNetCross.Memory.Copies.Benchmarks2/Program.cs @@ -1,129 +1,93 @@ using System; -using System.ComponentModel; using System.Diagnostics; +using System.IO; using System.Linq; -using System.Reflection; -using System.Runtime.InteropServices; -using System.Threading; using Newtonsoft.Json; namespace DotNetCross.Memory.Copies.Benchmarks2 { public class Program { + private const ulong TestDuration = 100; //private static extern bool QueryThreadCycleTime(IntPtr hThread, out ulong cycles); //private static readonly IntPtr PseudoHandle = (IntPtr)(-2); - public static double loopOverhead = 0; - public static ulong CyclesPerSecond = 0; - public static double nsPerCycle = 0; - private const ulong TestDuration = 100; + public static double loopOverhead; + public static ulong CyclesPerSecond; + public static double nsPerCycle; public static void Main(string[] args) { Console.WriteLine($"Warmup..."); //Tests.Warmup(); CyclesPerSecond = GetCyclesPerSeond(); - nsPerCycle = (1000 * 1000 * 1000.0) / CyclesPerSecond; - Tests.TestDuration = TestDuration * CyclesPerSecond / 1000; - loopOverhead = Tests.TestOverhead(); + nsPerCycle = 1000*1000*1000.0/CyclesPerSecond; + Tests.TestDuration = TestDuration*CyclesPerSecond/1000; + loopOverhead = Tests.TestOverhead(1000,1000); Console.WriteLine($"offset src: {Tests.GetOffsetSrc():X04}"); Console.WriteLine($"offset dst: {Tests.GetOffsetDst():X04} "); Console.WriteLine($"CyclesPerSecond: {CyclesPerSecond,5:0} "); Console.WriteLine($"nsPerCycle: {nsPerCycle,5:0.0000} "); Console.WriteLine($"loopOverhead: {loopOverhead,5:0.0000} Cycles"); - Console.WriteLine($" {loopOverhead * nsPerCycle,5:0.0000} ns "); + Console.WriteLine($" {loopOverhead*nsPerCycle,5:0.0000} ns "); Console.WriteLine($"Starting..."); - - do { var GoogleChart = new GoogleChart { cols = new[] { - new Col() { label = "X" ,type="number"}, - new Col() { label = "ArrayCopy" ,type="number"}, - new Col() { label = "MsvcrtMemmove" ,type="number"}, - new Col() { label = "Anderman",type="number" }, - new Col() { label = "AndermanMovsb" ,type="number"}, + new Col {label = "X", type = "number"}, + new Col {label = "ArrayCopy", type = "number"}, + new Col {label = "MsvcrtMemmove", type = "number"}, + new Col {label = "TestJames", type = "number"}, + new Col {label = "Anderman", type = "number"} } }; - var sizes = new[] - { - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, 64, 66, 68, 70, 72, 74, 76, 78, 80, 82, 84, 86, 88, 90, 92, 94, 96, 103, 113, 122, 128, 135, - 145, - 154, 160, 167, 177, 186, 192, 199, 209, 218, 224, 231, 241, 250, 256, 263, 273, 282, 288, 295, 305, 314, 320, 327, 337, 346, 352, 359, 369, 378, 384, 391, 401, 410, 416, 423, 433, 442, 448, 455, 465, 474, 480, 487, 497, 506, 512, 519, 529, 538, 544, 551, 561, 570, 576, 583, - 593, 602, 608, 615, 625, 634, 640, 647, 657, 666, 672, 679, 689, 698, 704, 711, 721, 730, 736, 743, 753, 762, 768, 775, 785, 794, 800, 807, 817, 826, 832, 839, 849, 858, 864, 871, 881, 890, 896, 903, 913, 922, 928, 935, 945, 954, 960, 967, 977, 986, 1024, 1086, 1155, 1223, - 1280, 1342, 1411, 1479, 1536, 1598, 1667, 1735, 1792, 1854, 1923, 1991, 2048, 2110, 2179, 2247, 2304, 2366, 2435, 2503, 2560, 2622, 2691, 2759, 2816, 2878, 2947, 3015, 3072, 3134, 3203, 3271, 3328, 3390, 3459, 3527, 3584, 3646, 3715, 3783, 3840, 3902, 3971, 4039, 4096, 4158, - 4227, 4295, 4352, 4414, 4483, 4551, 4608, 4670, 4739, 4807, 4864, 4926, 4995, 5063, 5120, 5182, 5251, 5319, 5376, 5438, 5507, 5575, 5632, 5694, 5763, 5831, 5888, 5950, 6019, 6087, 6144, 6206, 6275, 6343, 6400, 6462, 6531, 6599, 6656, 6718, 6787, 6855, 6912, 6974, 7043, 7111, - 7168, 7230, 7299, 7367, 7424, 7486, 7555, 7623, 7680, 7742, 7811, 7879, 7936, 7998, 8067, 8135, 8192, 8254, 8323, 8391, 16384, 32768, 65536, 131072, 262144, 524288, 1048576, 2*1048576, 4*1048576, 8*1048576 - }; - var selectedSizes = sizes.Where(x => x >= 16374 ).ToArray(); + var sizes = new[] + { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, 64, 66, 68, 70, 72, 74, 76, 78, 80, 82, 84, 86, 88, 90, 92, 94, 96, 103, 113, 122, 128, 135, + 145, + 154, 160, 167, 177, 186, 192, 199, 209, 218, 224, 231, 241, 250, 256, 263, 273, 282, 288, 295, 305, 314, 320, 327, 337, 346, 352, 359, 369, 378, 384, 391, 401, 410, 416, 423, 433, 442, 448, 455, 465, 474, 480, 487, 497, 506, 512, 519, 529, 538, 544, 551, 561, 570, 576, 583, + 593, 602, 608, 615, 625, 634, 640, 647, 657, 666, 672, 679, 689, 698, 704, 711, 721, 730, 736, 743, 753, 762, 768, 775, 785, 794, 800, 807, 817, 826, 832, 839, 849, 858, 864, 871, 881, 890, 896, 903, 913, 922, 928, 935, 945, 954, 960, 967, 977, 986, 1024, 1086, 1155, 1223, + 1280, 1342, 1411, 1479, 1536, 1598, 1667, 1735, 1792, 1854, 1923, 1991, 2048, 2110, 2179, 2247, 2304, 2366, 2435, 2503, 2560, 2622, 2691, 2759, 2816, 2878, 2947, 3015, 3072, 3134, 3203, 3271, 3328, 3390, 3459, 3527, 3584, 3646, 3715, 3783, 3840, 3902, 3971, 4039, 4096, 4158, + 4227, 4295, 4352, 4414, 4483, 4551, 4608, 4670, 4739, 4807, 4864, 4926, 4995, 5063, 5120, 5182, 5251, 5319, 5376, 5438, 5507, 5575, 5632, 5694, 5763, 5831, 5888, 5950, 6019, 6087, 6144, 6206, 6275, 6343, 6400, 6462, 6531, 6599, 6656, 6718, 6787, 6855, 6912, 6974, 7043, 7111, + 7168, 7230, 7299, 7367, 7424, 7486, 7555, 7623, 7680, 7742, 7811, 7879, 7936, 7998, 8067, 8135, 8192, 8254, 8323, 8391, 16384, 32768, 65536, 131072, 262144,262144+256,262144+512, 524288, 1048576, 2*1048576, 4*1048576, 8*1048576 + }; + var selectedSizes = sizes.Where(x => x >= 8400 && x < 1048576).ToArray(); GoogleChart.rows = new Row[selectedSizes.Count()]; var index = 0; foreach (var size in selectedSizes) { - double cycles = Tests.TestArray(0, 0, size)-loopOverhead; - double cycles1 = Tests.TestMsvcrtMemmove(3, 0, size) - loopOverhead; - double cycles2 = Tests.TestJames(0, 0, size)-loopOverhead; - double cycles3 = Tests.TestAnderman(3, 3, size) - loopOverhead; + var cycles0 = Tests.TestArray(0, size) - loopOverhead; + var cycles = Tests.TestArray(0, size) - loopOverhead; + var cycles1 = Tests.TestMsvcrtMemmove(0, size) - loopOverhead; + var cycles2 = Tests.TestJames(0, size) - loopOverhead; + var cycles3 = Tests.TestAnderman(0, size) - loopOverhead; GoogleChart.rows[index++] = new Row { c = new[] { - new C() {v = size}, - new C() {v = cycles}, - new C() {v = cycles1}, - new C() {v = cycles2}, - new C() {v = cycles3} + new C {v = size}, + new C {v = cycles}, + new C {v = cycles1}, + new C {v = cycles2}, + new C {v = cycles3} } }; //double cycles3 = TestCode(TestAnderman); - Console.WriteLine($"{size:0} {(cycles),8:0.00} {(cycles1),8:0.00} {cycles2,8:0.00} {(cycles3),8:0.00} "); + Console.WriteLine($"{size:0} {cycles,8:0.00} {cycles1,8:0.00} {cycles2,8:0.00} {cycles3,8:0.00} "); } Console.WriteLine("ready"); - System.IO.File.WriteAllText(@"chart.json", "chartData="+JsonConvert.SerializeObject(GoogleChart)); + File.WriteAllText(@"chart.json", "chartData=" + JsonConvert.SerializeObject(GoogleChart)); - Console.ReadKey(); Tests.Warmup(); } while (false); } - public static void MainOld() - { - Console.WriteLine("Test en compile functions"); - //OldTests.InitArray(Array.Copy); - //OldTests.InitArray(MsvcrtMemove.MsvcrtMemmove); - //OldTests.InitArray(Anderman2.VectorizedCopyAnderman); - //OldTests.InitArray(UnsafeCpblk.Copy); - - - Console.WriteLine($"bytes\titerations\tarray\tmsmemmove\tanderman\tUnsafeBufferMemmoveJamesqo2\tns\tns\tns\tns\tGB\tGB/s\tGB/s\tGB/s\tGB/s"); - - //foreach (var copyBytes in new[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, 64, 66, 68, 70, 72, 74, 76, 78, 80, 82, 84, 86, 88, 90, 92, 94, 96}) - //foreach (var copyBytes in new[] { 128,256,512,1024,2048,4096,8192,16384,32768,65536}) - //foreach (var copyBytes in new[] { 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16}) - foreach ( - var copyBytes in - new[] - { - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, 64, 66, 68, 70, 72, 74, 76, 78, 80, 82, 84, 86, 88, 90, 92, 94, 96, 103, 113, 122, 128, 135, - 145, - 154, 160, 167, 177, 186, 192, 199, 209, 218, 224, 231, 241, 250, 256, 263, 273, 282, 288, 295, 305, 314, 320, 327, 337, 346, 352, 359, 369, 378, 384, 391, 401, 410, 416, 423, 433, 442, 448, 455, 465, 474, 480, 487, 497, 506, 512, 519, 529, 538, 544, 551, 561, 570, 576, 583, - 593, 602, 608, 615, 625, 634, 640, 647, 657, 666, 672, 679, 689, 698, 704, 711, 721, 730, 736, 743, 753, 762, 768, 775, 785, 794, 800, 807, 817, 826, 832, 839, 849, 858, 864, 871, 881, 890, 896, 903, 913, 922, 928, 935, 945, 954, 960, 967, 977, 986, 1024, 1086, 1155, 1223, - 1280, 1342, 1411, 1479, 1536, 1598, 1667, 1735, 1792, 1854, 1923, 1991, 2048, 2110, 2179, 2247, 2304, 2366, 2435, 2503, 2560, 2622, 2691, 2759, 2816, 2878, 2947, 3015, 3072, 3134, 3203, 3271, 3328, 3390, 3459, 3527, 3584, 3646, 3715, 3783, 3840, 3902, 3971, 4039, 4096, 4158, - 4227, 4295, 4352, 4414, 4483, 4551, 4608, 4670, 4739, 4807, 4864, 4926, 4995, 5063, 5120, 5182, 5251, 5319, 5376, 5438, 5507, 5575, 5632, 5694, 5763, 5831, 5888, 5950, 6019, 6087, 6144, 6206, 6275, 6343, 6400, 6462, 6531, 6599, 6656, 6718, 6787, 6855, 6912, 6974, 7043, 7111, - 7168, 7230, 7299, 7367, 7424, 7486, 7555, 7623, 7680, 7742, 7811, 7879, 7936, 7998, 8067, 8135, 8192, 8254, 8323, 8391, 16384, 32768, 65536, 131072, 262144, 524288, 1048576, 2*1048576, 4*1048576, 8*1048576 - }.Where(x => x >= 8192)) - { - Test(copyBytes); - } - - } private static ulong GetCyclesPerSeond() { var sw = Stopwatch.StartNew(); @@ -135,33 +99,5 @@ private static ulong GetCyclesPerSeond() return endms - startms; } - - private static void Test(int copyBytes) - { - const double GB = 1024 * 1024 * 1024; - var iterations = StopwatchTests.GetMinIterations(copyBytes); - var s4 = StopwatchTests.TestUnsafeBufferMemmoveJamesqo2(copyBytes, iterations); - var s1 = StopwatchTests.TestArrayCopy(copyBytes, iterations); - var s2 = StopwatchTests.TestMsMemmove(copyBytes, iterations); - var s3 = StopwatchTests.TestVectorizedIftree(copyBytes, iterations); - - double baseline = s1.Elapsed.Ticks; - Console.Write($"{copyBytes:#######0}"); - Console.Write($"\t{iterations:##########0}"); - Console.Write($"\t{(double)s1.Elapsed.Ticks / baseline: 0.00}"); - Console.Write($"\t{(double)s2.Elapsed.Ticks / baseline: 0.00}"); - Console.Write($"\t{(double)s3.Elapsed.Ticks / baseline: 0.00}"); - Console.Write($"\t{(double)s4.Elapsed.Ticks / baseline: 0.00}"); - Console.Write($"\t{s1.Elapsed.TotalMilliseconds * 1000000 / iterations: 0.00}"); - Console.Write($"\t{s2.Elapsed.TotalMilliseconds * 1000000 / iterations: 0.00}"); - Console.Write($"\t{s3.Elapsed.TotalMilliseconds * 1000000 / iterations: 0.00}"); - Console.Write($"\t{s4.Elapsed.TotalMilliseconds * 1000000 / iterations: 0.00}"); - Console.Write($"\t{copyBytes * (double)iterations / GB: 0.00} "); - Console.Write($"\t{copyBytes * (double)iterations / GB / (s1.Elapsed.TotalMilliseconds / 1000): 0.00}"); - Console.Write($"\t{copyBytes * (double)iterations / GB / (s2.Elapsed.TotalMilliseconds / 1000): 0.00}"); - Console.Write($"\t{copyBytes * (double)iterations / GB / (s3.Elapsed.TotalMilliseconds / 1000): 0.00}"); - Console.Write($"\t{copyBytes * (double)iterations / GB / (s4.Elapsed.TotalMilliseconds / 1000): 0.00}"); - Console.WriteLine(); - } } } \ No newline at end of file diff --git a/src/DotNetCross.Memory.Copies.Benchmarks2/StopwatchTests.cs b/src/DotNetCross.Memory.Copies.Benchmarks2/StopwatchTests.cs deleted file mode 100644 index 9599723..0000000 --- a/src/DotNetCross.Memory.Copies.Benchmarks2/StopwatchTests.cs +++ /dev/null @@ -1,112 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace DotNetCross.Memory.Copies.Benchmarks2 -{ - public static class StopwatchTests - { - private const int BufferSize = 1024 * 1024 * 1024; - private static readonly byte[] _src = new byte[BufferSize]; - private static readonly byte[] _dst = new byte[BufferSize]; - private static int _copySize; - private const long _max = 30000000; - private const double TestTimeInMs = 1000.0; - - //Use a contant to change the test. Vars has huge impact on the performance - private const int Cached = 0; //Pseudo Random Test - private const int Randomizor = 0x13751; //Pseudo Random Test - private const int Alignment = 1; //Alignment test - private const int Sequence = -1; //read seq through array - private const int TestMode = Cached; //Alignment test - - public static long GetMinIterations(int copyBytes) - { - var iterations = _max / (copyBytes == 0 ? 1 : copyBytes); - var s0 = new Stopwatch(); - do - { - s0 = TestArrayCopy(copyBytes, iterations); - if (s0.ElapsedMilliseconds <= 100) - iterations *= 10; - } while (s0.ElapsedMilliseconds <= 100); - iterations = (long)(iterations * (TestTimeInMs / s0.ElapsedMilliseconds)); - - return iterations; - } - - public static Stopwatch TestVectorizedIftree(int copyBytes, long interations) - { - var sw = Stopwatch.StartNew(); - var offset = 0; - for (long i = 0; i < interations; i++) - { - offset += TestMode == -1 ? copyBytes : TestMode; - if (offset + copyBytes >= BufferSize) offset &= 0x3fff; - AndermanOptimized.Memmove(_src, offset, _dst, offset, copyBytes); - } - sw.Stop(); - return sw; - } - - public static Stopwatch TestArrayCopy(int copyBytes, long interations) - { - var sw = Stopwatch.StartNew(); - var offset = 0; - for (long i = 0; i < interations; i++) - { - offset += TestMode == -1 ? copyBytes : TestMode; - if (offset + copyBytes >= BufferSize) offset &= 0x3fff; - Array.Copy(_src, offset, _dst, offset, copyBytes); - } - sw.Stop(); - return sw; - } - - public static Stopwatch TestUnsafeBufferMemmoveJamesqo2(int copyBytes, long interations) - { - var sw = Stopwatch.StartNew(); - var offset = 0; - for (long i = 0; i < interations; i++) - { - offset += TestMode == -1 ? copyBytes : TestMode; - if (offset + copyBytes >= BufferSize) offset &= 0x3fff; - UnsafeBufferMemmoveJamesqo2.Memmove(_src, offset, _dst, offset, copyBytes); - } - sw.Stop(); - return sw; - } - - public static Stopwatch TestMsMemmove(int copyBytes, long interations) - { - var sw = Stopwatch.StartNew(); - var offset = 0; - for (long i = 0; i < interations; i++) - { - offset += TestMode == -1 ? copyBytes : TestMode; - if (offset + copyBytes >= BufferSize) offset &= 0x3fff; - MsvcrtMemove.Memmove(_src, offset, _dst, offset, copyBytes); - } - sw.Stop(); - return sw; - } - - private static Stopwatch TestUnsafeCpblk(int copyBytes, long interations) - { - var sw = Stopwatch.StartNew(); - var offset = 0; - for (long i = 0; i < interations; i++) - { - offset += TestMode == -1 ? copyBytes : TestMode; - if (offset + copyBytes >= BufferSize) offset &= 0x3fff; - UnsafeCpblk.Copy(_src, offset, _dst, offset, copyBytes); - } - sw.Stop(); - return sw; - } - - } -} diff --git a/src/DotNetCross.Memory.Copies.Benchmarks2/Tests.cs b/src/DotNetCross.Memory.Copies.Benchmarks2/Tests.cs index 6197c38..a2f52ba 100644 --- a/src/DotNetCross.Memory.Copies.Benchmarks2/Tests.cs +++ b/src/DotNetCross.Memory.Copies.Benchmarks2/Tests.cs @@ -1,26 +1,27 @@ using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace DotNetCross.Memory.Copies.Benchmarks2 { public static class Tests { - private const int BufferSize = 1024 * 1024 * 1024; + private const int BufferSize = 1024*1024*1024; private static readonly byte[] _src = new byte[BufferSize]; private static readonly byte[] _dst = new byte[BufferSize]; - const int MinIterations = 50; + private const int Cached = 0; //Pseudo Random Test + private const int Randomizor = +0x3313751; //Pseudo Random Test + private const int Alignment = 1; //Alignment test + private const int Sequence = -1; //read seq through array + private const int TestMode = Cached; //Alignment test + + private const int MinIterations = 50; public static ulong TestDuration = 1000000; public static unsafe int GetOffsetDst() { fixed (byte* pSrcOrigin = &_dst[0]) { - return 0x10 - (int)((ulong)pSrcOrigin & 0xF); + return 0x10 - (int) ((ulong) pSrcOrigin & 0xF); } } @@ -28,7 +29,7 @@ public static unsafe int GetOffsetSrc() { fixed (byte* pSrcOrigin = &_src[0]) { - return 0x10 - (int)((ulong)pSrcOrigin & 0xF); + return 0x10 - (int) ((ulong) pSrcOrigin & 0xF); } } @@ -36,7 +37,7 @@ public static void Warmup() { for (var i = 1; i < 256; i++) { - _src[i] = (byte)i; + _src[i] = (byte) i; _dst[i] = 0; } for (var size = 0; size < 32768; size++) @@ -51,13 +52,12 @@ public static void Warmup() } } - public static double TestOverhead() + public static double TestOverhead(int offset, int size) { var mincycles = ulong.MaxValue; var startTest = Rdtsc.TimestampP(); var testCycles = 0UL; - int dummy = 10; do { ulong cycles = 0; @@ -66,7 +66,8 @@ public static double TestOverhead() var start = Rdtsc.TimestampP(); for (var h = 1; h < MinIterations; h++) { - dummy = h; + offset += TestMode == -1 ? size : TestMode; + if (offset + size >= BufferSize) offset &= 0xFFfff; } var end = Rdtsc.TimestampP(); cycles += end - start; @@ -76,11 +77,10 @@ public static double TestOverhead() testCycles = Rdtsc.TimestampP() - startTest; } while (testCycles < TestDuration && testCycles > 0); - return dummy > 0 ? (mincycles / 1000.0) / (double)MinIterations : 0; + return offset >= 0 ? mincycles/1000.0/MinIterations : 0; } - - public static double TestAnderman(int srcOffset, int dstOffset, int size) + public static double TestAnderman(int offset, int size) { var mincycles = ulong.MaxValue; var startTest = Rdtsc.TimestampP(); @@ -91,7 +91,9 @@ public static double TestAnderman(int srcOffset, int dstOffset, int size) var start = Rdtsc.TimestampP(); for (var j = 1; j < MinIterations; j++) { - AndermanOptimized.Memmove(_src, srcOffset, _dst, dstOffset, size); + offset += TestMode == -1 ? size : TestMode; + if (offset + size >= BufferSize) offset &= 0xFFfff; + AndermanOptimized.Memmove(_src, offset, _dst, offset, size); } var end = Rdtsc.TimestampP(); var cycles = end - start; @@ -101,10 +103,10 @@ public static double TestAnderman(int srcOffset, int dstOffset, int size) } testCycles = Rdtsc.TimestampP() - startTest; } while (testCycles < TestDuration && testCycles > 0); - return (mincycles / (double)MinIterations); + return mincycles/(double) MinIterations; } - public static double TestJames(int srcOffset, int dstOffset, int size) + public static double TestJames(int offset, int size) { var mincycles = ulong.MaxValue; var startTest = Rdtsc.TimestampP(); @@ -115,7 +117,9 @@ public static double TestJames(int srcOffset, int dstOffset, int size) var start = Rdtsc.TimestampP(); for (var j = 1; j < MinIterations; j++) { - UnsafeBufferMemmoveJamesqo2.Memmove(_src, srcOffset, _dst, dstOffset, size); + offset += TestMode == -1 ? size : TestMode; + if (offset + size >= BufferSize) offset &= 0xFFfff; + UnsafeBufferMemmoveJamesqo2.Memmove(_src, offset, _dst, offset, size); } var end = Rdtsc.TimestampP(); var cycles = end - start; @@ -125,10 +129,10 @@ public static double TestJames(int srcOffset, int dstOffset, int size) } testCycles = Rdtsc.TimestampP() - startTest; } while (testCycles < TestDuration && testCycles > 0); - return (mincycles / (double)MinIterations); + return mincycles/(double) MinIterations; } - public static double TestMsvcrtMemmove(int srcOffset, int dstOffset, int size) + public static double TestMsvcrtMemmove(int offset, int size) { var mincycles = ulong.MaxValue; var startTest = Rdtsc.TimestampP(); @@ -139,7 +143,9 @@ public static double TestMsvcrtMemmove(int srcOffset, int dstOffset, int size) var start = Rdtsc.TimestampP(); for (var j = 1; j < MinIterations; j++) { - MsvcrtMemove.Memmove(_src, srcOffset, _dst, dstOffset, size); + offset += TestMode == -1 ? size : TestMode; + if (offset + size >= BufferSize) offset &= 0xFFfff; + MsvcrtMemove.Memmove(_src, offset, _dst, offset, size); } var end = Rdtsc.TimestampP(); var cycles = end - start; @@ -149,10 +155,10 @@ public static double TestMsvcrtMemmove(int srcOffset, int dstOffset, int size) } testCycles = Rdtsc.TimestampP() - startTest; } while (testCycles < TestDuration && testCycles > 0); - return (mincycles / (double)MinIterations); + return mincycles/(double) MinIterations; } - public static double TestArray(int srcOffset, int dstOffset, int size) + public static double TestArray(int offset, int size) { var mincycles = ulong.MaxValue; var startTest = Rdtsc.TimestampP(); @@ -163,7 +169,9 @@ public static double TestArray(int srcOffset, int dstOffset, int size) var start = Rdtsc.TimestampP(); for (var j = 1; j < MinIterations; j++) { - Array.Copy(_src, srcOffset, _dst, dstOffset, size); + offset += TestMode == -1 ? size : TestMode; + if (offset + size >= BufferSize) offset &= 0xFFfff; + Array.Copy(_src, offset, _dst, offset, size); } var end = Rdtsc.TimestampP(); var cycles = end - start; @@ -173,10 +181,10 @@ public static double TestArray(int srcOffset, int dstOffset, int size) } testCycles = Rdtsc.TimestampP() - startTest; } while (testCycles < TestDuration && testCycles > 0); - return (mincycles / (double)MinIterations); + return mincycles/(double) MinIterations; } - public static double TestMovSb(int srcOffset, int dstOffset, int size) + public static double TestMovSb(int offset, int size) { var mincycles = ulong.MaxValue; var startTest = Rdtsc.TimestampP(); @@ -187,7 +195,9 @@ public static double TestMovSb(int srcOffset, int dstOffset, int size) var start = Rdtsc.TimestampP(); for (var j = 1; j < MinIterations; j++) { - AndermanMovsb.Memmove(_src, srcOffset, _dst, dstOffset, size); + offset += TestMode == -1 ? size : TestMode; + if (offset + size >= BufferSize) offset &= 0xFFfff; + AndermanMovsb.Memmove(_src, offset, _dst, offset, size); } var end = Rdtsc.TimestampP(); var cycles = end - start; @@ -197,10 +207,10 @@ public static double TestMovSb(int srcOffset, int dstOffset, int size) } testCycles = Rdtsc.TimestampP() - startTest; } while (testCycles < TestDuration && testCycles > 0); - return (mincycles / (double)MinIterations); + return mincycles/(double) MinIterations; } - public static double TestDelegate(Func copyAction, int srcOffset, int dstOffset, int size) + public static double TestDelegate(Func copyAction, int offset, int size) { var mincycles = ulong.MaxValue; var startTest = Rdtsc.TimestampP(); @@ -208,7 +218,7 @@ public static double TestDelegate(Func copyAction, int src do { - var cycles = copyAction(srcOffset, dstOffset, size); + var cycles = copyAction(offset, offset, size); if (cycles <= mincycles) { mincycles = cycles; @@ -216,18 +226,20 @@ public static double TestDelegate(Func copyAction, int src testCycles = Rdtsc.TimestampP() - startTest; } while (testCycles < TestDuration && testCycles > 0); - return (mincycles / (double)MinIterations); + return mincycles/(double) MinIterations; } - public static ulong TestAndermanDelegate(int srcOffset, int dstOffset, int size) + public static ulong TestAndermanDelegate(int offset, int size) { var start = Rdtsc.TimestampP(); for (var j = 1; j < MinIterations; j++) { - AndermanOptimized.Memmove(_src, srcOffset, _dst, dstOffset, size); + offset += TestMode == -1 ? size : TestMode; + if (offset + size >= BufferSize) offset &= 0xFFfff; + AndermanOptimized.Memmove(_src, offset, _dst, offset, size); } var end = Rdtsc.TimestampP(); return end - start; } } -} +} \ No newline at end of file diff --git a/src/DotNetCross.Memory.Copies.Benchmarks2/chart.html b/src/DotNetCross.Memory.Copies.Benchmarks2/chart.html index 1447ad5..8b7d561 100644 --- a/src/DotNetCross.Memory.Copies.Benchmarks2/chart.html +++ b/src/DotNetCross.Memory.Copies.Benchmarks2/chart.html @@ -4,7 +4,6 @@ chart - diff --git a/src/DotNetCross.Memory.Copies.Benchmarks2/msvcrtMemove.cs b/src/DotNetCross.Memory.Copies.Benchmarks2/msvcrtMemove.cs index 8bf7543..03f5f38 100644 --- a/src/DotNetCross.Memory.Copies.Benchmarks2/msvcrtMemove.cs +++ b/src/DotNetCross.Memory.Copies.Benchmarks2/msvcrtMemove.cs @@ -8,12 +8,11 @@ public static class MsvcrtMemove { public static unsafe void Memmove(byte[] src, int srcOffset, byte[] dst, int dstOffset, int count) { - fixed (byte* srcOrigin = src) - fixed (byte* dstOrigin = dst) + fixed (byte* pSrcOrigin = &src[srcOffset]) + fixed (byte* pDstOrigin = &dst[dstOffset]) { - var pSrc = srcOrigin + srcOffset; - var pDst = dstOrigin + dstOffset; - + var pSrc = pSrcOrigin; + var pDst = pDstOrigin; memmove(pDst, pSrc, count); } } From 3c0bf811e215442283fdd4c8625922e76c0ebc99 Mon Sep 17 00:00:00 2001 From: Thom Kiesewetter Date: Wed, 24 Jan 2018 19:27:42 +0100 Subject: [PATCH 7/7] Cleanup --- DotNetCross.Memory.Copies.sln | 6 - .../{ => Memcopy}/AndermanMovsb.cs | 16 - .../{ => Memcopy}/AndermanOptimized.cs | 0 .../Program.cs | 40 +- .../PublishProfiles/Local-publish.ps1 | 19 + .../PublishProfiles/publish-module.psm1 | 1231 +++++++++++++++++ .../Rdtsc.cs | 5 - .../Tests.cs | 54 +- .../UnsafeBufferMemmoveJamesqo2.cs | 459 ------ .../chart.html | 2 +- .../msvcrtMemove.cs | 23 - .../project.json | 7 +- .../Anderman.cs | 0 .../App.config | 0 .../CopiesBenchmark.cs | 0 ...otNetCross.Memory.Copies.Benchmarks.csproj | 0 .../Illyriad.cs | 0 .../Msvcrt.cs | 0 .../Program.cs | 0 .../Properties/AssemblyInfo.cs | 0 .../UnsafeAnderman.cs | 0 .../UnsafeAnderman2.cs | 0 .../UnsafeBufferMemmoveJamesqo.cs | 0 .../UnsafeBufferMemmoveJamesqo2.cs | 0 .../UnsafeBufferMemmoveOriginal.cs | 0 .../UnsafeBufferMemmoveTannerGooding.cs | 0 .../UnsafeBufferMemmoveTannerGooding2.cs | 0 .../UnsafeBufferMemoryCopy.cs | 0 .../UnsafeCpblk.cs | 0 .../UnsafeIllyriad.cs | 0 .../UnsafeNietras.cs | 0 .../UnsafeNoChecksCopiesBenchmark.cs | 0 .../packages.config | 0 src/{ => old}/mov_repsb/AndermanMovsb.cs | 0 src/{ => old}/mov_repsb/Program.cs | 0 .../mov_repsb/Properties/AssemblyInfo.cs | 0 src/{ => old}/mov_repsb/mov_repsb.xproj | 0 src/{ => old}/mov_repsb/project.json | 0 38 files changed, 1275 insertions(+), 587 deletions(-) rename src/DotNetCross.Memory.Copies.Benchmarks2/{ => Memcopy}/AndermanMovsb.cs (85%) rename src/DotNetCross.Memory.Copies.Benchmarks2/{ => Memcopy}/AndermanOptimized.cs (100%) create mode 100644 src/DotNetCross.Memory.Copies.Benchmarks2/Properties/PublishProfiles/Local-publish.ps1 create mode 100644 src/DotNetCross.Memory.Copies.Benchmarks2/Properties/PublishProfiles/publish-module.psm1 delete mode 100644 src/DotNetCross.Memory.Copies.Benchmarks2/UnsafeBufferMemmoveJamesqo2.cs delete mode 100644 src/DotNetCross.Memory.Copies.Benchmarks2/msvcrtMemove.cs rename src/{ => old}/DotNetCross.Memory.Copies.Benchmarks/Anderman.cs (100%) rename src/{ => old}/DotNetCross.Memory.Copies.Benchmarks/App.config (100%) rename src/{ => old}/DotNetCross.Memory.Copies.Benchmarks/CopiesBenchmark.cs (100%) rename src/{ => old}/DotNetCross.Memory.Copies.Benchmarks/DotNetCross.Memory.Copies.Benchmarks.csproj (100%) rename src/{ => old}/DotNetCross.Memory.Copies.Benchmarks/Illyriad.cs (100%) rename src/{ => old}/DotNetCross.Memory.Copies.Benchmarks/Msvcrt.cs (100%) rename src/{ => old}/DotNetCross.Memory.Copies.Benchmarks/Program.cs (100%) rename src/{ => old}/DotNetCross.Memory.Copies.Benchmarks/Properties/AssemblyInfo.cs (100%) rename src/{ => old}/DotNetCross.Memory.Copies.Benchmarks/UnsafeAnderman.cs (100%) rename src/{ => old}/DotNetCross.Memory.Copies.Benchmarks/UnsafeAnderman2.cs (100%) rename src/{ => old}/DotNetCross.Memory.Copies.Benchmarks/UnsafeBufferMemmoveJamesqo.cs (100%) rename src/{ => old}/DotNetCross.Memory.Copies.Benchmarks/UnsafeBufferMemmoveJamesqo2.cs (100%) rename src/{ => old}/DotNetCross.Memory.Copies.Benchmarks/UnsafeBufferMemmoveOriginal.cs (100%) rename src/{ => old}/DotNetCross.Memory.Copies.Benchmarks/UnsafeBufferMemmoveTannerGooding.cs (100%) rename src/{ => old}/DotNetCross.Memory.Copies.Benchmarks/UnsafeBufferMemmoveTannerGooding2.cs (100%) rename src/{ => old}/DotNetCross.Memory.Copies.Benchmarks/UnsafeBufferMemoryCopy.cs (100%) rename src/{ => old}/DotNetCross.Memory.Copies.Benchmarks/UnsafeCpblk.cs (100%) rename src/{ => old}/DotNetCross.Memory.Copies.Benchmarks/UnsafeIllyriad.cs (100%) rename src/{ => old}/DotNetCross.Memory.Copies.Benchmarks/UnsafeNietras.cs (100%) rename src/{ => old}/DotNetCross.Memory.Copies.Benchmarks/UnsafeNoChecksCopiesBenchmark.cs (100%) rename src/{ => old}/DotNetCross.Memory.Copies.Benchmarks/packages.config (100%) rename src/{ => old}/mov_repsb/AndermanMovsb.cs (100%) rename src/{ => old}/mov_repsb/Program.cs (100%) rename src/{ => old}/mov_repsb/Properties/AssemblyInfo.cs (100%) rename src/{ => old}/mov_repsb/mov_repsb.xproj (100%) rename src/{ => old}/mov_repsb/project.json (100%) diff --git a/DotNetCross.Memory.Copies.sln b/DotNetCross.Memory.Copies.sln index 71680e3..b69def4 100644 --- a/DotNetCross.Memory.Copies.sln +++ b/DotNetCross.Memory.Copies.sln @@ -3,8 +3,6 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 14 VisualStudioVersion = 14.0.25420.1 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DotNetCross.Memory.Copies.Benchmarks", "src\DotNetCross.Memory.Copies.Benchmarks\DotNetCross.Memory.Copies.Benchmarks.csproj", "{DF33082A-C7BE-4305-BCAD-159D667C9420}" -EndProject Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "DotNetCross.Memory.Copies.Benchmarks2", "src\DotNetCross.Memory.Copies.Benchmarks2\DotNetCross.Memory.Copies.Benchmarks2.xproj", "{9396CFEF-F925-4202-98E7-1F0759DF0C5B}" EndProject Global @@ -13,10 +11,6 @@ Global Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {DF33082A-C7BE-4305-BCAD-159D667C9420}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {DF33082A-C7BE-4305-BCAD-159D667C9420}.Debug|Any CPU.Build.0 = Debug|Any CPU - {DF33082A-C7BE-4305-BCAD-159D667C9420}.Release|Any CPU.ActiveCfg = Release|Any CPU - {DF33082A-C7BE-4305-BCAD-159D667C9420}.Release|Any CPU.Build.0 = Release|Any CPU {9396CFEF-F925-4202-98E7-1F0759DF0C5B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {9396CFEF-F925-4202-98E7-1F0759DF0C5B}.Debug|Any CPU.Build.0 = Debug|Any CPU {9396CFEF-F925-4202-98E7-1F0759DF0C5B}.Release|Any CPU.ActiveCfg = Release|Any CPU diff --git a/src/DotNetCross.Memory.Copies.Benchmarks2/AndermanMovsb.cs b/src/DotNetCross.Memory.Copies.Benchmarks2/Memcopy/AndermanMovsb.cs similarity index 85% rename from src/DotNetCross.Memory.Copies.Benchmarks2/AndermanMovsb.cs rename to src/DotNetCross.Memory.Copies.Benchmarks2/Memcopy/AndermanMovsb.cs index 7cf690c..3e586e3 100644 --- a/src/DotNetCross.Memory.Copies.Benchmarks2/AndermanMovsb.cs +++ b/src/DotNetCross.Memory.Copies.Benchmarks2/Memcopy/AndermanMovsb.cs @@ -101,21 +101,5 @@ public static unsafe void Memmove(byte[] src, int srcOffset, byte[] dst, int dst var result1 = movsb((ulong)pSrc, (ulong)pDst,(ulong)count); } } - public static unsafe void Pinvoke(byte[] src, int srcOffset, byte[] dst, int dstOffset, int count) - { - if (count == 0) return; - if (src == null || dst == null) throw new ArgumentNullException(nameof(src)); - if (srcOffset + count > src.Length) throw new ArgumentException(nameof(src)); - if (count < 0 || srcOffset < 0 || dstOffset < 0) throw new ArgumentOutOfRangeException(nameof(count)); - if (dstOffset + count > dst.Length) throw new ArgumentException(nameof(dst)); - - fixed (byte* pSrcOrigin = &src[srcOffset]) - fixed (byte* pDstOrigin = &dst[dstOffset]) - { - var pSrc = pSrcOrigin; - var pDst = pDstOrigin; - var result1 = pinvoke((ulong)pSrc, (ulong)pDst, (ulong)count); - } - } } } \ No newline at end of file diff --git a/src/DotNetCross.Memory.Copies.Benchmarks2/AndermanOptimized.cs b/src/DotNetCross.Memory.Copies.Benchmarks2/Memcopy/AndermanOptimized.cs similarity index 100% rename from src/DotNetCross.Memory.Copies.Benchmarks2/AndermanOptimized.cs rename to src/DotNetCross.Memory.Copies.Benchmarks2/Memcopy/AndermanOptimized.cs diff --git a/src/DotNetCross.Memory.Copies.Benchmarks2/Program.cs b/src/DotNetCross.Memory.Copies.Benchmarks2/Program.cs index 845b517..cd2d667 100644 --- a/src/DotNetCross.Memory.Copies.Benchmarks2/Program.cs +++ b/src/DotNetCross.Memory.Copies.Benchmarks2/Program.cs @@ -11,38 +11,38 @@ public class Program private const ulong TestDuration = 100; //private static extern bool QueryThreadCycleTime(IntPtr hThread, out ulong cycles); //private static readonly IntPtr PseudoHandle = (IntPtr)(-2); - public static double loopOverhead; + public static double LoopOverhead; public static ulong CyclesPerSecond; - public static double nsPerCycle; + public static double NsPerCycle; public static void Main(string[] args) { Console.WriteLine($"Warmup..."); //Tests.Warmup(); CyclesPerSecond = GetCyclesPerSeond(); - nsPerCycle = 1000*1000*1000.0/CyclesPerSecond; + NsPerCycle = 1000*1000*1000.0/CyclesPerSecond; Tests.TestDuration = TestDuration*CyclesPerSecond/1000; - loopOverhead = Tests.TestOverhead(1000,1000); + LoopOverhead = Tests.TestOverhead(1000,1000); Console.WriteLine($"offset src: {Tests.GetOffsetSrc():X04}"); Console.WriteLine($"offset dst: {Tests.GetOffsetDst():X04} "); Console.WriteLine($"CyclesPerSecond: {CyclesPerSecond,5:0} "); - Console.WriteLine($"nsPerCycle: {nsPerCycle,5:0.0000} "); - Console.WriteLine($"loopOverhead: {loopOverhead,5:0.0000} Cycles"); - Console.WriteLine($" {loopOverhead*nsPerCycle,5:0.0000} ns "); + Console.WriteLine($"nsPerCycle: {NsPerCycle,5:0.0000} "); + Console.WriteLine($"loopOverhead: {LoopOverhead,5:0.0000} Cycles"); + Console.WriteLine($" {LoopOverhead*NsPerCycle,5:0.0000} ns "); Console.WriteLine($"Starting..."); do { - var GoogleChart = new GoogleChart + var googleChart = new GoogleChart { cols = new[] { new Col {label = "X", type = "number"}, new Col {label = "ArrayCopy", type = "number"}, new Col {label = "MsvcrtMemmove", type = "number"}, - new Col {label = "TestJames", type = "number"}, + new Col {label = "AndermanMovsb", type = "number"}, new Col {label = "Anderman", type = "number"} } }; @@ -56,33 +56,31 @@ public static void Main(string[] args) 4227, 4295, 4352, 4414, 4483, 4551, 4608, 4670, 4739, 4807, 4864, 4926, 4995, 5063, 5120, 5182, 5251, 5319, 5376, 5438, 5507, 5575, 5632, 5694, 5763, 5831, 5888, 5950, 6019, 6087, 6144, 6206, 6275, 6343, 6400, 6462, 6531, 6599, 6656, 6718, 6787, 6855, 6912, 6974, 7043, 7111, 7168, 7230, 7299, 7367, 7424, 7486, 7555, 7623, 7680, 7742, 7811, 7879, 7936, 7998, 8067, 8135, 8192, 8254, 8323, 8391, 16384, 32768, 65536, 131072, 262144,262144+256,262144+512, 524288, 1048576, 2*1048576, 4*1048576, 8*1048576 }; - var selectedSizes = sizes.Where(x => x >= 8400 && x < 1048576).ToArray(); - GoogleChart.rows = new Row[selectedSizes.Count()]; + var selectedSizes = sizes.Where(x => x >= 0 && x < 10).ToArray(); + googleChart.rows = new Row[selectedSizes.Count()]; var index = 0; foreach (var size in selectedSizes) { - var cycles0 = Tests.TestArray(0, size) - loopOverhead; - var cycles = Tests.TestArray(0, size) - loopOverhead; - var cycles1 = Tests.TestMsvcrtMemmove(0, size) - loopOverhead; - var cycles2 = Tests.TestJames(0, size) - loopOverhead; - var cycles3 = Tests.TestAnderman(0, size) - loopOverhead; - GoogleChart.rows[index++] = new Row + var cycles = Tests.TestArray(0, size) - LoopOverhead; + var cycles0 = Tests.TestArray(0, size) - LoopOverhead; + var cycles2 = Tests.TestMovSb(0, size) - LoopOverhead; + var cycles3 = Tests.TestAnderman(0, size) - LoopOverhead; + googleChart.rows[index++] = new Row { c = new[] { new C {v = size}, - new C {v = cycles}, - new C {v = cycles1}, + new C {v = cycles0}, new C {v = cycles2}, new C {v = cycles3} } }; //double cycles3 = TestCode(TestAnderman); - Console.WriteLine($"{size:0} {cycles,8:0.00} {cycles1,8:0.00} {cycles2,8:0.00} {cycles3,8:0.00} "); + Console.WriteLine($"{size:0} {cycles,8:0.00} {cycles2,8:0.00} {cycles3,8:0.00} "); } Console.WriteLine("ready"); - File.WriteAllText(@"chart.json", "chartData=" + JsonConvert.SerializeObject(GoogleChart)); + File.WriteAllText(@"chart.json", "chartData=" + JsonConvert.SerializeObject(googleChart)); Tests.Warmup(); } while (false); diff --git a/src/DotNetCross.Memory.Copies.Benchmarks2/Properties/PublishProfiles/Local-publish.ps1 b/src/DotNetCross.Memory.Copies.Benchmarks2/Properties/PublishProfiles/Local-publish.ps1 new file mode 100644 index 0000000..6d9ff92 --- /dev/null +++ b/src/DotNetCross.Memory.Copies.Benchmarks2/Properties/PublishProfiles/Local-publish.ps1 @@ -0,0 +1,19 @@ +[cmdletbinding(SupportsShouldProcess=$true)] +param($publishProperties=@{}, $packOutput, $pubProfilePath) + +# to learn more about this file visit https://go.microsoft.com/fwlink/?LinkId=524327 + +try{ + if ($publishProperties['ProjectGuid'] -eq $null){ + $publishProperties['ProjectGuid'] = '9396cfef-f925-4202-98e7-1f0759df0c5b' + } + + $publishModulePath = Join-Path (Split-Path $MyInvocation.MyCommand.Path) 'publish-module.psm1' + Import-Module $publishModulePath -DisableNameChecking -Force + + # call Publish-AspNet to perform the publish operation + Publish-AspNet -publishProperties $publishProperties -packOutput $packOutput -pubProfilePath $pubProfilePath +} +catch{ + "An error occurred during publish.`n{0}" -f $_.Exception.Message | Write-Error +} \ No newline at end of file diff --git a/src/DotNetCross.Memory.Copies.Benchmarks2/Properties/PublishProfiles/publish-module.psm1 b/src/DotNetCross.Memory.Copies.Benchmarks2/Properties/PublishProfiles/publish-module.psm1 new file mode 100644 index 0000000..adc6ada --- /dev/null +++ b/src/DotNetCross.Memory.Copies.Benchmarks2/Properties/PublishProfiles/publish-module.psm1 @@ -0,0 +1,1231 @@ +# WARNING: DO NOT MODIFY this file. Visual Studio will override it. +param() + +$script:AspNetPublishHandlers = @{} + +<# +These settings can be overridden with environment variables. +The name of the environment variable should use "Publish" as a +prefix and the names below. For example: + + $env:PublishMSDeployUseChecksum = $true +#> +$global:AspNetPublishSettings = New-Object -TypeName PSCustomObject @{ + MsdeployDefaultProperties = @{ + 'MSDeployUseChecksum'=$false + 'SkipExtraFilesOnServer'=$true + 'retryAttempts' = 20 + 'EnableMSDeployBackup' = $false + 'DeleteExistingFiles' = $false + 'AllowUntrustedCertificate'= $false + 'MSDeployPackageContentFoldername'='website\' + 'EnvironmentName' = 'Production' + 'AuthType'='Basic' + 'MSDeployPublishMethod'='WMSVC' + } +} + +function InternalOverrideSettingsFromEnv{ + [cmdletbinding()] + param( + [Parameter(Position=0)] + [object[]]$settings = ($global:AspNetPublishSettings,$global:AspNetPublishSettings.MsdeployDefaultProperties), + + [Parameter(Position=1)] + [string]$prefix = 'Publish' + ) + process{ + foreach($settingsObj in $settings){ + if($settingsObj -eq $null){ + continue + } + + $settingNames = $null + if($settingsObj -is [hashtable]){ + $settingNames = $settingsObj.Keys + } + else{ + $settingNames = ($settingsObj | Get-Member -MemberType NoteProperty | Select-Object -ExpandProperty Name) + + } + + foreach($name in @($settingNames)){ + $fullname = ('{0}{1}' -f $prefix,$name) + if(Test-Path "env:$fullname"){ + $settingsObj.$name = ((get-childitem "env:$fullname").Value) + } + } + } + } +} + +InternalOverrideSettingsFromEnv -prefix 'Publish' -settings $global:AspNetPublishSettings,$global:AspNetPublishSettings.MsdeployDefaultProperties + +function Register-AspnetPublishHandler{ + [cmdletbinding()] + param( + [Parameter(Mandatory=$true,Position=0)] + $name, + [Parameter(Mandatory=$true,Position=1)] + [ScriptBlock]$handler, + [switch]$force + ) + process{ + if(!($script:AspNetPublishHandlers[$name]) -or $force ){ + 'Adding handler for [{0}]' -f $name | Write-Verbose + $script:AspNetPublishHandlers[$name] = $handler + } + elseif(!($force)){ + 'Ignoring call to Register-AspnetPublishHandler for [name={0}], because a handler with that name exists and -force was not passed.' -f $name | Write-Verbose + } + } +} + +function Get-AspnetPublishHandler{ + [cmdletbinding()] + param( + [Parameter(Mandatory=$true,Position=0)] + $name + ) + process{ + $foundHandler = $script:AspNetPublishHandlers[$name] + + if(!$foundHandler){ + throw ('AspnetPublishHandler with name "{0}" was not found' -f $name) + } + + $foundHandler + } +} + +function GetInternal-ExcludeFilesArg{ + [cmdletbinding()] + param( + $publishProperties + ) + process{ + $excludeFiles = $publishProperties['ExcludeFiles'] + foreach($exclude in $excludeFiles){ + if($exclude){ + [string]$objName = $exclude['objectname'] + + if([string]::IsNullOrEmpty($objName)){ + $objName = 'filePath' + } + + $excludePath = $exclude['absolutepath'] + + # output the result to the return list + ('-skip:objectName={0},absolutePath=''{1}''' -f $objName, $excludePath) + } + } + } +} + +function GetInternal-ReplacementsMSDeployArgs{ + [cmdletbinding()] + param( + $publishProperties + ) + process{ + foreach($replace in ($publishProperties['Replacements'])){ + if($replace){ + $typeValue = $replace['type'] + if(!$typeValue){ $typeValue = 'TextFile' } + + $file = $replace['file'] + $match = $replace['match'] + $newValue = $replace['newValue'] + + if($file -and $match -and $newValue){ + $setParam = ('-setParam:type={0},scope={1},match={2},value={3}' -f $typeValue,$file, $match,$newValue) + 'Adding setparam [{0}]' -f $setParam | Write-Verbose + + # return it + $setParam + } + else{ + 'Skipping replacement because its missing a required value.[file="{0}",match="{1}",newValue="{2}"]' -f $file,$match,$newValue | Write-Verbose + } + } + } + } +} + +<# +.SYNOPSIS +Returns an array of msdeploy arguments that are used across different providers. +For example this will handle useChecksum, AppOffline etc. +This will also add default properties if they are missing. +#> +function GetInternal-SharedMSDeployParametersFrom{ + [cmdletbinding()] + param( + [Parameter(Mandatory=$true,Position=0)] + [HashTable]$publishProperties, + [Parameter(Mandatory=$true,Position=1)] + [System.IO.FileInfo]$packOutput + ) + process{ + $sharedArgs = New-Object psobject -Property @{ + ExtraArgs = @() + DestFragment = '' + EFMigrationData = @{} + } + + # add default properties if they are missing + foreach($propName in $global:AspNetPublishSettings.MsdeployDefaultProperties.Keys){ + if($publishProperties["$propName"] -eq $null){ + $defValue = $global:AspNetPublishSettings.MsdeployDefaultProperties["$propName"] + 'Adding default property to publishProperties ["{0}"="{1}"]' -f $propName,$defValue | Write-Verbose + $publishProperties["$propName"] = $defValue + } + } + + if($publishProperties['MSDeployUseChecksum'] -eq $true){ + $sharedArgs.ExtraArgs += '-usechecksum' + } + + if($publishProperties['EnableMSDeployAppOffline'] -eq $true){ + $sharedArgs.ExtraArgs += '-enablerule:AppOffline' + } + + if($publishProperties['WebPublishMethod'] -eq 'MSDeploy'){ + if($publishProperties['SkipExtraFilesOnServer'] -eq $true){ + $sharedArgs.ExtraArgs += '-enableRule:DoNotDeleteRule' + } + } + + if($publishProperties['WebPublishMethod'] -eq 'FileSystem'){ + if($publishProperties['DeleteExistingFiles'] -eq $false){ + $sharedArgs.ExtraArgs += '-enableRule:DoNotDeleteRule' + } + } + + if($publishProperties['retryAttempts']){ + $sharedArgs.ExtraArgs += ('-retryAttempts:{0}' -f ([int]$publishProperties['retryAttempts'])) + } + + if($publishProperties['EncryptWebConfig'] -eq $true){ + $sharedArgs.ExtraArgs += '-EnableRule:EncryptWebConfig' + } + + if($publishProperties['EnableMSDeployBackup'] -eq $false){ + $sharedArgs.ExtraArgs += '-disablerule:BackupRule' + } + + if($publishProperties['AllowUntrustedCertificate'] -eq $true){ + $sharedArgs.ExtraArgs += '-allowUntrusted' + } + + # add excludes + $sharedArgs.ExtraArgs += (GetInternal-ExcludeFilesArg -publishProperties $publishProperties) + # add replacements + $sharedArgs.ExtraArgs += (GetInternal-ReplacementsMSDeployArgs -publishProperties $publishProperties) + + # add EF Migration + if (($publishProperties['EfMigrations'] -ne $null) -and $publishProperties['EfMigrations'].Count -gt 0){ + if (!(Test-Path -Path $publishProperties['ProjectPath'])) { + throw 'ProjectPath property needs to be defined in the pubxml for EF migration.' + } + try { + # generate T-SQL files + $EFSqlFiles = GenerateInternal-EFMigrationScripts -projectPath $publishProperties['ProjectPath'] -packOutput $packOutput -EFMigrations $publishProperties['EfMigrations'] + $sharedArgs.EFMigrationData.Add('EFSqlFiles',$EFSqlFiles) + } + catch { + throw ('An error occurred while generating EF migrations. {0} {1}' -f $_.Exception,(Get-PSCallStack)) + } + } + # add connection string update + if (($publishProperties['DestinationConnectionStrings'] -ne $null) -and $publishProperties['DestinationConnectionStrings'].Count -gt 0) { + try { + # create/update appsettings.[environment].json + GenerateInternal-AppSettingsFile -packOutput $packOutput -environmentName $publishProperties['EnvironmentName'] -connectionStrings $publishProperties['DestinationConnectionStrings'] + } + catch { + throw ('An error occurred while generating the publish appsettings file. {0} {1}' -f $_.Exception,(Get-PSCallStack)) + } + } + + if(-not [string]::IsNullOrWhiteSpace($publishProperties['ProjectGuid'])) { + AddInternal-ProjectGuidToWebConfig -publishProperties $publishProperties -packOutput $packOutput + } + + # return the args + $sharedArgs + } +} + +<# +.SYNOPSIS +This will publish the folder based on the properties in $publishProperties + +.PARAMETER publishProperties +This is a hashtable containing the publish properties. See the examples here for more info on how to use this parameter. + +.PARAMETER packOutput +The folder path to the output of the dnu publish command. This folder contains the files +that will be published. + +.PARAMETER pubProfilePath +Path to a publish profile (.pubxml file) to import publish properties from. If the same property exists in +publishProperties and the publish profile then publishProperties will win. + +.EXAMPLE + Publish-AspNet -packOutput $packOutput -publishProperties @{ + 'WebPublishMethod'='MSDeploy' + 'MSDeployServiceURL'='contoso.scm.azurewebsites.net:443';` + 'DeployIisAppPath'='contoso';'Username'='$contoso';'Password'="$env:PublishPwd"} + +.EXAMPLE +Publish-AspNet -packOutput $packOutput -publishProperties @{ + 'WebPublishMethod'='FileSystem' + 'publishUrl'="$publishDest" + } + +.EXAMPLE +Publish-AspNet -packOutput $packOutput -publishProperties @{ + 'WebPublishMethod'='MSDeploy' + 'MSDeployServiceURL'='contoso.scm.azurewebsites.net:443';` +'DeployIisAppPath'='contoso';'Username'='$contoso';'Password'="$env:PublishPwd" + 'ExcludeFiles'=@( + @{'absolutepath'='test.txt'}, + @{'absolutepath'='references.js'} +)} + +.EXAMPLE +Publish-AspNet -packOutput $packOutput -publishProperties @{ + 'WebPublishMethod'='FileSystem' + 'publishUrl'="$publishDest" + 'ExcludeFiles'=@( + @{'absolutepath'='test.txt'}, + @{'absolutepath'='_references.js'}) + 'Replacements' = @( + @{'file'='test.txt$';'match'='REPLACEME';'newValue'='updatedValue'}) + } + +Publish-AspNet -packOutput $packOutput -publishProperties @{ + 'WebPublishMethod'='FileSystem' + 'publishUrl'="$publishDest" + 'ExcludeFiles'=@( + @{'absolutepath'='test.txt'}, + @{'absolutepath'='c:\\full\\path\\ok\\as\\well\\_references.js'}) + 'Replacements' = @( + @{'file'='test.txt$';'match'='REPLACEME';'newValue'='updatedValue'}) + } + +.EXAMPLE +Publish-AspNet -packOutput $packOutput -publishProperties @{ + 'WebPublishMethod'='FileSystem' + 'publishUrl'="$publishDest" + 'EnableMSDeployAppOffline'='true' + 'AppOfflineTemplate'='offline-template.html' + 'MSDeployUseChecksum'='true' +} +#> +function Publish-AspNet{ + param( + [Parameter(Position=0,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)] + [hashtable]$publishProperties = @{}, + + [Parameter(Mandatory = $true,Position=1,ValueFromPipelineByPropertyName=$true)] + [System.IO.FileInfo]$packOutput, + + [Parameter(Position=2,ValueFromPipelineByPropertyName=$true)] + [System.IO.FileInfo]$pubProfilePath + ) + process{ + if($publishProperties['WebPublishMethodOverride']){ + 'Overriding publish method from $publishProperties[''WebPublishMethodOverride''] to [{0}]' -f ($publishProperties['WebPublishMethodOverride']) | Write-Verbose + $publishProperties['WebPublishMethod'] = $publishProperties['WebPublishMethodOverride'] + } + + if(-not [string]::IsNullOrWhiteSpace($pubProfilePath)){ + $profileProperties = Get-PropertiesFromPublishProfile -filepath $pubProfilePath + foreach($key in $profileProperties.Keys){ + if(-not ($publishProperties.ContainsKey($key))){ + 'Adding properties from publish profile [''{0}''=''{1}'']' -f $key,$profileProperties[$key] | Write-Verbose + $publishProperties.Add($key,$profileProperties[$key]) + } + } + } + + if(!([System.IO.Path]::IsPathRooted($packOutput))){ + $packOutput = [System.IO.Path]::GetFullPath((Join-Path $pwd $packOutput)) + } + + $pubMethod = $publishProperties['WebPublishMethod'] + 'Publishing with publish method [{0}]' -f $pubMethod | Write-Output + + # get the handler based on WebPublishMethod, and call it. + &(Get-AspnetPublishHandler -name $pubMethod) $publishProperties $packOutput + } +} + +<# +.SYNOPSIS + +Inputs: + +Example of $xmlDocument: '' +Example of $providerDataArray: + + [System.Collections.ArrayList]$providerDataArray = @() + + $iisAppSourceKeyValue=@{"iisApp" = @{"path"='c:\temp\pathtofiles';"appOfflineTemplate" ='offline-template.html'}} + $providerDataArray.Add($iisAppSourceKeyValue) + + $dbfullsqlKeyValue=@{"dbfullsql" = @{"path"="c:\Temp\PathToSqlFile"}} + $providerDataArray.Add($dbfullsqlKeyValue) + + $dbfullsqlKeyValue=@{"dbfullsql" = @{"path"="c:\Temp\PathToSqlFile2"}} + $providerDataArray.Add($dbfullsqlKeyValue) + + Manifest File content: + + + + + + +#> +function AddInternal-ProviderDataToManifest { + [cmdletbinding()] + param( + [Parameter(Mandatory=$true, Position=0)] + [XML]$xmlDocument, + [Parameter(Position=1)] + [System.Collections.ArrayList]$providerDataArray + ) + process { + $siteNode = $xmlDocument.SelectSingleNode("/sitemanifest") + if ($siteNode -eq $null) { + throw 'sitemanifest element is missing in the xml object' + } + foreach ($providerData in $providerDataArray) { + foreach ($providerName in $providerData.Keys) { + $providerValue = $providerData[$providerName] + $xmlNode = $xmlDocument.CreateElement($providerName) + foreach ($providerValueKey in $providerValue.Keys) { + $xmlNode.SetAttribute($providerValueKey, $providerValue[$providerValueKey]) | Out-Null + } + $siteNode.AppendChild($xmlNode) | Out-Null + } + } + } +} + +function AddInternal-ProjectGuidToWebConfig { + [cmdletbinding()] + param( + [Parameter(Position=0)] + [HashTable]$publishProperties, + [Parameter(Position=1)] + [System.IO.FileInfo]$packOutput + ) + process { + try { + [Reflection.Assembly]::LoadWithPartialName("System.Xml.Linq") | Out-Null + $webConfigPath = Join-Path $packOutput 'web.config' + $projectGuidCommentValue = 'ProjectGuid: {0}' -f $publishProperties['ProjectGuid'] + $xDoc = [System.Xml.Linq.XDocument]::Load($webConfigPath) + $allNodes = $xDoc.DescendantNodes() + $projectGuidComment = $allNodes | Where-Object { $_.NodeType -eq [System.Xml.XmlNodeType]::Comment -and $_.Value -eq $projectGuidCommentValue } | Select -First 1 + if($projectGuidComment -ne $null) { + if($publishProperties['IgnoreProjectGuid'] -eq $true) { + $projectGuidComment.Remove() | Out-Null + $xDoc.Save($webConfigPath) | Out-Null + } + } + else { + if(-not ($publishProperties['IgnoreProjectGuid'] -eq $true)) { + $projectGuidComment = New-Object -TypeName System.Xml.Linq.XComment -ArgumentList $projectGuidCommentValue + $xDoc.LastNode.AddAfterSelf($projectGuidComment) | Out-Null + $xDoc.Save($webConfigPath) | Out-Null + } + } + } + catch { + } + } +} + +<# +.SYNOPSIS + +Example of $EFMigrations: + $EFMigrations = @{'CarContext'='Car Context ConnectionString';'MovieContext'='Movie Context Connection String'} + +#> + +function GenerateInternal-EFMigrationScripts { + [cmdletbinding()] + param( + [Parameter(Mandatory=$true,Position=0)] + [System.IO.FileInfo]$projectPath, + [Parameter(Mandatory=$true,Position=1)] + [System.IO.FileInfo]$packOutput, + [Parameter(Position=2)] + [HashTable]$EFMigrations + ) + process { + $files = @{} + $dotnetExePath = GetInternal-DotNetExePath + foreach ($dbContextName in $EFMigrations.Keys) { + try + { + $tempDir = GetInternal-PublishTempPath -packOutput $packOutput + $efScriptFile = Join-Path $tempDir ('{0}.sql' -f $dbContextName) + $arg = ('ef migrations script --idempotent --output {0} --context {1}' -f + $efScriptFile, + $dbContextName) + + Execute-Command $dotnetExePath $arg $projectPath | Out-Null + if (Test-Path -Path $efScriptFile) { + if (!($files.ContainsKey($dbContextName))) { + $files.Add($dbContextName, $efScriptFile) | Out-Null + } + } + } + catch + { + throw 'error occured when executing dotnet.exe to generate EF T-SQL file' + } + } + # return files object + $files + } +} + +<# +.SYNOPSIS + +Example of $connectionStrings: + $connectionStrings = @{'DefaultConnection'='Default ConnectionString';'CarConnection'='Car Connection String'} + +#> +function GenerateInternal-AppSettingsFile { + [cmdletbinding()] + param( + [Parameter(Mandatory = $true,Position=0)] + [System.IO.FileInfo]$packOutput, + [Parameter(Mandatory = $true,Position=1)] + [string]$environmentName, + [Parameter(Position=2)] + [HashTable]$connectionStrings + ) + process { + $configProdJsonFile = 'appsettings.{0}.json' -f $environmentName + $configProdJsonFilePath = Join-Path -Path $packOutput -ChildPath $configProdJsonFile + + if ([string]::IsNullOrEmpty($configProdJsonFilePath)) { + throw ('The path of {0} is empty' -f $configProdJsonFilePath) + } + + if(!(Test-Path -Path $configProdJsonFilePath)) { + # create new file + '{}' | out-file -encoding utf8 -filePath $configProdJsonFilePath -Force + } + + $jsonObj = ConvertFrom-Json -InputObject (Get-Content -Path $configProdJsonFilePath -Raw) + # update when there exists one or more connection strings + if ($connectionStrings -ne $null) { + foreach ($name in $connectionStrings.Keys) { + #check for hierarchy style + if ($jsonObj.ConnectionStrings.$name) { + $jsonObj.ConnectionStrings.$name = $connectionStrings[$name] + continue + } + #check for horizontal style + $horizontalName = 'ConnectionStrings.{0}:' -f $name + if ($jsonObj.$horizontalName) { + $jsonObj.$horizontalName = $connectionStrings[$name] + continue + } + # create new one + if (!($jsonObj.ConnectionStrings)) { + $contentForDefaultConnection = '{}' + $jsonObj | Add-Member -name 'ConnectionStrings' -value (ConvertFrom-Json -InputObject $contentForDefaultConnection) -MemberType NoteProperty | Out-Null + } + if (!($jsonObj.ConnectionStrings.$name)) { + $jsonObj.ConnectionStrings | Add-Member -name $name -value $connectionStrings[$name] -MemberType NoteProperty | Out-Null + } + } + } + + $jsonObj | ConvertTo-Json | out-file -encoding utf8 -filePath $configProdJsonFilePath -Force + + #return the path of config.[environment].json + $configProdJsonFilePath + } +} + +<# +.SYNOPSIS + +Inputs: +Example of $providerDataArray: + + [System.Collections.ArrayList]$providerDataArray = @() + + $iisAppSourceKeyValue=@{"iisApp" = @{"path"='c:\temp\pathtofiles';"appOfflineTemplate" ='offline-template.html'}} + $providerDataArray.Add($iisAppSourceKeyValue) + + $dbfullsqlKeyValue=@{"dbfullsql" = @{"path"="c:\Temp\PathToSqlFile"}} + $providerDataArray.Add($dbfullsqlKeyValue) + + $dbfullsqlKeyValue=@{"dbfullsql" = @{"path"="c:\Temp\PathToSqlFile2"}} + $providerDataArray.Add($dbfullsqlKeyValue) + + Manifest File content: + + + + + + + +#> + +function GenerateInternal-ManifestFile { + [cmdletbinding()] + param( + [Parameter(Mandatory=$true,Position=0)] + [System.IO.FileInfo]$packOutput, + [Parameter(Mandatory=$true,Position=1)] + $publishProperties, + [Parameter(Mandatory=$true,Position=2)] + [System.Collections.ArrayList]$providerDataArray, + [Parameter(Mandatory=$true,Position=3)] + [ValidateNotNull()] + $manifestFileName + ) + process{ + $xmlDocument = [xml]'' + AddInternal-ProviderDataToManifest -xmlDocument $xmlDocument -providerDataArray $providerDataArray | Out-Null + $publishTempDir = GetInternal-PublishTempPath -packOutput $packOutput + $XMLFile = Join-Path $publishTempDir $manifestFileName + $xmlDocument.OuterXml | out-file -encoding utf8 -filePath $XMLFile -Force + + # return + [System.IO.FileInfo]$XMLFile + } +} + +function GetInternal-PublishTempPath { + [cmdletbinding()] + param( + [Parameter(Mandatory=$true, Position=0)] + [System.IO.FileInfo]$packOutput + ) + process { + $tempDir = [io.path]::GetTempPath() + $packOutputFolderName = Split-Path $packOutput -Leaf + $publishTempDir = [io.path]::combine($tempDir,'PublishTemp','obj',$packOutputFolderName) + if (!(Test-Path -Path $publishTempDir)) { + New-Item -Path $publishTempDir -type directory | Out-Null + } + # return + [System.IO.FileInfo]$publishTempDir + } +} + +function Publish-AspNetMSDeploy{ + param( + [Parameter(Mandatory = $true,Position=0,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)] + $publishProperties, + [Parameter(Mandatory = $true,Position=1,ValueFromPipelineByPropertyName=$true)] + $packOutput + ) + process{ + if($publishProperties){ + $publishPwd = $publishProperties['Password'] + + $sharedArgs = GetInternal-SharedMSDeployParametersFrom -publishProperties $publishProperties -packOutput $packOutput + $iisAppPath = $publishProperties['DeployIisAppPath'] + + # create source manifest + + # e.g + # + # + # + # + # + # + + [System.Collections.ArrayList]$providerDataArray = @() + $iisAppValues = @{"path"=$packOutput}; + $iisAppSourceKeyValue=@{"iisApp" = $iisAppValues} + $providerDataArray.Add($iisAppSourceKeyValue) | Out-Null + + if ($sharedArgs.EFMigrationData -ne $null -and $sharedArgs.EFMigrationData.Contains('EFSqlFiles')) { + foreach ($sqlFile in $sharedArgs.EFMigrationData['EFSqlFiles'].Values) { + $dbFullSqlSourceKeyValue=@{"dbFullSql" = @{"path"=$sqlFile}} + $providerDataArray.Add($dbFullSqlSourceKeyValue) | Out-Null + } + } + + [System.IO.FileInfo]$sourceXMLFile = GenerateInternal-ManifestFile -packOutput $packOutput -publishProperties $publishProperties -providerDataArray $providerDataArray -manifestFileName 'SourceManifest.xml' + + $providerDataArray.Clear() | Out-Null + # create destination manifest + + # e.g + # + # + # + # + # + + $iisAppValues = @{"path"=$iisAppPath}; + if(-not [string]::IsNullOrWhiteSpace($publishProperties['AppOfflineTemplate'])){ + $iisAppValues.Add("appOfflineTemplate", $publishProperties['AppOfflineTemplate']) | Out-Null + } + + $iisAppDestinationKeyValue=@{"iisApp" = $iisAppValues} + $providerDataArray.Add($iisAppDestinationKeyValue) | Out-Null + + if ($publishProperties['EfMigrations'] -ne $null -and $publishProperties['EfMigrations'].Count -gt 0) { + foreach ($connectionString in $publishProperties['EfMigrations'].Values) { + $dbFullSqlDestinationKeyValue=@{"dbFullSql" = @{"path"=$connectionString}} + $providerDataArray.Add($dbFullSqlDestinationKeyValue) | Out-Null + } + } + + + [System.IO.FileInfo]$destXMLFile = GenerateInternal-ManifestFile -packOutput $packOutput -publishProperties $publishProperties -providerDataArray $providerDataArray -manifestFileName 'DestinationManifest.xml' + + <# + "C:\Program Files (x86)\IIS\Microsoft Web Deploy V3\msdeploy.exe" + -source:manifest='C:\Users\testuser\AppData\Local\Temp\PublishTemp\obj\SourceManifest.xml' + -dest:manifest='C:\Users\testuser\AppData\Local\Temp\PublishTemp\obj\DestManifest.xml',ComputerName='https://contoso.scm.azurewebsites.net/msdeploy.axd',UserName='$contoso',Password='',IncludeAcls='False',AuthType='Basic' + -verb:sync + -enableRule:DoNotDeleteRule + -retryAttempts=2" + #> + + if(-not [string]::IsNullOrWhiteSpace($publishProperties['MSDeployPublishMethod'])){ + $serviceMethod = $publishProperties['MSDeployPublishMethod'] + } + + $msdeployComputerName= InternalNormalize-MSDeployUrl -serviceUrl $publishProperties['MSDeployServiceURL'] -siteName $iisAppPath -serviceMethod $publishProperties['MSDeployPublishMethod'] + if($publishProperties['UseMSDeployServiceURLAsIs'] -eq $true){ + $msdeployComputerName = $publishProperties['MSDeployServiceURL'] + } + + $publishArgs = @() + #use manifest to publish + $publishArgs += ('-source:manifest=''{0}''' -f $sourceXMLFile.FullName) + $publishArgs += ('-dest:manifest=''{0}'',ComputerName=''{1}'',UserName=''{2}'',Password=''{3}'',IncludeAcls=''False'',AuthType=''{4}''{5}' -f + $destXMLFile.FullName, + $msdeployComputerName, + $publishProperties['UserName'], + $publishPwd, + $publishProperties['AuthType'], + $sharedArgs.DestFragment) + $publishArgs += '-verb:sync' + $publishArgs += $sharedArgs.ExtraArgs + + $command = '"{0}" {1}' -f (Get-MSDeploy),($publishArgs -join ' ') + + if (! [String]::IsNullOrEmpty($publishPwd)) { + $command.Replace($publishPwd,'{PASSWORD-REMOVED-FROM-LOG}') | Print-CommandString + } + Execute-Command -exePath (Get-MSDeploy) -arguments ($publishArgs -join ' ') + } + else{ + throw 'publishProperties is empty, cannot publish' + } + } +} + +function Escape-TextForRegularExpressions{ + [cmdletbinding()] + param( + [Parameter(Position=0,Mandatory=$true)] + [string]$text + ) + process{ + [regex]::Escape($text) + } +} + +function Publish-AspNetMSDeployPackage{ + param( + [Parameter(Mandatory = $true,Position=0,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)] + $publishProperties, + [Parameter(Mandatory = $true,Position=1,ValueFromPipelineByPropertyName=$true)] + $packOutput + ) + process{ + if($publishProperties){ + $packageDestinationFilepath = $publishProperties['DesktopBuildPackageLocation'] + + if(!$packageDestinationFilepath){ + throw ('The package destination property (DesktopBuildPackageLocation) was not found in the publish properties') + } + + if(!([System.IO.Path]::IsPathRooted($packageDestinationFilepath))){ + $packageDestinationFilepath = [System.IO.Path]::GetFullPath((Join-Path $pwd $packageDestinationFilepath)) + } + + # if the dir doesn't exist create it + $pkgDir = ((new-object -typename System.IO.FileInfo($packageDestinationFilepath)).Directory) + if(!(Test-Path -Path $pkgDir)) { + New-Item $pkgDir -type Directory | Out-Null + } + + <# + "C:\Program Files (x86)\IIS\Microsoft Web Deploy V3\msdeploy.exe" + -source:manifest='C:\Users\testuser\AppData\Local\Temp\PublishTemp\obj\SourceManifest.xml' + -dest:package=c:\temp\path\contosoweb.zip + -verb:sync + -enableRule:DoNotDeleteRule + -retryAttempts=2 + #> + + $sharedArgs = GetInternal-SharedMSDeployParametersFrom -publishProperties $publishProperties -packOutput $packOutput + + # create source manifest + + # e.g + # + # + # + # + + [System.Collections.ArrayList]$providerDataArray = @() + $iisAppSourceKeyValue=@{"iisApp" = @{"path"=$packOutput}} + $providerDataArray.Add($iisAppSourceKeyValue) | Out-Null + + [System.IO.FileInfo]$sourceXMLFile = GenerateInternal-ManifestFile -packOutput $packOutput -publishProperties $publishProperties -providerDataArray $providerDataArray -manifestFileName 'SourceManifest.xml' + + $publishArgs = @() + $publishArgs += ('-source:manifest=''{0}''' -f $sourceXMLFile.FullName) + $publishArgs += ('-dest:package=''{0}''' -f $packageDestinationFilepath) + $publishArgs += '-verb:sync' + $packageContentFolder = $publishProperties['MSDeployPackageContentFoldername'] + if(!$packageContentFolder){ $packageContentFolder = 'website' } + $publishArgs += ('-replace:match=''{0}'',replace=''{1}''' -f (Escape-TextForRegularExpressions $packOutput), $packageContentFolder ) + $publishArgs += $sharedArgs.ExtraArgs + + $command = '"{0}" {1}' -f (Get-MSDeploy),($publishArgs -join ' ') + $command | Print-CommandString + Execute-Command -exePath (Get-MSDeploy) -arguments ($publishArgs -join ' ') + } + else{ + throw 'publishProperties is empty, cannot publish' + } + } +} + +function Publish-AspNetFileSystem{ + param( + [Parameter(Mandatory = $true,Position=0,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)] + $publishProperties, + [Parameter(Mandatory = $true,Position=1,ValueFromPipelineByPropertyName=$true)] + $packOutput + ) + process{ + $pubOut = $publishProperties['publishUrl'] + + if([string]::IsNullOrWhiteSpace($pubOut)){ + throw ('publishUrl is a required property for FileSystem publish but it was empty.') + } + + # if it's a relative path then update it to a full path + if(!([System.IO.Path]::IsPathRooted($pubOut))){ + $pubOut = [System.IO.Path]::GetFullPath((Join-Path $pwd $pubOut)) + $publishProperties['publishUrl'] = "$pubOut" + } + + 'Publishing files to {0}' -f $pubOut | Write-Output + + # we use msdeploy.exe because it supports incremental publish/skips/replacements/etc + # msdeploy.exe -verb:sync -source:manifest='C:\Users\testuser\AppData\Local\Temp\PublishTemp\obj\SourceManifest.xml' -dest:manifest='C:\Users\testuser\AppData\Local\Temp\PublishTemp\obj\DestManifest.xml' + + $sharedArgs = GetInternal-SharedMSDeployParametersFrom -publishProperties $publishProperties -packOutput $packOutput + + # create source manifest + + # e.g + # + # + # + # + + [System.Collections.ArrayList]$providerDataArray = @() + $contentPathValues = @{"path"=$packOutput}; + $contentPathSourceKeyValue=@{"contentPath" = $contentPathValues} + $providerDataArray.Add($contentPathSourceKeyValue) | Out-Null + + [System.IO.FileInfo]$sourceXMLFile = GenerateInternal-ManifestFile -packOutput $packOutput -publishProperties $publishProperties -providerDataArray $providerDataArray -manifestFileName 'SourceManifest.xml' + + $providerDataArray.Clear() | Out-Null + # create destination manifest + + # e.g + # + # + # + $contentPathValues = @{"path"=$publishProperties['publishUrl']}; + if(-not [string]::IsNullOrWhiteSpace($publishProperties['AppOfflineTemplate'])){ + $contentPathValues.Add("appOfflineTemplate", $publishProperties['AppOfflineTemplate']) | Out-Null + } + $contentPathDestinationKeyValue=@{"contentPath" = $contentPathValues} + $providerDataArray.Add($contentPathDestinationKeyValue) | Out-Null + + [System.IO.FileInfo]$destXMLFile = GenerateInternal-ManifestFile -packOutput $packOutput -publishProperties $publishProperties -providerDataArray $providerDataArray -manifestFileName 'DestinationManifest.xml' + + $publishArgs = @() + $publishArgs += ('-source:manifest=''{0}''' -f $sourceXMLFile.FullName) + $publishArgs += ('-dest:manifest=''{0}''{1}' -f $destXMLFile.FullName, $sharedArgs.DestFragment) + $publishArgs += '-verb:sync' + $publishArgs += $sharedArgs.ExtraArgs + + $command = '"{0}" {1}' -f (Get-MSDeploy),($publishArgs -join ' ') + $command | Print-CommandString + Execute-Command -exePath (Get-MSDeploy) -arguments ($publishArgs -join ' ') + + # copy sql script to script folder + if (($sharedArgs.EFMigrationData['EFSqlFiles'] -ne $null) -and ($sharedArgs.EFMigrationData['EFSqlFiles'].Count -gt 0)) { + $scriptsDir = Join-Path $pubOut 'efscripts' + + if (!(Test-Path -Path $scriptsDir)) { + New-Item -Path $scriptsDir -type directory | Out-Null + } + + foreach ($sqlFile in $sharedArgs.EFMigrationData['EFSqlFiles'].Values) { + Copy-Item $sqlFile -Destination $scriptsDir -Force -Recurse | Out-Null + } + } + } +} + +<# +.SYNOPSIS + This can be used to read a publish profile to extract the property values into a hashtable. + +.PARAMETER filepath + Path to the publish profile to get the properties from. Currenlty this only supports reading + .pubxml files. + +.EXAMPLE + Get-PropertiesFromPublishProfile -filepath c:\projects\publish\devpublish.pubxml +#> +function Get-PropertiesFromPublishProfile{ + [cmdletbinding()] + param( + [Parameter(Position=0,Mandatory=$true)] + [ValidateNotNull()] + [ValidateScript({Test-Path $_})] + [System.IO.FileInfo]$filepath + ) + begin{ + Add-Type -AssemblyName System.Core + Add-Type -AssemblyName Microsoft.Build + } + process{ + 'Reading publish properties from profile [{0}]' -f $filepath | Write-Verbose + # use MSBuild to get the project and read properties + $projectCollection = (New-Object Microsoft.Build.Evaluation.ProjectCollection) + if(!([System.IO.Path]::IsPathRooted($filepath))){ + $filepath = [System.IO.Path]::GetFullPath((Join-Path $pwd $filepath)) + } + $project = ([Microsoft.Build.Construction.ProjectRootElement]::Open([string]$filepath.Fullname, $projectCollection)) + + $properties = @{} + foreach($property in $project.Properties){ + $properties[$property.Name]=$property.Value + } + + $properties + } +} + +function Print-CommandString{ + [cmdletbinding()] + param( + [Parameter(Mandatory=$true,Position=0,ValueFromPipeline=$true)] + $command + ) + process{ + 'Executing command [{0}]' -f $command | Write-Output + } +} + +function Execute-CommandString{ + [cmdletbinding()] + param( + [Parameter(Mandatory=$true,Position=0,ValueFromPipeline=$true)] + [string[]]$command, + + [switch] + $useInvokeExpression, + + [switch] + $ignoreErrors + ) + process{ + foreach($cmdToExec in $command){ + 'Executing command [{0}]' -f $cmdToExec | Write-Verbose + if($useInvokeExpression){ + try { + Invoke-Expression -Command $cmdToExec + } + catch { + if(-not $ignoreErrors){ + $msg = ('The command [{0}] exited with exception [{1}]' -f $cmdToExec, $_.ToString()) + throw $msg + } + } + } + else { + cmd.exe /D /C $cmdToExec + + if(-not $ignoreErrors -and ($LASTEXITCODE -ne 0)){ + $msg = ('The command [{0}] exited with code [{1}]' -f $cmdToExec, $LASTEXITCODE) + throw $msg + } + } + } + } +} + +function Execute-Command { + [cmdletbinding()] + param( + [Parameter(Mandatory = $true,Position=0,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)] + [String]$exePath, + [Parameter(Mandatory = $true,Position=1,ValueFromPipelineByPropertyName=$true)] + [String]$arguments, + [Parameter(Position=2)] + [System.IO.FileInfo]$workingDirectory + ) + process{ + $psi = New-Object -TypeName System.Diagnostics.ProcessStartInfo + $psi.CreateNoWindow = $true + $psi.UseShellExecute = $false + $psi.RedirectStandardOutput = $true + $psi.RedirectStandardError=$true + $psi.FileName = $exePath + $psi.Arguments = $arguments + if($workingDirectory -and (Test-Path -Path $workingDirectory)) { + $psi.WorkingDirectory = $workingDirectory + } + + $process = New-Object -TypeName System.Diagnostics.Process + $process.StartInfo = $psi + $process.EnableRaisingEvents=$true + + # Register the event handler for error + $stdErrEvent = Register-ObjectEvent -InputObject $process -EventName 'ErrorDataReceived' -Action { + if (! [String]::IsNullOrEmpty($EventArgs.Data)) { + $EventArgs.Data | Write-Error + } + } + + # Starting process. + $process.Start() | Out-Null + $process.BeginErrorReadLine() | Out-Null + $output = $process.StandardOutput.ReadToEnd() + $process.WaitForExit() | Out-Null + $output | Write-Output + + # UnRegister the event handler for error + Unregister-Event -SourceIdentifier $stdErrEvent.Name | Out-Null + } +} + + +function GetInternal-DotNetExePath { + process { + $dotnetinstallpath = $env:dotnetinstallpath + if (!$dotnetinstallpath) { + $DotNetRegItem = Get-ItemProperty -Path 'hklm:\software\dotnet\setup\' + if ($env:DOTNET_HOME) { + $dotnetinstallpath = Join-Path $env:DOTNET_HOME -ChildPath 'dotnet.exe' + } + elseif ($DotNetRegItem -and $DotNetRegItem.InstallDir){ + $dotnetinstallpath = Join-Path $DotNetRegItem.InstallDir -ChildPath 'dotnet.exe' + } + } + if (!(Test-Path $dotnetinstallpath)) { + throw 'Unable to find dotnet.exe, please install it and try again' + } + # return + [System.IO.FileInfo]$dotnetinstallpath + } +} + +function Get-MSDeploy{ + [cmdletbinding()] + param() + process{ + $installPath = $env:msdeployinstallpath + + if(!$installPath){ + $keysToCheck = @('hklm:\SOFTWARE\Microsoft\IIS Extensions\MSDeploy\3','hklm:\SOFTWARE\Microsoft\IIS Extensions\MSDeploy\2','hklm:\SOFTWARE\Microsoft\IIS Extensions\MSDeploy\1') + + foreach($keyToCheck in $keysToCheck){ + if(Test-Path $keyToCheck){ + $installPath = (Get-itemproperty $keyToCheck -Name InstallPath -ErrorAction SilentlyContinue | select -ExpandProperty InstallPath -ErrorAction SilentlyContinue) + } + + if($installPath){ + break; + } + } + } + + if(!$installPath){ + throw "Unable to find msdeploy.exe, please install it and try again" + } + + [string]$msdInstallLoc = (join-path $installPath 'msdeploy.exe') + + "Found msdeploy.exe at [{0}]" -f $msdInstallLoc | Write-Verbose + + $msdInstallLoc + } +} + +function InternalNormalize-MSDeployUrl{ + [cmdletbinding()] + param( + [Parameter(Position=0,Mandatory=$true)] + [string]$serviceUrl, + + [string] $siteName, + + [ValidateSet('WMSVC','RemoteAgent','InProc')] + [string]$serviceMethod = 'WMSVC' + ) + process{ + $tempUrl = $serviceUrl + $resultUrl = $serviceUrl + + $httpsStr = 'https://' + $httpStr = 'http://' + $msdeployAxd = 'msdeploy.axd' + + if(-not [string]::IsNullOrWhiteSpace($serviceUrl)){ + if([string]::Compare($serviceMethod,'WMSVC',[StringComparison]::OrdinalIgnoreCase) -eq 0){ + # if no http or https then add one + if(-not ($serviceUrl.StartsWith($httpStr,[StringComparison]::OrdinalIgnoreCase) -or + $serviceUrl.StartsWith($httpsStr,[StringComparison]::OrdinalIgnoreCase)) ){ + + $serviceUrl = [string]::Concat($httpsStr,$serviceUrl.TrimStart()) + } + [System.Uri]$serviceUri = New-Object -TypeName 'System.Uri' $serviceUrl + [System.UriBuilder]$serviceUriBuilder = New-Object -TypeName 'System.UriBuilder' $serviceUrl + + # if it's https and the port was not passed in override it to 8172 + if( ([string]::Compare('https',$serviceUriBuilder.Scheme,[StringComparison]::OrdinalIgnoreCase) -eq 0) -and + -not $serviceUrl.Contains((':{0}' -f $serviceUriBuilder.Port)) ) { + $serviceUriBuilder.Port = 8172 + } + + # if no path then add one + if([string]::Compare('/',$serviceUriBuilder.Path,[StringComparison]::OrdinalIgnoreCase) -eq 0){ + $serviceUriBuilder.Path = $msdeployAxd + } + + if ([string]::IsNullOrEmpty($serviceUriBuilder.Query) -and -not([string]::IsNullOrEmpty($siteName))) + { + $serviceUriBuilder.Query = "site=" + $siteName; + } + + $resultUrl = $serviceUriBuilder.Uri.AbsoluteUri + } + elseif([string]::Compare($serviceMethod,'RemoteAgent',[StringComparison]::OrdinalIgnoreCase) -eq 0){ + [System.UriBuilder]$serviceUriBuilder = New-Object -TypeName 'System.UriBuilder' $serviceUrl + # http://{computername}/MSDEPLOYAGENTSERVICE + # remote agent must use http + $serviceUriBuilder.Scheme = 'http' + $serviceUriBuilder.Path = '/MSDEPLOYAGENTSERVICE' + + $resultUrl = $serviceUriBuilder.Uri.AbsoluteUri + } + else{ + # see if it's for localhost + [System.Uri]$serviceUri = New-Object -TypeName 'System.Uri' $serviceUrl + $resultUrl = $serviceUri.AbsoluteUri + } + } + + # return the result to the caller + $resultUrl + } +} + +function InternalRegister-AspNetKnownPublishHandlers{ + [cmdletbinding()] + param() + process{ + 'Registering MSDeploy handler' | Write-Verbose + Register-AspnetPublishHandler -name 'MSDeploy' -force -handler { + [cmdletbinding()] + param( + [Parameter(Mandatory = $true,Position=0,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)] + $publishProperties, + [Parameter(Mandatory = $true,Position=1,ValueFromPipelineByPropertyName=$true)] + $packOutput + ) + + Publish-AspNetMSDeploy -publishProperties $publishProperties -packOutput $packOutput + } + + 'Registering MSDeploy package handler' | Write-Verbose + Register-AspnetPublishHandler -name 'Package' -force -handler { + [cmdletbinding()] + param( + [Parameter(Mandatory = $true,Position=0,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)] + $publishProperties, + [Parameter(Mandatory = $true,Position=1,ValueFromPipelineByPropertyName=$true)] + $packOutput + ) + + Publish-AspNetMSDeployPackage -publishProperties $publishProperties -packOutput $packOutput + } + + 'Registering FileSystem handler' | Write-Verbose + Register-AspnetPublishHandler -name 'FileSystem' -force -handler { + [cmdletbinding()] + param( + [Parameter(Mandatory = $true,Position=0,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)] + $publishProperties, + [Parameter(Mandatory = $true,Position=1,ValueFromPipelineByPropertyName=$true)] + $packOutput + ) + + Publish-AspNetFileSystem -publishProperties $publishProperties -packOutput $packOutput + } + } +} + +<# +.SYNOPSIS + Used for testing purposes only. +#> +function InternalReset-AspNetPublishHandlers{ + [cmdletbinding()] + param() + process{ + $script:AspNetPublishHandlers = @{} + InternalRegister-AspNetKnownPublishHandlers + } +} + +Export-ModuleMember -function Get-*,Publish-*,Register-*,Enable-* +if($env:IsDeveloperMachine){ + # you can set the env var to expose all functions to importer. easy for development. + # this is required for executing pester test cases, it's set by build.ps1 + Export-ModuleMember -function * +} + +# register the handlers so that Publish-AspNet can be called +InternalRegister-AspNetKnownPublishHandlers + diff --git a/src/DotNetCross.Memory.Copies.Benchmarks2/Rdtsc.cs b/src/DotNetCross.Memory.Copies.Benchmarks2/Rdtsc.cs index 50f3eba..42aeac9 100644 --- a/src/DotNetCross.Memory.Copies.Benchmarks2/Rdtsc.cs +++ b/src/DotNetCross.Memory.Copies.Benchmarks2/Rdtsc.cs @@ -1,11 +1,7 @@ using System; -using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; -using System.Linq; using System.Runtime.InteropServices; -using System.Text; -using System.Threading.Tasks; namespace DotNetCross.Memory.Copies.Benchmarks2 { @@ -250,7 +246,6 @@ static Rdtsc() } } - public static FuncUInt64 TestMovsb { get; set; } [DllImport("kernel32.dll", ExactSpelling = true)] private static extern void GetNativeSystemInfo(out SystemInfo lpSystemInfo); diff --git a/src/DotNetCross.Memory.Copies.Benchmarks2/Tests.cs b/src/DotNetCross.Memory.Copies.Benchmarks2/Tests.cs index a2f52ba..6105df1 100644 --- a/src/DotNetCross.Memory.Copies.Benchmarks2/Tests.cs +++ b/src/DotNetCross.Memory.Copies.Benchmarks2/Tests.cs @@ -12,7 +12,7 @@ public static class Tests private const int Randomizor = +0x3313751; //Pseudo Random Test private const int Alignment = 1; //Alignment test private const int Sequence = -1; //read seq through array - private const int TestMode = Cached; //Alignment test + private const int TestMode = Randomizor; //Alignment test private const int MinIterations = 50; public static ulong TestDuration = 1000000; @@ -106,58 +106,6 @@ public static double TestAnderman(int offset, int size) return mincycles/(double) MinIterations; } - public static double TestJames(int offset, int size) - { - var mincycles = ulong.MaxValue; - var startTest = Rdtsc.TimestampP(); - var testCycles = 0UL; - - do - { - var start = Rdtsc.TimestampP(); - for (var j = 1; j < MinIterations; j++) - { - offset += TestMode == -1 ? size : TestMode; - if (offset + size >= BufferSize) offset &= 0xFFfff; - UnsafeBufferMemmoveJamesqo2.Memmove(_src, offset, _dst, offset, size); - } - var end = Rdtsc.TimestampP(); - var cycles = end - start; - if (cycles <= mincycles) - { - mincycles = cycles; - } - testCycles = Rdtsc.TimestampP() - startTest; - } while (testCycles < TestDuration && testCycles > 0); - return mincycles/(double) MinIterations; - } - - public static double TestMsvcrtMemmove(int offset, int size) - { - var mincycles = ulong.MaxValue; - var startTest = Rdtsc.TimestampP(); - var testCycles = 0UL; - - do - { - var start = Rdtsc.TimestampP(); - for (var j = 1; j < MinIterations; j++) - { - offset += TestMode == -1 ? size : TestMode; - if (offset + size >= BufferSize) offset &= 0xFFfff; - MsvcrtMemove.Memmove(_src, offset, _dst, offset, size); - } - var end = Rdtsc.TimestampP(); - var cycles = end - start; - if (cycles <= mincycles) - { - mincycles = cycles; - } - testCycles = Rdtsc.TimestampP() - startTest; - } while (testCycles < TestDuration && testCycles > 0); - return mincycles/(double) MinIterations; - } - public static double TestArray(int offset, int size) { var mincycles = ulong.MaxValue; diff --git a/src/DotNetCross.Memory.Copies.Benchmarks2/UnsafeBufferMemmoveJamesqo2.cs b/src/DotNetCross.Memory.Copies.Benchmarks2/UnsafeBufferMemmoveJamesqo2.cs deleted file mode 100644 index afe583f..0000000 --- a/src/DotNetCross.Memory.Copies.Benchmarks2/UnsafeBufferMemmoveJamesqo2.cs +++ /dev/null @@ -1,459 +0,0 @@ -#define BIT64 -#define AMD64 -using System; -using System.Numerics; - -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Runtime.InteropServices; -using System.Text; -using System.Threading.Tasks; -#if BIT64 -using nuint = System.UInt64; -#else // BIT64 -using nuint = System.UInt32; -#endif // BIT64 -namespace DotNetCross.Memory.Copies.Benchmarks2 -{ - public class UnsafeBufferMemmoveJamesqo2 - { - public static unsafe void Memmove(byte[] src, int srcOffset, byte[] dst, int dstOffset, int len) - { - if (src == null || dst == null) throw new ArgumentNullException(nameof(src)); - if (len < 0 || srcOffset < 0 || dstOffset < 0) throw new ArgumentOutOfRangeException(nameof(len)); - if (srcOffset + len > src.Length) throw new ArgumentException(nameof(src)); - if (dstOffset + len > dst.Length) throw new ArgumentException(nameof(dst)); - - fixed (byte* pSrcOrigin = &src[srcOffset]) - fixed (byte* pDstOrigin = &dst[dstOffset]) - { - var srcOrigin = pSrcOrigin; - var dest = pDstOrigin; - Memmove(dest, srcOrigin, (nuint)len); - } - } - - //[System.Security.SecurityCritical] - internal static unsafe void Memmove(byte* dest, byte* src, nuint len) - { - // P/Invoke into the native version when the buffers are overlapping and the copy needs to be performed backwards - // This check can produce false positives for lengths greater than int.MaxValue. It is fine because we want to use the P/Invoke path for the large lengths anyway. - - //if ((nuint)dest - (nuint)src < len) goto PInvoke; - - // This is portable version of memcpy. It mirrors what the hand optimized assembly versions of memcpy typically do. - // - // Ideally, we would just use the cpblk IL instruction here. Unfortunately, cpblk IL instruction is not as efficient as - // possible yet and so we have this implementation here for now. - - // Note: It's important that this switch handles lengths at least up to 15 for AMD64. - // We assume below len is at least 16 and make one 128-bit write without checking. - - // The switch will be very fast since it can be implemented using a jump - // table in assembly. See http://stackoverflow.com/a/449297/4077294 for more info. - switch (len) - { - case 0: - return; - case 1: - *dest = *src; - return; - case 2: - *(short*)dest = *(short*)src; - return; - case 3: - *(short*)dest = *(short*)src; - *(dest + 2) = *(src + 2); - return; - case 4: - *(int*)dest = *(int*)src; - return; - case 5: - *(int*)dest = *(int*)src; - *(dest + 4) = *(src + 4); - return; - case 6: - *(int*)dest = *(int*)src; - *(short*)(dest + 4) = *(short*)(src + 4); - return; - case 7: - *(int*)dest = *(int*)src; - *(short*)(dest + 4) = *(short*)(src + 4); - *(dest + 6) = *(src + 6); - return; - case 8: -#if BIT64 - *(long*)dest = *(long*)src; -#else // BIT64 - *(int*)dest = *(int*)src; - *(int*)(dest + 4) = *(int*)(src + 4); -#endif // BIT64 - return; - case 9: -#if BIT64 - *(long*)dest = *(long*)src; -#else // BIT64 - *(int*)dest = *(int*)src; - *(int*)(dest + 4) = *(int*)(src + 4); -#endif // BIT64 - *(dest + 8) = *(src + 8); - return; - case 10: -#if BIT64 - *(long*)dest = *(long*)src; -#else // BIT64 - *(int*)dest = *(int*)src; - *(int*)(dest + 4) = *(int*)(src + 4); -#endif // BIT64 - *(short*)(dest + 8) = *(short*)(src + 8); - return; - case 11: -#if BIT64 - *(long*)dest = *(long*)src; -#else // BIT64 - *(int*)dest = *(int*)src; - *(int*)(dest + 4) = *(int*)(src + 4); -#endif // BIT64 - *(short*)(dest + 8) = *(short*)(src + 8); - *(dest + 10) = *(src + 10); - return; - case 12: -#if BIT64 - *(long*)dest = *(long*)src; -#else // BIT64 - *(int*)dest = *(int*)src; - *(int*)(dest + 4) = *(int*)(src + 4); -#endif // BIT64 - *(int*)(dest + 8) = *(int*)(src + 8); - return; - case 13: -#if BIT64 - *(long*)dest = *(long*)src; -#else // BIT64 - *(int*)dest = *(int*)src; - *(int*)(dest + 4) = *(int*)(src + 4); -#endif // BIT64 - *(int*)(dest + 8) = *(int*)(src + 8); - *(dest + 12) = *(src + 12); - return; - case 14: -#if BIT64 - *(long*)dest = *(long*)src; -#else // BIT64 - *(int*)dest = *(int*)src; - *(int*)(dest + 4) = *(int*)(src + 4); -#endif // BIT64 - *(int*)(dest + 8) = *(int*)(src + 8); - *(short*)(dest + 12) = *(short*)(src + 12); - return; - case 15: -#if BIT64 - *(long*)dest = *(long*)src; -#else // BIT64 - *(int*)dest = *(int*)src; - *(int*)(dest + 4) = *(int*)(src + 4); -#endif // BIT64 - *(int*)(dest + 8) = *(int*)(src + 8); - *(short*)(dest + 12) = *(short*)(src + 12); - *(dest + 14) = *(src + 14); - return; - } - - // P/Invoke into the native version for large lengths. - // Currently the threshold at which the native version is faster seems to be around 8192 - // on amd64 Windows, but this is subject to change if this implementation can be made faster. - //if (len >= 8192) goto PInvoke; - - // So far SIMD is only enabled for AMD64, so on that plaform we want - // to 16-byte align while on others (including arm64) we'll want to word-align -#if AMD64 - nuint alignment = 16u; -#else // AMD64 - nuint alignment = (nuint)sizeof(nuint); -#endif // AMD64 - - // (nuint)dest % alignment calculates how far we are from the previous aligned address - // Note that it's *very* important alignment is unsigned. - // (int)dest % (int)alignment for example will give different results if the lhs is negative. - - // If dest is aligned this will be 0. - nuint i = (nuint)dest % alignment; - - // We know from the above switch-case that len is at least 16, so here - // we subtract i from 16. This represents the furthest aligned address - // we know it's okay to write upto. - // To make it clearer, (dest + i) after this is equivalent to - // [previous aligned address] + 16. - i = 16u - i; - -#if AMD64 - // SIMD is enabled for AMD64, so take advantage of that and use movdqu - *(Buffer16*)dest = *(Buffer16*)src; -#elif ARM64 - // ARM64 has 64-bit words but no SIMD yet, so make 2 word writes - // First one isn't aligned, second one is (remember from earlier notes dest + i is 8-aligned) - *(long*)dest = *(long*)src; - *(long*)(dest + i - 8) = *(long*)(src + i - 8); -#else // AMD64, ARM64 - // i386 and ARM: 32-bit words, no SIMD (yet) - // make 1 unaligned word write, then 3 4-byte aligned ones - *(int*)dest = *(int*)src; - *(int*)(dest + i - 12) = *(int*)(src + i - 12); - *(int*)(dest + i - 8) = *(int*)(src + i - 8); - *(int*)(dest + i - 4) = *(int*)(src + i - 4); -#endif // AMD64, ARM64 - - // i now represents the number of bytes we've copied so far. - //Contract.Assert(i <= len && i > 0 && i <= 16); - //Contract.Assert((nuint)(dest + i) % alignment == 0); - - // chunk: bytes processed per iteration in unrolled loop - // Note: Not directly related to sizeof(nuint), e.g. sizeof(nuint) * 8 is not a valid substitution. - nuint chunk = sizeof(nuint) == 4 ? 32 : 64; - - // mask: represents how many bytes are left after alignment - // Since we copy the bytes in chunks of 2, mask will also have the lower few - // bits of mask (mask & (chunk - 1), but we don't explicitly calculate that) - // will represent how many bytes are left *after* the unrolled loop. - nuint mask = len - i; - - // Protect ourselves from unsigned overflow - if (len < chunk) - goto LoopCleanup; - - // end: point after which we stop the unrolled loop - // This is the end of the buffer, minus the space - // required for 1 iteration of the loop. - nuint end = len - chunk; - - // This can return false in the first iteration if the process of - // aligning the pointer for writes has not left enough space - // for this loop to run, so unfortunately this can't be a do-while loop. - while (i <= end) - { - // Some versions of this loop looks very costly since there appear - // to be a bunch of temporary values being created with the adds, - // but the jit (for x86 anyways) will convert each of these to - // use memory addressing operands. - - // So the only cost is a bit of code size, which is made up for by the fact that - // we save on writes to dest/src. - -#if AMD64 - // Write 64 bytes at a time, taking advantage of xmm register on AMD64 - // This will be translated to 4 movdqus (maybe movdqas in the future, dotnet/coreclr#2725) - *(Buffer64*)(dest + i) = *(Buffer64*)(src + i); -#elif ARM64 - // ARM64: Also unroll by 64 bytes, this time using longs since we don't - // take advantage of SIMD for that plaform yet. - *(long*)(dest + i) = *(long*)(src + i); - *(long*)(dest + i + 8) = *(long*)(src + i + 8); - *(long*)(dest + i + 16) = *(long*)(src + i + 16); - *(long*)(dest + i + 24) = *(long*)(src + i + 24); - *(long*)(dest + i + 32) = *(long*)(src + i + 32); - *(long*)(dest + i + 40) = *(long*)(src + i + 40); - *(long*)(dest + i + 48) = *(long*)(src + i + 48); - *(long*)(dest + i + 56) = *(long*)(src + i + 56); -#else // AMD64, ARM64 - // i386/ARM32: - // Write 32 bytes at a time, via 8 32-bit word writes - *(int*)(dest + i) = *(int*)(src + i); - *(int*)(dest + i + 4) = *(int*)(src + i + 4); - *(int*)(dest + i + 8) = *(int*)(src + i + 8); - *(int*)(dest + i + 12) = *(int*)(src + i + 12); - *(int*)(dest + i + 16) = *(int*)(src + i + 16); - *(int*)(dest + i + 20) = *(int*)(src + i + 20); - *(int*)(dest + i + 24) = *(int*)(src + i + 24); - *(int*)(dest + i + 28) = *(int*)(src + i + 28); -#endif // AMD64, ARM64 - - i += chunk; - } - - LoopCleanup: - // If we've reached this point, there are at most chunk - 1 bytes left - -#if BIT64 - // mask & 63 represents how many bytes there are left. - // if the mask & 32 bit is set that means this number - // will be >= 32. (same principle applies for other - // powers of 2 below) - if ((mask & 32) != 0) - { -#if AMD64 - *(Buffer32*)(dest + i) = *(Buffer32*)(src + i); -#else // AMD64 - *(long*)(dest + i) = *(long*)(src + i); - *(long*)(dest + i + 8) = *(long*)(src + i + 8); - *(long*)(dest + i + 16) = *(long*)(src + i + 16); - *(long*)(dest + i + 24) = *(long*)(src + i + 24); -#endif // AMD64 - - i += 32; - } -#endif // BIT64 - - // Now there can be at most 31 bytes left - - if ((mask & 16) != 0) - { -#if AMD64 - *(Buffer16*)(dest + i) = *(Buffer16*)(src + i); -#elif ARM64 - *(long*)(dest + i) = *(long*)(src + i); - *(long*)(dest + i + 8) = *(long*)(src + i + 8); -#else // AMD64, ARM64 - *(int*)(dest + i) = *(int*)(src + i); - *(int*)(dest + i + 4) = *(int*)(src + i + 4); - *(int*)(dest + i + 8) = *(int*)(src + i + 8); - *(int*)(dest + i + 12) = *(int*)(src + i + 12); -#endif // AMD64, ARM64 - - i += 16; - } - - // Now there can be at most 15 bytes left - // For AMD64 we just want to make 1 (potentially) unaligned xmm write and quit. - // For other platforms we have another switch-case for 0..15. - // Again, this is implemented with a jump table so it's very fast. - -#if AMD64 - i = len - 16; - *(Buffer16*)(dest + i) = *(Buffer16*)(src + i); -#else // AMD64 - - switch (mask & 15) - { - case 0: - // No-op: We already finished copying all the bytes. - return; - case 1: - *(dest + i) = *(src + i); - return; - case 2: - *(short*)(dest + i) = *(short*)(src + i); - return; - case 3: - *(short*)(dest + i) = *(short*)(src + i); - *(dest + i + 2) = *(src + i + 2); - return; - case 4: - *(int*)(dest + i) = *(int*)(src + i); - return; - case 5: - *(int*)(dest + i) = *(int*)(src + i); - *(dest + i + 4) = *(src + i + 4); - return; - case 6: - *(int*)(dest + i) = *(int*)(src + i); - *(short*)(dest + i + 4) = *(short*)(src + i + 4); - return; - case 7: - *(int*)(dest + i) = *(int*)(src + i); - *(short*)(dest + i + 4) = *(short*)(src + i + 4); - *(dest + i + 6) = *(src + i + 6); - return; - case 8: -#if BIT64 - *(long*)(dest + i) = *(long*)(src + i); -#else // BIT64 - *(int*)(dest + i) = *(int*)(src + i); - *(int*)(dest + i + 4) = *(int*)(src + i + 4); -#endif // BIT64 - return; - case 9: -#if BIT64 - *(long*)(dest + i) = *(long*)(src + i); -#else // BIT64 - *(int*)(dest + i) = *(int*)(src + i); - *(int*)(dest + i + 4) = *(int*)(src + i + 4); -#endif // BIT64 - *(dest + i + 8) = *(src + i + 8); - return; - case 10: -#if BIT64 - *(long*)(dest + i) = *(long*)(src + i); -#else // BIT64 - *(int*)(dest + i) = *(int*)(src + i); - *(int*)(dest + i + 4) = *(int*)(src + i + 4); -#endif // BIT64 - *(short*)(dest + i + 8) = *(short*)(src + i + 8); - return; - case 11: -#if BIT64 - *(long*)(dest + i) = *(long*)(src + i); -#else // BIT64 - *(int*)(dest + i) = *(int*)(src + i); - *(int*)(dest + i + 4) = *(int*)(src + i + 4); -#endif // BIT64 - *(short*)(dest + i + 8) = *(short*)(src + i + 8); - *(dest + i + 10) = *(src + i + 10); - return; - case 12: -#if BIT64 - *(long*)(dest + i) = *(long*)(src + i); -#else // BIT64 - *(int*)(dest + i) = *(int*)(src + i); - *(int*)(dest + i + 4) = *(int*)(src + i + 4); -#endif // BIT64 - *(int*)(dest + i + 8) = *(int*)(src + i + 8); - return; - case 13: -#if BIT64 - *(long*)(dest + i) = *(long*)(src + i); -#else // BIT64 - *(int*)(dest + i) = *(int*)(src + i); - *(int*)(dest + i + 4) = *(int*)(src + i + 4); -#endif // BIT64 - *(int*)(dest + i + 8) = *(int*)(src + i + 8); - *(dest + i + 12) = *(src + i + 12); - return; - case 14: -#if BIT64 - *(long*)(dest + i) = *(long*)(src + i); -#else // BIT64 - *(int*)(dest + i) = *(int*)(src + i); - *(int*)(dest + i + 4) = *(int*)(src + i + 4); -#endif // BIT64 - *(int*)(dest + i + 8) = *(int*)(src + i + 8); - *(short*)(dest + i + 12) = *(short*)(src + i + 12); - return; - case 15: -#if BIT64 - *(long*)(dest + i) = *(long*)(src + i); -#else // BIT64 - *(int*)(dest + i) = *(int*)(src + i); - *(int*)(dest + i + 4) = *(int*)(src + i + 4); -#endif // BIT64 - *(int*)(dest + i + 8) = *(int*)(src + i + 8); - *(short*)(dest + i + 12) = *(short*)(src + i + 12); - *(dest + i + 14) = *(src + i + 14); - return; - } - -#endif // AMD64 - - return; - - //PInvoke: - //_Memmove(dest, src, len); - - } - - [StructLayout(LayoutKind.Sequential, Size = 64)] - private struct Buffer64 - { - } - - [StructLayout(LayoutKind.Sequential, Size = 32)] - private struct Buffer32 - { - } - - [StructLayout(LayoutKind.Sequential, Size = 16)] - private struct Buffer16 - { - } - } -} \ No newline at end of file diff --git a/src/DotNetCross.Memory.Copies.Benchmarks2/chart.html b/src/DotNetCross.Memory.Copies.Benchmarks2/chart.html index 8b7d561..697521b 100644 --- a/src/DotNetCross.Memory.Copies.Benchmarks2/chart.html +++ b/src/DotNetCross.Memory.Copies.Benchmarks2/chart.html @@ -19,7 +19,7 @@ title: 'ClockCycles/operation' }, colors: ['#5B9BD5', '#ED7D31', '#A5A5A5', '#FFC000'], - title:"Memmove", + title:"Memmove Random", height: window.innerHeight-20, width: window.innerWidth-20 }; diff --git a/src/DotNetCross.Memory.Copies.Benchmarks2/msvcrtMemove.cs b/src/DotNetCross.Memory.Copies.Benchmarks2/msvcrtMemove.cs deleted file mode 100644 index 03f5f38..0000000 --- a/src/DotNetCross.Memory.Copies.Benchmarks2/msvcrtMemove.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System; -using System.Diagnostics; -using System.Runtime.InteropServices; - -namespace DotNetCross.Memory.Copies.Benchmarks2 -{ - public static class MsvcrtMemove { - - public static unsafe void Memmove(byte[] src, int srcOffset, byte[] dst, int dstOffset, int count) - { - fixed (byte* pSrcOrigin = &src[srcOffset]) - fixed (byte* pDstOrigin = &dst[dstOffset]) - { - var pSrc = pSrcOrigin; - var pDst = pDstOrigin; - memmove(pDst, pSrc, count); - } - } - - [DllImport("msvcrt.dll", SetLastError = false)] - public static extern unsafe IntPtr memmove(void* dest, void* src, int count); - } -} \ No newline at end of file diff --git a/src/DotNetCross.Memory.Copies.Benchmarks2/project.json b/src/DotNetCross.Memory.Copies.Benchmarks2/project.json index db8beba..259edb9 100644 --- a/src/DotNetCross.Memory.Copies.Benchmarks2/project.json +++ b/src/DotNetCross.Memory.Copies.Benchmarks2/project.json @@ -8,19 +8,20 @@ "dependencies": { "Microsoft.NETCore.App": { "type": "platform", - "version": "1.0.0" + "version": "1.0.1" }, "System.Runtime.CompilerServices.Unsafe": "4.0.0", "System.Linq": "4.1.0", "System.Reflection": "4.1.0", - "Newtonsoft.Json": "9.0.1", + "Newtonsoft.Json": "9.0.1" }, "runtimes": { "win8-x64": {} }, "frameworks": { "netcoreapp1.0": { - "imports": "dnxcore50" + "dependencies": { + } } } } diff --git a/src/DotNetCross.Memory.Copies.Benchmarks/Anderman.cs b/src/old/DotNetCross.Memory.Copies.Benchmarks/Anderman.cs similarity index 100% rename from src/DotNetCross.Memory.Copies.Benchmarks/Anderman.cs rename to src/old/DotNetCross.Memory.Copies.Benchmarks/Anderman.cs diff --git a/src/DotNetCross.Memory.Copies.Benchmarks/App.config b/src/old/DotNetCross.Memory.Copies.Benchmarks/App.config similarity index 100% rename from src/DotNetCross.Memory.Copies.Benchmarks/App.config rename to src/old/DotNetCross.Memory.Copies.Benchmarks/App.config diff --git a/src/DotNetCross.Memory.Copies.Benchmarks/CopiesBenchmark.cs b/src/old/DotNetCross.Memory.Copies.Benchmarks/CopiesBenchmark.cs similarity index 100% rename from src/DotNetCross.Memory.Copies.Benchmarks/CopiesBenchmark.cs rename to src/old/DotNetCross.Memory.Copies.Benchmarks/CopiesBenchmark.cs diff --git a/src/DotNetCross.Memory.Copies.Benchmarks/DotNetCross.Memory.Copies.Benchmarks.csproj b/src/old/DotNetCross.Memory.Copies.Benchmarks/DotNetCross.Memory.Copies.Benchmarks.csproj similarity index 100% rename from src/DotNetCross.Memory.Copies.Benchmarks/DotNetCross.Memory.Copies.Benchmarks.csproj rename to src/old/DotNetCross.Memory.Copies.Benchmarks/DotNetCross.Memory.Copies.Benchmarks.csproj diff --git a/src/DotNetCross.Memory.Copies.Benchmarks/Illyriad.cs b/src/old/DotNetCross.Memory.Copies.Benchmarks/Illyriad.cs similarity index 100% rename from src/DotNetCross.Memory.Copies.Benchmarks/Illyriad.cs rename to src/old/DotNetCross.Memory.Copies.Benchmarks/Illyriad.cs diff --git a/src/DotNetCross.Memory.Copies.Benchmarks/Msvcrt.cs b/src/old/DotNetCross.Memory.Copies.Benchmarks/Msvcrt.cs similarity index 100% rename from src/DotNetCross.Memory.Copies.Benchmarks/Msvcrt.cs rename to src/old/DotNetCross.Memory.Copies.Benchmarks/Msvcrt.cs diff --git a/src/DotNetCross.Memory.Copies.Benchmarks/Program.cs b/src/old/DotNetCross.Memory.Copies.Benchmarks/Program.cs similarity index 100% rename from src/DotNetCross.Memory.Copies.Benchmarks/Program.cs rename to src/old/DotNetCross.Memory.Copies.Benchmarks/Program.cs diff --git a/src/DotNetCross.Memory.Copies.Benchmarks/Properties/AssemblyInfo.cs b/src/old/DotNetCross.Memory.Copies.Benchmarks/Properties/AssemblyInfo.cs similarity index 100% rename from src/DotNetCross.Memory.Copies.Benchmarks/Properties/AssemblyInfo.cs rename to src/old/DotNetCross.Memory.Copies.Benchmarks/Properties/AssemblyInfo.cs diff --git a/src/DotNetCross.Memory.Copies.Benchmarks/UnsafeAnderman.cs b/src/old/DotNetCross.Memory.Copies.Benchmarks/UnsafeAnderman.cs similarity index 100% rename from src/DotNetCross.Memory.Copies.Benchmarks/UnsafeAnderman.cs rename to src/old/DotNetCross.Memory.Copies.Benchmarks/UnsafeAnderman.cs diff --git a/src/DotNetCross.Memory.Copies.Benchmarks/UnsafeAnderman2.cs b/src/old/DotNetCross.Memory.Copies.Benchmarks/UnsafeAnderman2.cs similarity index 100% rename from src/DotNetCross.Memory.Copies.Benchmarks/UnsafeAnderman2.cs rename to src/old/DotNetCross.Memory.Copies.Benchmarks/UnsafeAnderman2.cs diff --git a/src/DotNetCross.Memory.Copies.Benchmarks/UnsafeBufferMemmoveJamesqo.cs b/src/old/DotNetCross.Memory.Copies.Benchmarks/UnsafeBufferMemmoveJamesqo.cs similarity index 100% rename from src/DotNetCross.Memory.Copies.Benchmarks/UnsafeBufferMemmoveJamesqo.cs rename to src/old/DotNetCross.Memory.Copies.Benchmarks/UnsafeBufferMemmoveJamesqo.cs diff --git a/src/DotNetCross.Memory.Copies.Benchmarks/UnsafeBufferMemmoveJamesqo2.cs b/src/old/DotNetCross.Memory.Copies.Benchmarks/UnsafeBufferMemmoveJamesqo2.cs similarity index 100% rename from src/DotNetCross.Memory.Copies.Benchmarks/UnsafeBufferMemmoveJamesqo2.cs rename to src/old/DotNetCross.Memory.Copies.Benchmarks/UnsafeBufferMemmoveJamesqo2.cs diff --git a/src/DotNetCross.Memory.Copies.Benchmarks/UnsafeBufferMemmoveOriginal.cs b/src/old/DotNetCross.Memory.Copies.Benchmarks/UnsafeBufferMemmoveOriginal.cs similarity index 100% rename from src/DotNetCross.Memory.Copies.Benchmarks/UnsafeBufferMemmoveOriginal.cs rename to src/old/DotNetCross.Memory.Copies.Benchmarks/UnsafeBufferMemmoveOriginal.cs diff --git a/src/DotNetCross.Memory.Copies.Benchmarks/UnsafeBufferMemmoveTannerGooding.cs b/src/old/DotNetCross.Memory.Copies.Benchmarks/UnsafeBufferMemmoveTannerGooding.cs similarity index 100% rename from src/DotNetCross.Memory.Copies.Benchmarks/UnsafeBufferMemmoveTannerGooding.cs rename to src/old/DotNetCross.Memory.Copies.Benchmarks/UnsafeBufferMemmoveTannerGooding.cs diff --git a/src/DotNetCross.Memory.Copies.Benchmarks/UnsafeBufferMemmoveTannerGooding2.cs b/src/old/DotNetCross.Memory.Copies.Benchmarks/UnsafeBufferMemmoveTannerGooding2.cs similarity index 100% rename from src/DotNetCross.Memory.Copies.Benchmarks/UnsafeBufferMemmoveTannerGooding2.cs rename to src/old/DotNetCross.Memory.Copies.Benchmarks/UnsafeBufferMemmoveTannerGooding2.cs diff --git a/src/DotNetCross.Memory.Copies.Benchmarks/UnsafeBufferMemoryCopy.cs b/src/old/DotNetCross.Memory.Copies.Benchmarks/UnsafeBufferMemoryCopy.cs similarity index 100% rename from src/DotNetCross.Memory.Copies.Benchmarks/UnsafeBufferMemoryCopy.cs rename to src/old/DotNetCross.Memory.Copies.Benchmarks/UnsafeBufferMemoryCopy.cs diff --git a/src/DotNetCross.Memory.Copies.Benchmarks/UnsafeCpblk.cs b/src/old/DotNetCross.Memory.Copies.Benchmarks/UnsafeCpblk.cs similarity index 100% rename from src/DotNetCross.Memory.Copies.Benchmarks/UnsafeCpblk.cs rename to src/old/DotNetCross.Memory.Copies.Benchmarks/UnsafeCpblk.cs diff --git a/src/DotNetCross.Memory.Copies.Benchmarks/UnsafeIllyriad.cs b/src/old/DotNetCross.Memory.Copies.Benchmarks/UnsafeIllyriad.cs similarity index 100% rename from src/DotNetCross.Memory.Copies.Benchmarks/UnsafeIllyriad.cs rename to src/old/DotNetCross.Memory.Copies.Benchmarks/UnsafeIllyriad.cs diff --git a/src/DotNetCross.Memory.Copies.Benchmarks/UnsafeNietras.cs b/src/old/DotNetCross.Memory.Copies.Benchmarks/UnsafeNietras.cs similarity index 100% rename from src/DotNetCross.Memory.Copies.Benchmarks/UnsafeNietras.cs rename to src/old/DotNetCross.Memory.Copies.Benchmarks/UnsafeNietras.cs diff --git a/src/DotNetCross.Memory.Copies.Benchmarks/UnsafeNoChecksCopiesBenchmark.cs b/src/old/DotNetCross.Memory.Copies.Benchmarks/UnsafeNoChecksCopiesBenchmark.cs similarity index 100% rename from src/DotNetCross.Memory.Copies.Benchmarks/UnsafeNoChecksCopiesBenchmark.cs rename to src/old/DotNetCross.Memory.Copies.Benchmarks/UnsafeNoChecksCopiesBenchmark.cs diff --git a/src/DotNetCross.Memory.Copies.Benchmarks/packages.config b/src/old/DotNetCross.Memory.Copies.Benchmarks/packages.config similarity index 100% rename from src/DotNetCross.Memory.Copies.Benchmarks/packages.config rename to src/old/DotNetCross.Memory.Copies.Benchmarks/packages.config diff --git a/src/mov_repsb/AndermanMovsb.cs b/src/old/mov_repsb/AndermanMovsb.cs similarity index 100% rename from src/mov_repsb/AndermanMovsb.cs rename to src/old/mov_repsb/AndermanMovsb.cs diff --git a/src/mov_repsb/Program.cs b/src/old/mov_repsb/Program.cs similarity index 100% rename from src/mov_repsb/Program.cs rename to src/old/mov_repsb/Program.cs diff --git a/src/mov_repsb/Properties/AssemblyInfo.cs b/src/old/mov_repsb/Properties/AssemblyInfo.cs similarity index 100% rename from src/mov_repsb/Properties/AssemblyInfo.cs rename to src/old/mov_repsb/Properties/AssemblyInfo.cs diff --git a/src/mov_repsb/mov_repsb.xproj b/src/old/mov_repsb/mov_repsb.xproj similarity index 100% rename from src/mov_repsb/mov_repsb.xproj rename to src/old/mov_repsb/mov_repsb.xproj diff --git a/src/mov_repsb/project.json b/src/old/mov_repsb/project.json similarity index 100% rename from src/mov_repsb/project.json rename to src/old/mov_repsb/project.json