|
| 1 | +// Licensed to the .NET Foundation under one or more agreements. |
| 2 | +// The .NET Foundation licenses this file to you under the MIT license. |
| 3 | +// See the LICENSE file in the project root for more information. |
| 4 | + |
| 5 | +// Adapted from mandelbrot C# .NET Core #7 program |
| 6 | +// http://benchmarksgame.alioth.debian.org/u64q/program.php?test=mandelbrot&lang=csharpcore&id=7 |
| 7 | +// aka (as of 2017-10-02) rev 1.2 of https://alioth.debian.org/scm/viewvc.php/benchmarksgame/bench/mandelbrot/mandelbrot.csharp-7.csharp?root=benchmarksgame&view=log |
| 8 | +// Best-scoring C# .NET Core version as of 2017-10-02 |
| 9 | + |
| 10 | +/* The Computer Language Benchmarks Game |
| 11 | + http://benchmarksgame.alioth.debian.org/ |
| 12 | +
|
| 13 | + started with Java #2 program (Krause/Whipkey/Bennet/AhnTran/Enotus/Stalcup) |
| 14 | + adapted for C# by Jan de Vaan |
| 15 | + simplified and optimised to use TPL by Anthony Lloyd |
| 16 | + simplified to compute Cib alongside Crb by Tanner Gooding |
| 17 | + optimized to use Vector<double> by Tanner Gooding |
| 18 | +*/ |
| 19 | + |
| 20 | +using System; |
| 21 | +using System.IO; |
| 22 | +using System.Numerics; |
| 23 | +using System.Runtime.CompilerServices; |
| 24 | +using System.Security.Cryptography; |
| 25 | +using System.Threading.Tasks; |
| 26 | +using Microsoft.Xunit.Performance; |
| 27 | +using Xunit; |
| 28 | + |
| 29 | +[assembly: OptimizeForBenchmarks] |
| 30 | + |
| 31 | +namespace BenchmarksGame |
| 32 | +{ |
| 33 | + public class MandelBrot_7 |
| 34 | + { |
| 35 | + // Vector<double>.Count is treated as a constant by the JIT, don't bother |
| 36 | + // storing it in a temporary variable anywhere below. |
| 37 | + |
| 38 | + [MethodImpl(MethodImplOptions.AggressiveInlining)] |
| 39 | + private static unsafe byte GetByte(double* pCrb, double Ciby, int x, int y) |
| 40 | + { |
| 41 | + // This currently does not do anything special for 'Count > 2' |
| 42 | + |
| 43 | + var res = 0; |
| 44 | + |
| 45 | + for (var i = 0; i < 8; i += 2) |
| 46 | + { |
| 47 | + var Crbx = Unsafe.Read<Vector<double>>(pCrb + x + i); |
| 48 | + var Zr = Crbx; |
| 49 | + var vCiby = new Vector<double>(Ciby); |
| 50 | + var Zi = vCiby; |
| 51 | + |
| 52 | + var b = 0; |
| 53 | + var j = 49; |
| 54 | + |
| 55 | + do |
| 56 | + { |
| 57 | + var nZr = Zr * Zr - Zi * Zi + Crbx; |
| 58 | + Zi = Zr * Zi + Zr * Zi + vCiby; |
| 59 | + Zr = nZr; |
| 60 | + |
| 61 | + var t = Zr * Zr + Zi * Zi; |
| 62 | + |
| 63 | + if (t[0] > 4) |
| 64 | + { |
| 65 | + b |= 2; |
| 66 | + |
| 67 | + if (b == 3) |
| 68 | + { |
| 69 | + break; |
| 70 | + } |
| 71 | + } |
| 72 | + |
| 73 | + if (t[1] > 4) |
| 74 | + { |
| 75 | + b |= 1; |
| 76 | + |
| 77 | + if (b == 3) |
| 78 | + { |
| 79 | + break; |
| 80 | + } |
| 81 | + } |
| 82 | + |
| 83 | + } while (--j > 0); |
| 84 | + |
| 85 | + res = (res << 2) + b; |
| 86 | + } |
| 87 | + |
| 88 | + return (byte)(res ^ -1); |
| 89 | + } |
| 90 | + |
| 91 | + public static int Main(string[] args) |
| 92 | + { |
| 93 | + var size = (args.Length > 0) ? int.Parse(args[0]) : 80; |
| 94 | + var lineLength = size >> 3; |
| 95 | + |
| 96 | + var data = DoBench(size, lineLength); |
| 97 | + var dataLength = size * lineLength; |
| 98 | + |
| 99 | + Console.Out.Write("P4\n{0} {0}\n", size); |
| 100 | + Console.OpenStandardOutput().Write(data, 0, dataLength); |
| 101 | + |
| 102 | + return MatchesChecksum(data, dataLength, "3B-EF-65-05-1D-39-7F-9B-96-8D-EF-98-BF-06-CE-74") ? 100 : -1; |
| 103 | + } |
| 104 | + |
| 105 | + // Commented out data left in source to provide checksums for each case |
| 106 | + |
| 107 | + [Benchmark(InnerIterationCount = 7)] |
| 108 | + //[InlineData(1000, 125, "B2-13-51-CE-B0-29-2C-4E-75-5E-91-19-18-E4-0C-D9")] |
| 109 | + //[InlineData(2000, 250, "5A-21-55-9B-7B-18-2F-34-9B-33-C5-F9-B5-2C-40-56")] |
| 110 | + //[InlineData(3000, 375, "E5-82-85-0A-3C-89-69-B1-A8-21-63-52-75-B3-C8-33")] |
| 111 | + [InlineData(4000, 500, "C7-E6-66-43-66-73-F8-A8-D3-B4-D7-97-2F-FC-A1-D3")] |
| 112 | + //[InlineData(5000, 625, "6D-36-F1-F6-37-8F-34-EB-52-F9-2D-11-89-12-B2-2F")] |
| 113 | + //[InlineData(6000, 750, "8B-05-78-EB-2E-0E-98-F2-C7-39-76-ED-0F-A9-D2-B8")] |
| 114 | + //[InlineData(7000, 875, "01-F8-F2-2A-AB-70-C7-BA-E3-64-19-E7-D2-84-DF-57")] |
| 115 | + //[InlineData(8000, 1000, "C8-ED-D7-FB-65-66-3A-D9-C6-04-9E-96-E8-CA-4F-2C")] |
| 116 | + public static void Bench(int size, int lineLength, string checksum) |
| 117 | + { |
| 118 | + byte[] bytes = null; |
| 119 | + |
| 120 | + Benchmark.Iterate(() => { |
| 121 | + bytes = DoBench(size, lineLength); |
| 122 | + }); |
| 123 | + |
| 124 | + Assert.True(MatchesChecksum(bytes, size * lineLength, checksum)); |
| 125 | + } |
| 126 | + |
| 127 | + static bool MatchesChecksum(byte[] bytes, int length, string checksum) |
| 128 | + { |
| 129 | + using (var md5 = MD5.Create()) |
| 130 | + { |
| 131 | + byte[] hash = md5.ComputeHash(bytes, 0, length); |
| 132 | + return (checksum == BitConverter.ToString(hash)); |
| 133 | + } |
| 134 | + } |
| 135 | + |
| 136 | + static unsafe byte[] DoBench(int size, int lineLength) |
| 137 | + { |
| 138 | + var adjustedSize = size + (Vector<double>.Count * 8); |
| 139 | + adjustedSize &= ~(Vector<double>.Count * 8); |
| 140 | + |
| 141 | + var Crb = new double[adjustedSize]; |
| 142 | + var Cib = new double[adjustedSize]; |
| 143 | + |
| 144 | + fixed (double* pCrb = &Crb[0]) |
| 145 | + fixed (double* pCib = &Cib[0]) |
| 146 | + { |
| 147 | + var invN = new Vector<double>(2.0 / size); |
| 148 | + |
| 149 | + var onePtFive = new Vector<double>(1.5); |
| 150 | + var step = new Vector<double>(Vector<double>.Count); |
| 151 | + |
| 152 | + Vector<double> value; |
| 153 | + |
| 154 | + if (Vector<double>.Count == 2) |
| 155 | + { |
| 156 | + // Software implementation should hit this path too |
| 157 | + |
| 158 | + value = new Vector<double>(new double[] { |
| 159 | + 0, 1 |
| 160 | + }); |
| 161 | + } |
| 162 | + else if (Vector<double>.Count == 4) |
| 163 | + { |
| 164 | + value = new Vector<double>(new double[] { |
| 165 | + 0, 1, 2, 3 |
| 166 | + }); |
| 167 | + } |
| 168 | + else |
| 169 | + { |
| 170 | + // No hardware supports about 'Count == 8' today |
| 171 | + |
| 172 | + value = new Vector<double>(new double[] { |
| 173 | + 0, 1, 2, 3, 4, 5, 6, 7 |
| 174 | + }); |
| 175 | + } |
| 176 | + |
| 177 | + for (var i = 0; i < size; i += Vector<double>.Count) |
| 178 | + { |
| 179 | + var t = value * invN; |
| 180 | + |
| 181 | + Unsafe.Write(pCrb + i, t - onePtFive); |
| 182 | + Unsafe.Write(pCib + i, t - Vector<double>.One); |
| 183 | + |
| 184 | + value += step; |
| 185 | + } |
| 186 | + } |
| 187 | + |
| 188 | + var data = new byte[adjustedSize * lineLength]; |
| 189 | + |
| 190 | + fixed (double* pCrb = &Crb[0]) |
| 191 | + { |
| 192 | + // C# doesn't let us pass a pinned variable to a lambda directly |
| 193 | + var _Crb = pCrb; |
| 194 | + |
| 195 | + Parallel.For(0, size, y => { |
| 196 | + var offset = y * lineLength; |
| 197 | + |
| 198 | + for (var x = 0; x < lineLength; x++) |
| 199 | + { |
| 200 | + data[offset + x] = GetByte(_Crb, Cib[y], x * 8, y); |
| 201 | + } |
| 202 | + }); |
| 203 | + } |
| 204 | + |
| 205 | + return data; |
| 206 | + } |
| 207 | + } |
| 208 | +} |
0 commit comments