summaryrefslogtreecommitdiff
path: root/tests/src/JIT/Performance/CodeQuality/SIMD
diff options
context:
space:
mode:
authorJiyoung Yun <jy910.yun@samsung.com>2016-11-23 19:09:09 +0900
committerJiyoung Yun <jy910.yun@samsung.com>2016-11-23 19:09:09 +0900
commit4b4aad7217d3292650e77eec2cf4c198ea9c3b4b (patch)
tree98110734c91668dfdbb126fcc0e15ddbd93738ca /tests/src/JIT/Performance/CodeQuality/SIMD
parentfa45f57ed55137c75ac870356a1b8f76c84b229c (diff)
downloadcoreclr-4b4aad7217d3292650e77eec2cf4c198ea9c3b4b.tar.gz
coreclr-4b4aad7217d3292650e77eec2cf4c198ea9c3b4b.tar.bz2
coreclr-4b4aad7217d3292650e77eec2cf4c198ea9c3b4b.zip
Imported Upstream version 1.1.0upstream/1.1.0
Diffstat (limited to 'tests/src/JIT/Performance/CodeQuality/SIMD')
-rw-r--r--tests/src/JIT/Performance/CodeQuality/SIMD/ConsoleMandel/Abstractions.cs138
-rw-r--r--tests/src/JIT/Performance/CodeQuality/SIMD/ConsoleMandel/ConsoleMandel.cs256
-rw-r--r--tests/src/JIT/Performance/CodeQuality/SIMD/ConsoleMandel/ConsoleMandel.csproj52
-rw-r--r--tests/src/JIT/Performance/CodeQuality/SIMD/ConsoleMandel/Interfaces.cs145
-rw-r--r--tests/src/JIT/Performance/CodeQuality/SIMD/ConsoleMandel/ScalarDouble.cs158
-rw-r--r--tests/src/JIT/Performance/CodeQuality/SIMD/ConsoleMandel/ScalarFloat.cs132
-rw-r--r--tests/src/JIT/Performance/CodeQuality/SIMD/ConsoleMandel/VectorDouble.cs212
-rw-r--r--tests/src/JIT/Performance/CodeQuality/SIMD/ConsoleMandel/VectorDoubleStrict.cs215
-rw-r--r--tests/src/JIT/Performance/CodeQuality/SIMD/ConsoleMandel/VectorFloat.cs214
-rw-r--r--tests/src/JIT/Performance/CodeQuality/SIMD/ConsoleMandel/VectorFloatStrict.cs191
-rw-r--r--tests/src/JIT/Performance/CodeQuality/SIMD/ConsoleMandel/VectorHelpers.cs38
-rw-r--r--tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/Camera.cs25
-rw-r--r--tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/Color.cs143
-rw-r--r--tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/ISect.cs16
-rw-r--r--tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/Light.cs13
-rw-r--r--tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/ObjectPool.cs68
-rw-r--r--tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/Plane.cs25
-rw-r--r--tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/ProducerConsumerCollectionBase.cs107
-rw-r--r--tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/Ray.cs13
-rw-r--r--tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/RayTracer.csproj58
-rw-r--r--tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/RayTracerBench.cs144
-rw-r--r--tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/Raytracer.cs196
-rw-r--r--tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/Scene.cs24
-rw-r--r--tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/SceneObject.cs14
-rw-r--r--tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/Sphere.cs38
-rw-r--r--tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/Surface.cs26
-rw-r--r--tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/Surfaces.cs44
-rw-r--r--tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/Vector.cs77
28 files changed, 2782 insertions, 0 deletions
diff --git a/tests/src/JIT/Performance/CodeQuality/SIMD/ConsoleMandel/Abstractions.cs b/tests/src/JIT/Performance/CodeQuality/SIMD/ConsoleMandel/Abstractions.cs
new file mode 100644
index 0000000000..8cfc0d1238
--- /dev/null
+++ b/tests/src/JIT/Performance/CodeQuality/SIMD/ConsoleMandel/Abstractions.cs
@@ -0,0 +1,138 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//
+
+using System;
+using System.Numerics;
+using System.Runtime.CompilerServices;
+
+namespace Algorithms
+{
+ // A float implementation of the BCL Complex type that only
+ // contains the bare essentials, plus a couple operations needed
+ // for efficient Mandelbrot calcuation.
+ internal struct ComplexFloat
+ {
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ public ComplexFloat(float real, float imaginary)
+ {
+ Real = real; Imaginary = imaginary;
+ }
+
+ public float Real;
+ public float Imaginary;
+
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ public ComplexFloat square()
+ {
+ return new ComplexFloat(Real * Real - Imaginary * Imaginary, 2.0f * Real * Imaginary);
+ }
+
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ public float sqabs()
+ {
+ return Real * Real + Imaginary * Imaginary;
+ }
+
+ public override string ToString()
+ {
+ return String.Format("[{0} + {1}Imaginary]", Real, Imaginary);
+ }
+
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ public static ComplexFloat operator +(ComplexFloat a, ComplexFloat b)
+ {
+ return new ComplexFloat(a.Real + b.Real, a.Imaginary + b.Imaginary);
+ }
+ }
+
+ // A couple extension methods that operate on BCL Complex types to help efficiently calculate
+ // the Mandelbrot set (They're instance methods on the ComplexFloat custom type)
+ public static partial class extensions
+ {
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ public static double sqabs(this Complex val)
+ {
+ return val.Real * val.Real + val.Imaginary * val.Imaginary;
+ }
+
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ public static Complex square(this Complex val)
+ {
+ return new Complex(val.Real * val.Real - val.Imaginary * val.Imaginary, 2.0 * val.Real * val.Imaginary);
+ }
+ }
+
+ // This is an implementation of ComplexFloat that operates on Vector<float> at a time SIMD types
+ internal struct ComplexVecFloat
+ {
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ public ComplexVecFloat(Vector<float> real, Vector<float> imaginary)
+ {
+ Real = real; Imaginary = imaginary;
+ }
+
+ public Vector<float> Real;
+ public Vector<float> Imaginary;
+
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ public ComplexVecFloat square()
+ {
+ return new ComplexVecFloat(Real * Real - Imaginary * Imaginary, Real * Imaginary + Real * Imaginary);
+ }
+
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ public Vector<float> sqabs()
+ {
+ return Real * Real + Imaginary * Imaginary;
+ }
+
+ public override string ToString()
+ {
+ return String.Format("[{0} + {1}Imaginary]", Real, Imaginary);
+ }
+
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ public static ComplexVecFloat operator +(ComplexVecFloat a, ComplexVecFloat b)
+ {
+ return new ComplexVecFloat(a.Real + b.Real, a.Imaginary + b.Imaginary);
+ }
+ }
+
+ // This is an implementation of Complex that operates on Vector<double> at a time SIMD types
+ internal struct ComplexVecDouble
+ {
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ public ComplexVecDouble(Vector<double> real, Vector<double> imaginary)
+ {
+ Real = real; Imaginary = imaginary;
+ }
+
+ public Vector<double> Real;
+ public Vector<double> Imaginary;
+
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ public ComplexVecDouble square()
+ {
+ return new ComplexVecDouble(Real * Real - Imaginary * Imaginary, Real * Imaginary + Real * Imaginary);
+ }
+
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ public Vector<double> sqabs()
+ {
+ return Real * Real + Imaginary * Imaginary;
+ }
+
+ public override string ToString()
+ {
+ return String.Format("[{0} + {1}Imaginary]", Real, Imaginary);
+ }
+
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ public static ComplexVecDouble operator +(ComplexVecDouble a, ComplexVecDouble b)
+ {
+ return new ComplexVecDouble(a.Real + b.Real, a.Imaginary + b.Imaginary);
+ }
+ }
+}
diff --git a/tests/src/JIT/Performance/CodeQuality/SIMD/ConsoleMandel/ConsoleMandel.cs b/tests/src/JIT/Performance/CodeQuality/SIMD/ConsoleMandel/ConsoleMandel.cs
new file mode 100644
index 0000000000..ed7f675700
--- /dev/null
+++ b/tests/src/JIT/Performance/CodeQuality/SIMD/ConsoleMandel/ConsoleMandel.cs
@@ -0,0 +1,256 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//
+
+using Microsoft.Xunit.Performance;
+using System;
+using System.Diagnostics;
+using Xunit;
+
+[assembly: OptimizeForBenchmarks]
+[assembly: MeasureInstructionsRetired]
+
+namespace ConsoleMandel
+{
+ public static class Program
+ {
+ private const int Pass = 100;
+ private const int Fail = -1;
+ private static bool s_silent = false;
+
+ private static void DoNothing(int x, int y, int count) { }
+
+ private static void DrawDot(int x, int y, int count)
+ {
+ if (x == 0)
+ Console.WriteLine();
+ Console.Write((count < 1000) ? ' ' : '*');
+ }
+
+ private static Algorithms.FractalRenderer.Render GetRenderer(Action<int, int, int> draw, int which)
+ {
+ return Algorithms.FractalRenderer.SelectRender(draw, Abort, IsVector(which), IsDouble(which), IsMulti(which), UsesADT(which), !UseIntTypes(which));
+ }
+
+ private static bool Abort() { return false; }
+
+ private static bool UseIntTypes(int num) { return (num & 8) == 0; }
+
+ private static bool IsVector(int num) { return num > 7; }
+
+ private static bool IsDouble(int num) { return (num & 4) != 0; }
+
+ private static bool IsMulti(int num) { return (num & 2) != 0; }
+
+ private static bool UsesADT(int num) { return (num & 1) != 0; }
+
+ private static void PrintDescription(int i)
+ {
+ Console.WriteLine("{0}: {1} {2}-Precision {3}Threaded using {4} and {5} int types", i,
+ IsVector(i) ? "Vector" : "Scalar",
+ IsDouble(i) ? "Double" : "Single",
+ IsMulti(i) ? "Multi" : "Single",
+ UsesADT(i) ? "ADT" : "Raw Values",
+ UseIntTypes(i) ? "using" : "not using any");
+ }
+
+ private static void PrintUsage()
+ {
+ Console.WriteLine("Usage:\n ConsoleMandel [0-23] -[bench #] where # is the number of iterations.");
+ for (int i = 0; i < 24; i++)
+ {
+ PrintDescription(i);
+ }
+ Console.WriteLine("The numeric argument selects the implementation number;");
+ Console.WriteLine("If not specified, all are run.");
+ Console.WriteLine("In non-benchmark mode, dump a text view of the Mandelbrot set.");
+ Console.WriteLine("In benchmark mode, a larger set is computed but nothing is dumped.");
+ }
+
+ private static int Main(string[] args)
+ {
+ try
+ {
+ int which = -1;
+ bool verbose = false;
+ bool bench = false;
+ int iters = 1;
+ int argNum = 0;
+ while (argNum < args.Length)
+ {
+ if (args[argNum].ToUpperInvariant() == "-BENCH")
+ {
+ bench = true;
+ if ((args.Length <= (argNum + 1)) || !Int32.TryParse(args[argNum + 1], out iters))
+ {
+ iters = 5;
+ }
+ argNum++;
+ }
+ else if (args[argNum].ToUpperInvariant() == "-V")
+ {
+ verbose = true;
+ }
+ else if (args[argNum].ToUpperInvariant() == "-S")
+ {
+ s_silent = true;
+ }
+ else if (!Int32.TryParse(args[argNum], out which))
+ {
+ PrintUsage();
+ return Fail;
+ }
+ argNum++;
+ }
+ if (bench)
+ {
+ Bench(iters, which);
+ return Pass;
+ }
+ if (which == -1)
+ {
+ PrintUsage();
+ return Pass;
+ }
+ if (verbose)
+ {
+ PrintDescription(which);
+ }
+ if (IsVector(which))
+ {
+ if (verbose)
+ {
+ Console.WriteLine(" Vector Count is {0}", IsDouble(which) ? System.Numerics.Vector<Double>.Count : System.Numerics.Vector<Single>.Count);
+ Console.WriteLine(" {0} Accelerated.", System.Numerics.Vector.IsHardwareAccelerated ? "IS" : "IS NOT");
+ }
+ }
+ var render = GetRenderer(DrawDot, which);
+ render(-1.5f, .5f, -1f, 1f, 2.0f / 60.0f);
+ return Pass;
+ }
+ catch (System.Exception)
+ {
+ return Fail;
+ }
+ }
+
+ public static void Bench(int iters, int which)
+ {
+ float XC = -1.248f;
+ float YC = -.0362f;
+ float Range = .001f;
+ float xmin = XC - Range;
+ float xmax = XC + Range;
+ float ymin = YC - Range;
+ float ymax = YC + Range;
+ float step = Range / 1000f; // This will render one million pixels
+ float warm = Range / 100f; // To warm up, just render 10000 pixels :-)
+ Algorithms.FractalRenderer.Render[] renderers = new Algorithms.FractalRenderer.Render[24];
+ // Warm up each renderer
+ if (!s_silent)
+ {
+ Console.WriteLine("Warming up...");
+ }
+ Stopwatch timer = new Stopwatch();
+ int firstRenderer = (which == -1) ? 0 : which;
+ int lastRenderer = (which == -1) ? (renderers.Length - 1) : which;
+ for (int i = firstRenderer; i <= lastRenderer; i++)
+ {
+ renderers[i] = GetRenderer(DoNothing, i);
+ timer.Restart();
+ renderers[i](xmin, xmax, ymin, ymax, warm);
+ timer.Stop();
+ if (!s_silent)
+ {
+ Console.WriteLine("{0}{1}{2}{3}{4} Complete [{5} ms]",
+ UseIntTypes(i) ? "IntBV " : "Strict ",
+ IsVector(i) ? "Vector " : "Scalar ",
+ IsDouble(i) ? "Double " : "Single ",
+ UsesADT(i) ? "ADT " : "Raw ",
+ IsMulti(i) ? "Multi " : "Single ",
+ timer.ElapsedMilliseconds);
+ }
+ }
+ if (!s_silent)
+ {
+ Console.WriteLine(" Run Type : Min Max Average Std-Dev");
+ }
+ for (int i = firstRenderer; i <= lastRenderer; i++)
+ {
+ long totalTime = 0;
+ long min = long.MaxValue;
+ long max = long.MinValue;
+ for (int count = 0; count < iters; count++)
+ {
+ timer.Restart();
+ renderers[i](xmin, xmax, ymin, ymax, step);
+ timer.Stop();
+ long time = timer.ElapsedMilliseconds;
+ max = Math.Max(time, max);
+ min = Math.Min(time, min);
+ totalTime += time;
+ }
+ double avg = totalTime / (double)iters;
+ double stdDev = Math.Sqrt(totalTime / (iters - 1.0)) / avg;
+ if (s_silent)
+ {
+ Console.WriteLine("Average: {0,0:0.0}", avg);
+ }
+ else
+ {
+ Console.WriteLine("{0}{1}{2}{3}{4}: {5,8} {6,8} {7,10:0.0} {8,10:P}",
+ UseIntTypes(i) ? "IntBV " : "Strict ",
+ IsVector(i) ? "Vector " : "Scalar ",
+ IsDouble(i) ? "Double " : "Single ",
+ UsesADT(i) ? "ADT " : "Raw ",
+ IsMulti(i) ? "Multi " : "Single ",
+ min, max, avg, stdDev);
+ }
+ }
+ }
+
+ public static void XBench(int iters, int which)
+ {
+ float XC = -1.248f;
+ float YC = -.0362f;
+ float Range = .001f;
+ float xmin = XC - Range;
+ float xmax = XC + Range;
+ float ymin = YC - Range;
+ float ymax = YC + Range;
+ float step = Range / 100f;
+
+ Algorithms.FractalRenderer.Render renderer = GetRenderer(DoNothing, which);
+
+ for (int count = 0; count < iters; count++)
+ {
+ renderer(xmin, xmax, ymin, ymax, step);
+ }
+ }
+
+ [Benchmark]
+ public static void VectorFloatSinglethreadRawNoInt()
+ {
+ foreach (var iteration in Benchmark.Iterations)
+ {
+ using (iteration.StartMeasurement())
+ {
+ XBench(10, 8);
+ }
+ }
+ }
+
+ [Benchmark]
+ public static void VectorFloatSinglethreadADTNoInt()
+ {
+ foreach (var iteration in Benchmark.Iterations)
+ {
+ using (iteration.StartMeasurement())
+ {
+ XBench(10, 9);
+ }
+ }
+ }
+ }
+}
diff --git a/tests/src/JIT/Performance/CodeQuality/SIMD/ConsoleMandel/ConsoleMandel.csproj b/tests/src/JIT/Performance/CodeQuality/SIMD/ConsoleMandel/ConsoleMandel.csproj
new file mode 100644
index 0000000000..73a4c99541
--- /dev/null
+++ b/tests/src/JIT/Performance/CodeQuality/SIMD/ConsoleMandel/ConsoleMandel.csproj
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" />
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <SchemaVersion>2.0</SchemaVersion>
+ <ProjectGuid>{95DFC527-4DC1-495E-97D7-E94EE1F7140D}</ProjectGuid>
+ <OutputType>Exe</OutputType>
+ <AppDesignerFolder>Properties</AppDesignerFolder>
+ <FileAlignment>512</FileAlignment>
+ <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+ <ReferencePath>$(ProgramFiles)\Common Files\microsoft shared\VSTT\11.0\UITestExtensionPackages</ReferencePath>
+ <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir>
+ <NuGetPackageImportStamp>7a9bfb7d</NuGetPackageImportStamp>
+ <NuGetTargetMoniker>.NETStandard,Version=v1.4</NuGetTargetMoniker>
+ </PropertyGroup>
+ <!-- Default configurations to help VS understand the configurations -->
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+ <DebugType>pdbonly</DebugType>
+ <Optimize>true</Optimize>
+ </PropertyGroup>
+ <ItemGroup>
+ <CodeAnalysisDependentAssemblyPaths Condition=" '$(VS100COMNTOOLS)' != '' " Include="$(VS100COMNTOOLS)..\IDE\PrivateAssemblies">
+ <Visible>False</Visible>
+ </CodeAnalysisDependentAssemblyPaths>
+ </ItemGroup>
+ <ItemGroup>
+ <Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="ConsoleMandel.cs" />
+ <Compile Include="Abstractions.cs" />
+ <Compile Include="Interfaces.cs" />
+ <Compile Include="ScalarDouble.cs" />
+ <Compile Include="ScalarFloat.cs" />
+ <Compile Include="VectorDouble.cs" />
+ <Compile Include="VectorDoubleStrict.cs" />
+ <Compile Include="VectorFloat.cs" />
+ <Compile Include="VectorFloatStrict.cs" />
+ <Compile Include="VectorHelpers.cs" />
+ </ItemGroup>
+ <PropertyGroup>
+ <ProjectJson>$(JitPackagesConfigFileDirectory)benchmark\project.json</ProjectJson>
+ <ProjectLockJson>$(JitPackagesConfigFileDirectory)benchmark\project.lock.json</ProjectLockJson>
+ </PropertyGroup>
+ <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
+ <PropertyGroup Condition=" '$(MsBuildProjectDirOverride)' != '' ">
+ </PropertyGroup>
+</Project>
diff --git a/tests/src/JIT/Performance/CodeQuality/SIMD/ConsoleMandel/Interfaces.cs b/tests/src/JIT/Performance/CodeQuality/SIMD/ConsoleMandel/Interfaces.cs
new file mode 100644
index 0000000000..dda2690e3d
--- /dev/null
+++ b/tests/src/JIT/Performance/CodeQuality/SIMD/ConsoleMandel/Interfaces.cs
@@ -0,0 +1,145 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//
+
+using System;
+
+namespace Algorithms
+{
+ public abstract class FractalRenderer
+ {
+ public delegate void Render(float xmin, float xmax, float ymin, float ymax, float step);
+
+ private Func<bool> _abort;
+ private Action<int, int, int> _drawPixel;
+ protected const int max_iters = 1000; // Make this higher to see more detail when zoomed in (and slow down rendering a lot)
+
+ protected FractalRenderer(Action<int, int, int> draw, Func<bool> checkAbort)
+ {
+ _drawPixel = draw; _abort = checkAbort;
+ }
+
+ protected Action<int, int, int> DrawPixel { get { return _drawPixel; } }
+
+ public bool Abort { get { return _abort(); } }
+
+ public static Render SelectRender(Action<int, int, int> draw, Func<bool> abort, bool useVectorTypes, bool doublePrecision, bool isMultiThreaded, bool useAbstractDataType, bool dontUseIntTypes = true)
+ {
+ if (useVectorTypes && doublePrecision)
+ {
+ if (dontUseIntTypes)
+ {
+ var r = new VectorDoubleStrictRenderer(draw, abort);
+ if (isMultiThreaded)
+ {
+ if (useAbstractDataType)
+ return r.RenderMultiThreadedWithADT;
+ else // !useAbstractDataType
+ return r.RenderMultiThreadedNoADT;
+ }
+ else // !isMultiThreaded
+ {
+ if (useAbstractDataType)
+ return r.RenderSingleThreadedWithADT;
+ else // !useAbstractDataType
+ return r.RenderSingleThreadedNoADT;
+ }
+ }
+ else // !dontUseIntTypes
+ {
+ var r = new VectorDoubleRenderer(draw, abort);
+ if (isMultiThreaded)
+ {
+ if (useAbstractDataType)
+ return r.RenderMultiThreadedWithADT;
+ else // !useAbstractDataType
+ return r.RenderMultiThreadedNoADT;
+ }
+ else // !isMultiThreaded
+ {
+ if (useAbstractDataType)
+ return r.RenderSingleThreadedWithADT;
+ else // !useAbstractDataType
+ return r.RenderSingleThreadedNoADT;
+ }
+ }
+ }
+ else if (useVectorTypes && !doublePrecision)
+ {
+ if (dontUseIntTypes)
+ {
+ var r = new VectorFloatStrictRenderer(draw, abort);
+ if (isMultiThreaded)
+ {
+ if (useAbstractDataType)
+ return r.RenderMultiThreadedWithADT;
+ else // !useAbstractDataType
+ return r.RenderMultiThreadedNoADT;
+ }
+ else // !isMultiThreaded
+ {
+ if (useAbstractDataType)
+ return r.RenderSingleThreadedWithADT;
+ else // !useAbstractDataType
+ return r.RenderSingleThreadedNoADT;
+ }
+ }
+ else // !dontUseIntTypes
+ {
+ var r = new VectorFloatRenderer(draw, abort);
+ if (isMultiThreaded)
+ {
+ if (useAbstractDataType)
+ return r.RenderMultiThreadedWithADT;
+ else // !useAbstractDataType
+ return r.RenderMultiThreadedNoADT;
+ }
+ else // !isMultiThreaded
+ {
+ if (useAbstractDataType)
+ return r.RenderSingleThreadedWithADT;
+ else // !useAbstractDataType
+ return r.RenderSingleThreadedNoADT;
+ }
+ }
+ }
+ else if (!useVectorTypes && doublePrecision)
+ {
+ var r = new ScalarDoubleRenderer(draw, abort);
+ if (isMultiThreaded)
+ {
+ if (useAbstractDataType)
+ return r.RenderMultiThreadedWithADT;
+ else // !useAbstractDataType
+ return r.RenderMultiThreadedNoADT;
+ }
+ else // !isMultiThreaded
+ {
+ if (useAbstractDataType)
+ return r.RenderSingleThreadedWithADT;
+ else // !useAbstractDataType
+ return r.RenderSingleThreadedNoADT;
+ }
+ }
+ else // (!useVectorTypes && !doublePrecision)
+ {
+ var r = new ScalarFloatRenderer(draw, abort);
+ if (isMultiThreaded)
+ {
+ if (useAbstractDataType)
+ return r.RenderMultiThreadedWithADT;
+ else // !useAbstractDataType
+ return r.RenderMultiThreadedNoADT;
+ }
+ else // !isMultiThreaded
+ {
+ if (useAbstractDataType)
+ return r.RenderSingleThreadedWithADT;
+ else // !useAbstractDataType
+ return r.RenderSingleThreadedNoADT;
+ }
+ }
+ }
+ }
+}
diff --git a/tests/src/JIT/Performance/CodeQuality/SIMD/ConsoleMandel/ScalarDouble.cs b/tests/src/JIT/Performance/CodeQuality/SIMD/ConsoleMandel/ScalarDouble.cs
new file mode 100644
index 0000000000..e4ef4fbdf2
--- /dev/null
+++ b/tests/src/JIT/Performance/CodeQuality/SIMD/ConsoleMandel/ScalarDouble.cs
@@ -0,0 +1,158 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//
+
+using System;
+using System.Numerics;
+using System.Threading.Tasks;
+
+namespace Algorithms
+{
+ // This class contains renderers that use scalar doubles
+ internal class ScalarDoubleRenderer : FractalRenderer
+ {
+ public ScalarDoubleRenderer(Action<int, int, int> dp, Func<bool> abortFunc)
+ : base(dp, abortFunc)
+ {
+ }
+
+ protected const double limit = 4.0;
+
+ // Render the fractal using the BCL Complex data type abstraction on a single thread with scalar doubles
+ public void RenderSingleThreadedWithADT(float xminf, float xmaxf, float yminf, float ymaxf, float stepf)
+ {
+ double xmin = (double)xminf;
+ double xmax = (double)xmaxf;
+ double ymin = (double)yminf;
+ double ymax = (double)ymaxf;
+ double step = (double)stepf;
+
+ int yp = 0;
+ for (double y = ymin; y < ymax && !Abort; y += step, yp++)
+ {
+ int xp = 0;
+ for (double x = xmin; x < xmax; x += step, xp++)
+ {
+ Complex num = new Complex(x, y);
+ Complex accum = num;
+ int iters = 0;
+ double sqabs = 0f;
+ do
+ {
+ accum = accum.square();
+ accum += num;
+ iters++;
+ sqabs = accum.sqabs();
+ } while (sqabs < limit && iters < max_iters);
+
+ DrawPixel(xp, yp, iters);
+ }
+ }
+ }
+
+ // Render the fractal with no data type abstraction on a single thread with scalar doubles
+ public void RenderSingleThreadedNoADT(float xminf, float xmaxf, float yminf, float ymaxf, float stepf)
+ {
+ double xmin = (double)xminf;
+ double xmax = (double)xmaxf;
+ double ymin = (double)yminf;
+ double ymax = (double)ymaxf;
+ double step = (double)stepf;
+
+ int yp = 0;
+ for (double y = ymin; y < ymax && !Abort; y += step, yp++)
+ {
+ int xp = 0;
+ for (double x = xmin; x < xmax; x += step, xp++)
+ {
+ double accumx = x;
+ double accumy = y;
+ int iters = 0;
+ double sqabs = 0.0;
+ do
+ {
+ double naccumx = accumx * accumx - accumy * accumy;
+ double naccumy = 2.0 * accumx * accumy;
+ accumx = naccumx + x;
+ accumy = naccumy + y;
+ iters++;
+ sqabs = accumx * accumx + accumy * accumy;
+ } while (sqabs < limit && iters < max_iters);
+
+ DrawPixel(xp, yp, iters);
+ }
+ }
+ }
+
+ // Render the fractal using the BCL Complex data type abstraction on multiple threads with scalar doubles
+ public void RenderMultiThreadedWithADT(float xminf, float xmaxf, float yminf, float ymaxf, float stepf)
+ {
+ double xmin = (double)xminf;
+ double xmax = (double)xmaxf;
+ double ymin = (double)yminf;
+ double ymax = (double)ymaxf;
+ double step = (double)stepf;
+
+ Parallel.For(0, (int)(((ymax - ymin) / step) + .5), (yp) =>
+ {
+ if (Abort)
+ return;
+ double y = ymin + step * yp;
+ int xp = 0;
+ for (double x = xmin; x < xmax; x += step, xp++)
+ {
+ Complex num = new Complex(x, y);
+ Complex accum = num;
+ int iters = 0;
+ double sqabs = 0f;
+ do
+ {
+ accum = accum.square();
+ accum += num;
+ iters++;
+ sqabs = accum.sqabs();
+ } while (sqabs < limit && iters < max_iters);
+
+ DrawPixel(xp, yp, iters);
+ }
+ });
+ }
+
+ // Render the fractal with no data type abstraction on multiple threads with scalar doubles
+ public void RenderMultiThreadedNoADT(float xmind, float xmaxd, float ymind, float ymaxd, float stepd)
+ {
+ double xmin = (double)xmind;
+ double xmax = (double)xmaxd;
+ double ymin = (double)ymind;
+ double ymax = (double)ymaxd;
+ double step = (double)stepd;
+
+ Parallel.For(0, (int)(((ymax - ymin) / step) + .5), (yp) =>
+ {
+ if (Abort)
+ return;
+ double y = ymin + step * yp;
+ int xp = 0;
+ for (double x = xmin; x < xmax; x += step, xp++)
+ {
+ double accumx = x;
+ double accumy = y;
+ int iters = 0;
+ double sqabs = 0.0;
+ do
+ {
+ double naccumx = accumx * accumx - accumy * accumy;
+ double naccumy = 2.0 * accumx * accumy;
+ accumx = naccumx + x;
+ accumy = naccumy + y;
+ iters++;
+ sqabs = accumx * accumx + accumy * accumy;
+ } while (sqabs < limit && iters < max_iters);
+
+ DrawPixel(xp, yp, iters);
+ }
+ });
+ }
+ }
+}
diff --git a/tests/src/JIT/Performance/CodeQuality/SIMD/ConsoleMandel/ScalarFloat.cs b/tests/src/JIT/Performance/CodeQuality/SIMD/ConsoleMandel/ScalarFloat.cs
new file mode 100644
index 0000000000..a7f17385aa
--- /dev/null
+++ b/tests/src/JIT/Performance/CodeQuality/SIMD/ConsoleMandel/ScalarFloat.cs
@@ -0,0 +1,132 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//
+
+using System;
+using System.Threading.Tasks;
+
+namespace Algorithms
+{
+ // This class contains renderers that use scalar floats
+ internal class ScalarFloatRenderer : FractalRenderer
+ {
+ public ScalarFloatRenderer(Action<int, int, int> dp, Func<bool> abortFunc)
+ : base(dp, abortFunc)
+ {
+ }
+
+ protected const float limit = 4.0f;
+
+ // Render the fractal using a Complex data type on a single thread with scalar floats
+ public void RenderSingleThreadedWithADT(float xmin, float xmax, float ymin, float ymax, float step)
+ {
+ int yp = 0;
+ for (float y = ymin; y < ymax && !Abort; y += step, yp++)
+ {
+ int xp = 0;
+ for (float x = xmin; x < xmax; x += step, xp++)
+ {
+ ComplexFloat num = new ComplexFloat(x, y);
+ ComplexFloat accum = num;
+ int iters = 0;
+ float sqabs = 0f;
+ do
+ {
+ accum = accum.square();
+ accum += num;
+ iters++;
+ sqabs = accum.sqabs();
+ } while (sqabs < limit && iters < max_iters);
+
+ DrawPixel(xp, yp, iters);
+ }
+ }
+ }
+
+ // Render the fractal with no data type abstraction on a single thread with scalar floats
+ public void RenderSingleThreadedNoADT(float xmin, float xmax, float ymin, float ymax, float step)
+ {
+ int yp = 0;
+ for (float y = ymin; y < ymax && !Abort; y += step, yp++)
+ {
+ int xp = 0;
+ for (float x = xmin; x < xmax; x += step, xp++)
+ {
+ float accumx = x;
+ float accumy = y;
+ int iters = 0;
+ float sqabs = 0f;
+ do
+ {
+ float naccumx = accumx * accumx - accumy * accumy;
+ float naccumy = 2.0f * accumx * accumy;
+ accumx = naccumx + x;
+ accumy = naccumy + y;
+ iters++;
+ sqabs = accumx * accumx + accumy * accumy;
+ } while (sqabs < limit && iters < max_iters);
+ DrawPixel(xp, yp, iters);
+ }
+ }
+ }
+
+ // Render the fractal using a Complex data type on a single thread with scalar floats
+ public void RenderMultiThreadedWithADT(float xmin, float xmax, float ymin, float ymax, float step)
+ {
+ Parallel.For(0, (int)(((ymax - ymin) / step) + .5f), (yp) =>
+ {
+ if (Abort)
+ return;
+ float y = ymin + step * yp;
+ int xp = 0;
+ for (float x = xmin; x < xmax; x += step, xp++)
+ {
+ ComplexFloat num = new ComplexFloat(x, y);
+ ComplexFloat accum = num;
+ int iters = 0;
+ float sqabs = 0f;
+ do
+ {
+ accum = accum.square();
+ accum += num;
+ iters++;
+ sqabs = accum.sqabs();
+ } while (sqabs < limit && iters < max_iters);
+
+ DrawPixel(xp, yp, iters);
+ }
+ });
+ }
+
+ // Render the fractal with no data type abstraction on multiple threads with scalar floats
+ public void RenderMultiThreadedNoADT(float xmin, float xmax, float ymin, float ymax, float step)
+ {
+ Parallel.For(0, (int)(((ymax - ymin) / step) + .5f), (yp) =>
+ {
+ if (Abort)
+ return;
+ float y = ymin + step * yp;
+ int xp = 0;
+ for (float x = xmin; x < xmax; x += step, xp++)
+ {
+ float accumx = x;
+ float accumy = y;
+ int iters = 0;
+ float sqabs = 0f;
+ do
+ {
+ float naccumx = accumx * accumx - accumy * accumy;
+ float naccumy = 2.0f * accumx * accumy;
+ accumx = naccumx + x;
+ accumy = naccumy + y;
+ iters++;
+ sqabs = accumx * accumx + accumy * accumy;
+ } while (sqabs < limit && iters < max_iters);
+
+ DrawPixel(xp, yp, iters);
+ }
+ });
+ }
+ }
+}
diff --git a/tests/src/JIT/Performance/CodeQuality/SIMD/ConsoleMandel/VectorDouble.cs b/tests/src/JIT/Performance/CodeQuality/SIMD/ConsoleMandel/VectorDouble.cs
new file mode 100644
index 0000000000..0d57e0b4bc
--- /dev/null
+++ b/tests/src/JIT/Performance/CodeQuality/SIMD/ConsoleMandel/VectorDouble.cs
@@ -0,0 +1,212 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//
+
+using System;
+using System.Numerics;
+using System.Threading.Tasks;
+
+namespace Algorithms
+{
+ // This contains renderers that only use Vector<double>'s and Vector<long> types.
+ internal class VectorDoubleRenderer : FractalRenderer
+ {
+ private const double limit = 4.0;
+
+ private static Vector<double> s_dummy = Vector<double>.One;
+
+ public VectorDoubleRenderer(Action<int, int, int> dp, Func<bool> abortFunc)
+ : base(dp, abortFunc)
+ {
+ }
+
+ // Render the fractal on a single thread using raw Vector<double> data types
+ // For a well commented version, go see VectorFloatRenderer.RenderSingleThreadedWithADT in VectorFloat.cs
+ public void RenderSingleThreadedNoADT(float xminf, float xmaxf, float yminf, float ymaxf, float stepf)
+ {
+ double xmin = (double)xminf;
+ double xmax = (double)xmaxf;
+ double ymin = (double)yminf;
+ double ymax = (double)ymaxf;
+ double step = (double)stepf;
+
+ Vector<double> vlimit = new Vector<double>(limit);
+ Vector<double> vinc = new Vector<double>((double)Vector<double>.Count * step);
+ Vector<double> vstep = new Vector<double>(step);
+ Vector<long> vmax_iters = new Vector<long>(max_iters);
+ Vector<double> vxmin = VectorHelper.Create(i => xmin + step * i);
+
+ double y = ymin;
+ int yp = 0;
+ for (Vector<double> vy = new Vector<double>(ymin); y <= ymax && !Abort; vy += vstep, y += step, yp++)
+ {
+ int xp = 0;
+ Vector<double> vxmaxd = new Vector<double>(xmax);
+ for (Vector<double> vx = vxmin; Vector.LessThanOrEqualAny(vx, vxmaxd); vx += vinc, xp += Vector<long>.Count)
+ {
+ Vector<double> accumx = vx;
+ Vector<double> accumy = vy;
+
+ Vector<long> viters = Vector<long>.Zero;
+ Vector<long> increment = Vector<long>.One;
+
+ do
+ {
+ Vector<double> naccumx = accumx * accumx - accumy * accumy;
+ Vector<double> naccumy = accumx * accumy + accumx * accumy;
+ accumx = naccumx + vx;
+ accumy = naccumy + vy;
+ viters += increment;
+ Vector<double> sqabs = accumx * accumx + accumy * accumy;
+ Vector<long> vCond = Vector.LessThanOrEqual(sqabs, vlimit) &
+ Vector.LessThanOrEqual(viters, vmax_iters);
+ increment = increment & vCond;
+ } while (increment != Vector<long>.Zero);
+
+ viters.ForEach((iter, elemNum) => DrawPixel(xp + elemNum, yp, (int)iter));
+ }
+ }
+ }
+
+ // Render the fractal on a single thread using the ComplexVecDouble data type
+ // For a well commented version, go see VectorFloatRenderer.RenderSingleThreadedWithADT in VectorFloat.cs
+ public void RenderSingleThreadedWithADT(float xminf, float xmaxf, float yminf, float ymaxf, float stepf)
+ {
+ double xmin = (double)xminf;
+ double xmax = (double)xmaxf;
+ double ymin = (double)yminf;
+ double ymax = (double)ymaxf;
+ double step = (double)stepf;
+
+ Vector<double> vlimit = new Vector<double>(limit);
+ Vector<double> vinc = new Vector<double>((double)Vector<double>.Count * step);
+ Vector<double> vstep = new Vector<double>(step);
+ Vector<long> vmax_iters = new Vector<long>(max_iters);
+ Vector<double> vxmax = new Vector<double>(xmax);
+ Vector<double> vxmin = VectorHelper.Create(i => xmin + step * i);
+
+ double y = ymin;
+ int yp = 0;
+ for (Vector<double> vy = new Vector<double>(ymin); y <= ymax && !Abort; vy += vstep, y += step, yp++)
+ {
+ int xp = 0;
+ for (Vector<double> vx = vxmin; Vector.LessThanOrEqualAny(vx, vxmax); vx += vinc, xp += Vector<long>.Count)
+ {
+ ComplexVecDouble num = new ComplexVecDouble(vx, vy);
+ ComplexVecDouble accum = num;
+
+ Vector<long> viters = Vector<long>.Zero;
+ Vector<long> increment = Vector<long>.One;
+
+ do
+ {
+ accum = accum.square() + num;
+ viters += increment;
+ Vector<long> vCond = Vector.LessThanOrEqual(accum.sqabs(), vlimit) &
+ Vector.LessThanOrEqual(viters, vmax_iters);
+ increment = increment & vCond;
+ } while (increment != Vector<long>.Zero);
+
+ viters.ForEach((iter, elemNum) => DrawPixel(xp + elemNum, yp, (int)iter));
+ }
+ }
+ }
+
+ // Render the fractal on multiple threads using raw Vector<double> data types
+ // For a well commented version, go see VectorFloatRenderer.RenderSingleThreadedWithADT in VectorFloat.cs
+ public void RenderMultiThreadedNoADT(float xminf, float xmaxf, float yminf, float ymaxf, float stepf)
+ {
+ double xmin = (double)xminf;
+ double xmax = (double)xmaxf;
+ double ymin = (double)yminf;
+ double ymax = (double)ymaxf;
+ double step = (double)stepf;
+
+ Vector<long> vmax_iters = new Vector<long>(max_iters);
+ Vector<double> vlimit = new Vector<double>(limit);
+ Vector<double> vinc = new Vector<double>((double)Vector<double>.Count * step);
+ Vector<double> vxmax = new Vector<double>(xmax);
+ Vector<double> vstep = new Vector<double>(step);
+ Vector<double> vxmin = VectorHelper.Create(i => xmin + step * i);
+
+ Parallel.For(0, (int)(((ymax - ymin) / step) + .5), (yp) =>
+ {
+ if (Abort)
+ return;
+
+ Vector<double> vy = new Vector<double>(ymin + step * yp);
+ int xp = 0;
+ for (Vector<double> vx = vxmin; Vector.LessThanOrEqualAny(vx, vxmax); vx += vinc, xp += Vector<long>.Count)
+ {
+ Vector<double> accumx = vx;
+ Vector<double> accumy = vy;
+
+ Vector<long> viters = Vector<long>.Zero;
+ Vector<long> increment = Vector<long>.One;
+
+ do
+ {
+ Vector<double> naccumx = accumx * accumx - accumy * accumy;
+ Vector<double> naccumy = accumx * accumy + accumx * accumy;
+ accumx = naccumx + vx;
+ accumy = naccumy + vy;
+ viters += increment;
+ Vector<double> sqabs = accumx * accumx + accumy * accumy;
+ Vector<long> vCond = Vector.LessThanOrEqual(sqabs, vlimit) &
+ Vector.LessThanOrEqual(viters, vmax_iters);
+ increment = increment & vCond;
+ } while (increment != Vector<long>.Zero);
+
+ viters.ForEach((iter, elemNum) => DrawPixel(xp + elemNum, yp, (int)iter));
+ }
+ });
+ }
+
+ // Render the fractal on multiple threads using the ComplexVecDouble data type
+ // For a well commented version, go see VectorFloatRenderer.RenderSingleThreadedWithADT in VectorFloat.cs
+ public void RenderMultiThreadedWithADT(float xminf, float xmaxf, float yminf, float ymaxf, float stepf)
+ {
+ double xmin = (double)xminf;
+ double xmax = (double)xmaxf;
+ double ymin = (double)yminf;
+ double ymax = (double)ymaxf;
+ double step = (double)stepf;
+
+ Vector<long> vmax_iters = new Vector<long>(max_iters);
+ Vector<double> vlimit = new Vector<double>(limit);
+ Vector<double> vinc = new Vector<double>((double)Vector<double>.Count * step);
+ Vector<double> vxmax = new Vector<double>(xmax);
+ Vector<double> vstep = new Vector<double>(step);
+ Vector<double> vxmin = VectorHelper.Create(i => xmin + step * i);
+
+ Parallel.For(0, (int)(((ymax - ymin) / step) + .5), (yp) =>
+ {
+ if (Abort)
+ return;
+
+ Vector<double> vy = new Vector<double>(ymin + step * yp);
+ int xp = 0;
+ for (Vector<double> vx = vxmin; Vector.LessThanOrEqualAny(vx, vxmax); vx += vinc, xp += Vector<long>.Count)
+ {
+ ComplexVecDouble num = new ComplexVecDouble(vx, vy);
+ ComplexVecDouble accum = num;
+
+ Vector<long> viters = Vector<long>.Zero;
+ Vector<long> increment = Vector<long>.One;
+
+ do
+ {
+ accum = accum.square() + num;
+ viters += increment;
+ Vector<long> vCond = Vector.LessThanOrEqual(accum.sqabs(), vlimit) &
+ Vector.LessThanOrEqual(viters, vmax_iters);
+ increment = increment & vCond;
+ } while (increment != Vector<long>.Zero);
+
+ viters.ForEach((iter, elemNum) => DrawPixel(xp + elemNum, yp, (int)iter));
+ }
+ });
+ }
+ }
+}
diff --git a/tests/src/JIT/Performance/CodeQuality/SIMD/ConsoleMandel/VectorDoubleStrict.cs b/tests/src/JIT/Performance/CodeQuality/SIMD/ConsoleMandel/VectorDoubleStrict.cs
new file mode 100644
index 0000000000..a6099c7821
--- /dev/null
+++ b/tests/src/JIT/Performance/CodeQuality/SIMD/ConsoleMandel/VectorDoubleStrict.cs
@@ -0,0 +1,215 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//
+
+using System;
+using System.Numerics;
+using System.Threading.Tasks;
+
+namespace Algorithms
+{
+ // This contains renderers that only use Vector<double>'s with no Vector<long> types. It's
+ // primarily useful when targeting AVX (not AVX2), because AVX doesn't support 256 bits of
+ // integer values, only floating point values.
+ internal class VectorDoubleStrictRenderer : FractalRenderer
+ {
+ private const double limit = 4.0;
+
+ private static Vector<double> s_dummy;
+
+ static VectorDoubleStrictRenderer()
+ {
+ s_dummy = Vector<double>.One;
+ }
+
+ public VectorDoubleStrictRenderer(Action<int, int, int> dp, Func<bool> abortFunc)
+ : base(dp, abortFunc)
+ {
+ }
+
+ // Render the fractal on multiple threads using the ComplexVecDouble data type
+ // For a well commented version, go see VectorFloatRenderer.RenderSingleThreadedWithADT in VectorFloat.cs
+ public void RenderMultiThreadedWithADT(float xminf, float xmaxf, float yminf, float ymaxf, float stepf)
+ {
+ double xmin = (double)xminf;
+ double xmax = (double)xmaxf;
+ double ymin = (double)yminf;
+ double ymax = (double)ymaxf;
+ double step = (double)stepf;
+
+ Vector<double> vmax_iters = new Vector<double>((double)max_iters);
+ Vector<double> vlimit = new Vector<double>(limit);
+ Vector<double> vstep = new Vector<double>(step);
+ Vector<double> vinc = new Vector<double>((double)Vector<double>.Count * step);
+ Vector<double> vxmax = new Vector<double>(xmax);
+ Vector<double> vxmin = VectorHelper.Create(i => xmin + step * i);
+
+ Parallel.For(0, (int)(((ymax - ymin) / step) + .5), (yp) =>
+ {
+ if (Abort)
+ return;
+
+ Vector<double> vy = new Vector<double>(ymin + step * yp);
+ int xp = 0;
+ for (Vector<double> vx = vxmin; Vector.LessThanOrEqualAny(vx, vxmax); vx += vinc, xp += Vector<double>.Count)
+ {
+ ComplexVecDouble num = new ComplexVecDouble(vx, vy);
+ ComplexVecDouble accum = num;
+
+ Vector<double> viters = Vector<double>.Zero;
+ Vector<double> increment = Vector<double>.One;
+ do
+ {
+ accum = accum.square() + num;
+ viters += increment;
+ Vector<double> vCond = Vector.LessThanOrEqual<double>(accum.sqabs(), vlimit) &
+ Vector.LessThanOrEqual<double>(viters, vmax_iters);
+ increment = increment & vCond;
+ } while (increment != Vector<double>.Zero);
+
+ viters.ForEach((iter, elemNum) => DrawPixel(xp + elemNum, yp, (int)iter));
+ }
+ });
+ }
+
+ // Render the fractal on multiple threads using raw Vector<double> data types
+ // For a well commented version, go see VectorFloatRenderer.RenderSingleThreadedWithADT in VectorFloat.cs
+ public void RenderMultiThreadedNoADT(float xminf, float xmaxf, float yminf, float ymaxf, float stepf)
+ {
+ double xmin = (double)xminf;
+ double xmax = (double)xmaxf;
+ double ymin = (double)yminf;
+ double ymax = (double)ymaxf;
+ double step = (double)stepf;
+
+ Vector<double> vmax_iters = new Vector<double>((double)max_iters);
+ Vector<double> vlimit = new Vector<double>(limit);
+ Vector<double> vstep = new Vector<double>(step);
+ Vector<double> vinc = new Vector<double>((double)Vector<double>.Count * step);
+ Vector<double> vxmax = new Vector<double>(xmax);
+ Vector<double> vxmin = VectorHelper.Create(i => xmin + step * i);
+
+ Parallel.For(0, (int)(((ymax - ymin) / step) + .5), (yp) =>
+ {
+ if (Abort)
+ return;
+
+ Vector<double> vy = new Vector<double>(ymin + step * yp);
+ int xp = 0;
+ for (Vector<double> vx = vxmin; Vector.LessThanOrEqualAny(vx, vxmax); vx += vinc, xp += Vector<double>.Count)
+ {
+ Vector<double> accumx = vx;
+ Vector<double> accumy = vy;
+
+ Vector<double> viters = Vector<double>.Zero;
+ Vector<double> increment = Vector<double>.One;
+ do
+ {
+ Vector<double> naccumx = accumx * accumx - accumy * accumy;
+ Vector<double> naccumy = accumx * accumy + accumx * accumy;
+ accumx = naccumx + vx;
+ accumy = naccumy + vy;
+ viters += increment;
+ Vector<double> sqabs = accumx * accumx + accumy * accumy;
+ Vector<double> vCond = Vector.LessThanOrEqual<double>(sqabs, vlimit) &
+ Vector.LessThanOrEqual<double>(viters, vmax_iters);
+ increment = increment & vCond;
+ } while (increment != Vector<double>.Zero);
+
+ viters.ForEach((iter, elemNum) => DrawPixel(xp + elemNum, yp, (int)iter));
+ }
+ });
+ }
+
+ // Render the fractal on a single thread using the ComplexVecDouble data type
+ // For a well commented version, go see VectorFloatRenderer.RenderSingleThreadedWithADT in VectorFloat.cs
+ public void RenderSingleThreadedWithADT(float xminf, float xmaxf, float yminf, float ymaxf, float stepf)
+ {
+ double xmin = (double)xminf;
+ double xmax = (double)xmaxf;
+ double ymin = (double)yminf;
+ double ymax = (double)ymaxf;
+ double step = (double)stepf;
+
+ Vector<double> vmax_iters = new Vector<double>((double)max_iters);
+ Vector<double> vlimit = new Vector<double>(limit);
+ Vector<double> vstep = new Vector<double>(step);
+ Vector<double> vinc = new Vector<double>((double)Vector<double>.Count * step);
+ Vector<double> vxmax = new Vector<double>(xmax);
+ Vector<double> vxmin = VectorHelper.Create(i => xmin + step * i);
+
+ double y = ymin;
+ int yp = 0;
+ for (Vector<double> vy = new Vector<double>(ymin); y <= ymax && !Abort; vy += vstep, y += step, yp++)
+ {
+ int xp = 0;
+ for (Vector<double> vx = vxmin; Vector.LessThanOrEqualAny(vx, vxmax); vx += vinc, xp += Vector<double>.Count)
+ {
+ ComplexVecDouble num = new ComplexVecDouble(vx, vy);
+ ComplexVecDouble accum = num;
+
+ Vector<double> viters = Vector<double>.Zero;
+ Vector<double> increment = Vector<double>.One;
+ do
+ {
+ accum = accum.square() + num;
+ viters += increment;
+ Vector<double> vCond = Vector.LessThanOrEqual<double>(accum.sqabs(), vlimit) &
+ Vector.LessThanOrEqual<double>(viters, vmax_iters);
+ increment = increment & vCond;
+ } while (increment != Vector<double>.Zero);
+
+ viters.ForEach((iter, elemNum) => DrawPixel(xp + elemNum, yp, (int)iter));
+ }
+ }
+ }
+
+ // Render the fractal on a single thread using raw Vector<double> data types
+ // For a well commented version, go see VectorFloatRenderer.RenderSingleThreadedWithADT in VectorFloat.cs
+ public void RenderSingleThreadedNoADT(float xminf, float xmaxf, float yminf, float ymaxf, float stepf)
+ {
+ double xmin = (double)xminf;
+ double xmax = (double)xmaxf;
+ double ymin = (double)yminf;
+ double ymax = (double)ymaxf;
+ double step = (double)stepf;
+
+ Vector<double> vmax_iters = new Vector<double>((double)max_iters);
+ Vector<double> vlimit = new Vector<double>(limit);
+ Vector<double> vstep = new Vector<double>(step);
+ Vector<double> vinc = new Vector<double>((double)Vector<double>.Count * step);
+ Vector<double> vxmax = new Vector<double>(xmax);
+ Vector<double> vxmin = VectorHelper.Create(i => xmin + step * i);
+
+ double y = ymin;
+ int yp = 0;
+ for (Vector<double> vy = new Vector<double>(ymin); y <= ymax && !Abort; vy += vstep, y += step, yp++)
+ {
+ int xp = 0;
+ for (Vector<double> vx = vxmin; Vector.LessThanOrEqualAny(vx, vxmax); vx += vinc, xp += Vector<double>.Count)
+ {
+ Vector<double> accumx = vx;
+ Vector<double> accumy = vy;
+
+ Vector<double> viters = Vector<double>.Zero;
+ Vector<double> increment = Vector<double>.One;
+ do
+ {
+ Vector<double> naccumx = accumx * accumx - accumy * accumy;
+ Vector<double> naccumy = accumx * accumy + accumx * accumy;
+ accumx = naccumx + vx;
+ accumy = naccumy + vy;
+ viters += increment;
+ Vector<double> sqabs = accumx * accumx + accumy * accumy;
+ Vector<double> vCond = Vector.LessThanOrEqual<double>(sqabs, vlimit) &
+ Vector.LessThanOrEqual<double>(viters, vmax_iters);
+ increment = increment & vCond;
+ } while (increment != Vector<double>.Zero);
+
+ viters.ForEach((iter, elemNum) => DrawPixel(xp + elemNum, yp, (int)iter));
+ }
+ }
+ }
+ }
+}
diff --git a/tests/src/JIT/Performance/CodeQuality/SIMD/ConsoleMandel/VectorFloat.cs b/tests/src/JIT/Performance/CodeQuality/SIMD/ConsoleMandel/VectorFloat.cs
new file mode 100644
index 0000000000..418c271fcf
--- /dev/null
+++ b/tests/src/JIT/Performance/CodeQuality/SIMD/ConsoleMandel/VectorFloat.cs
@@ -0,0 +1,214 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//
+
+using System;
+using System.Numerics;
+using System.Threading.Tasks;
+
+namespace Algorithms
+{
+ // This class contains renderers that use Vector (SIMD) floats
+ internal class VectorFloatRenderer : FractalRenderer
+ {
+ private const float limit = 4.0f;
+
+ public VectorFloatRenderer(Action<int, int, int> dp, Func<bool> abortFunc)
+ : base(dp, abortFunc)
+ {
+ }
+
+ // Render the fractal on a single thread using the ComplexFloatVec data type
+ // This is the implementation that has the best comments.
+ public void RenderSingleThreadedWithADT(float xmin, float xmax, float ymin, float ymax, float step)
+ {
+ // Initialize a pile of method constant vectors
+ Vector<int> vmax_iters = new Vector<int>(max_iters);
+ Vector<float> vlimit = new Vector<float>(limit);
+ Vector<float> vstep = new Vector<float>(step);
+ Vector<float> vxmax = new Vector<float>(xmax);
+ Vector<float> vinc = new Vector<float>((float)Vector<float>.Count * step);
+ // Use my little helper routine: it's kind of slow, but I find it pleasantly readable.
+ // The alternative would be this:
+ // float[] xmins = new float[Vector<float>.Count];
+ // for (int i = 0; i < xmins.Count; i++)
+ // xmins[i] = xmin + step * i;
+ // Vector<float> vxmin = new Vector<float>(xmins);
+ // Both allocate some memory, this one just does it in a separate routine :-)
+ Vector<float> vxmin = VectorHelper.Create(i => xmin + step * i);
+
+ float y = ymin;
+ int yp = 0;
+ for (Vector<float> vy = new Vector<float>(ymin);
+ y <= ymax && !Abort;
+ vy += vstep, y += step, yp++)
+ {
+ int xp = 0;
+ for (Vector<float> vx = vxmin;
+ Vector.LessThanOrEqualAny(vx, vxmax); // Vector.{comparision}Any|All return bools, not masks
+ vx += vinc, xp += Vector<int>.Count)
+ {
+ ComplexVecFloat num = new ComplexVecFloat(vx, vy);
+ ComplexVecFloat accum = num;
+
+ Vector<int> viters = Vector<int>.Zero; // Iteration counts start at all zeros
+ Vector<int> increment = Vector<int>.One; // Increment starts out as all ones
+ do
+ {
+ // This is work that can be vectorized
+ accum = accum.square() + num;
+ // Increment the iteration count Only pixels that haven't already hit the
+ // limit will be incremented because the increment variable gets masked below
+ viters += increment;
+ // Create a mask that correspons to the element-wise logical operation
+ // "accum <= limit && iters <= max_iters" Note that the bitwise and is used,
+ // because the Vector.{comparision} operations return masks, not boolean values
+ Vector<int> vCond = Vector.LessThanOrEqual(accum.sqabs(), vlimit) &
+ Vector.LessThanOrEqual(viters, vmax_iters);
+ // increment becomes zero for the elems that have hit the limit because
+ // vCond is a mask of all zeros or ones, based on the results of the
+ // Vector.{comparison} operations
+ increment = increment & vCond;
+ // Keep going until we have no elements that haven't either hit the value
+ // limit or the iteration count
+ } while (increment != Vector<int>.Zero);
+
+ // This is another little helper I created. It's definitely kind of slow but I
+ // find it pleasantly succinct. It could also be written like this:
+ //
+ // for (int eNum = 0; eNum < Vector<int>.Count; eNum++)
+ // DrawPixel(xp + eNum, yp, viters[eNum]);
+ //
+ // Neither implementation is particularly fast, because pulling individual elements
+ // is a slow operation for vector types.
+ viters.ForEach((iter, elemNum) => DrawPixel(xp + elemNum, yp, iter));
+ }
+ }
+ }
+
+ // Render the fractal on a single thread using raw Vector<float> data types
+ // For a well commented version, go see VectorFloatRenderer.RenderSingleThreadedWithADT
+ public void RenderSingleThreadedNoADT(float xmin, float xmax, float ymin, float ymax, float step)
+ {
+ Vector<int> vmax_iters = new Vector<int>(max_iters);
+ Vector<float> vlimit = new Vector<float>(limit);
+ Vector<float> vstep = new Vector<float>(step);
+ Vector<float> vxmax = new Vector<float>(xmax);
+ Vector<float> vinc = new Vector<float>((float)Vector<float>.Count * step);
+ Vector<float> vxmin = VectorHelper.Create(i => xmin + step * i);
+
+ float y = ymin;
+ int yp = 0;
+ for (Vector<float> vy = new Vector<float>(ymin); y <= ymax && !Abort; vy += vstep, y += step, yp++)
+ {
+ int xp = 0;
+ for (Vector<float> vx = vxmin; Vector.LessThanOrEqualAny(vx, vxmax); vx += vinc, xp += Vector<int>.Count)
+ {
+ Vector<float> accumx = vx;
+ Vector<float> accumy = vy;
+
+ Vector<int> viters = Vector<int>.Zero;
+ Vector<int> increment = Vector<int>.One;
+ do
+ {
+ Vector<float> naccumx = accumx * accumx - accumy * accumy;
+ Vector<float> naccumy = accumx * accumy + accumx * accumy;
+ accumx = naccumx + vx;
+ accumy = naccumy + vy;
+ viters += increment;
+ Vector<float> sqabs = accumx * accumx + accumy * accumy;
+ Vector<int> vCond = Vector.LessThanOrEqual(sqabs, vlimit) &
+ Vector.LessThanOrEqual(viters, vmax_iters);
+ increment = increment & vCond;
+ } while (increment != Vector<int>.Zero);
+
+ viters.ForEach((iter, elemNum) => DrawPixel(xp + elemNum, yp, (int)iter));
+ }
+ }
+ }
+
+ // Render the fractal on multiple threads using raw Vector<float> data types
+ // For a well commented version, go see VectorFloatRenderer.RenderSingleThreadedWithADT
+ public void RenderMultiThreadedNoADT(float xmin, float xmax, float ymin, float ymax, float step)
+ {
+ Vector<int> vmax_iters = new Vector<int>(max_iters);
+ Vector<float> vlimit = new Vector<float>(limit);
+ Vector<float> vstep = new Vector<float>(step);
+ Vector<float> vinc = new Vector<float>((float)Vector<float>.Count * step);
+ Vector<float> vxmax = new Vector<float>(xmax);
+ Vector<float> vxmin = VectorHelper.Create(i => xmin + step * i);
+
+ Parallel.For(0, (int)(((ymax - ymin) / step) + .5f), (yp) =>
+ {
+ if (Abort)
+ return;
+
+ Vector<float> vy = new Vector<float>(ymin + step * yp);
+ int xp = 0;
+ for (Vector<float> vx = vxmin; Vector.LessThanOrEqualAny(vx, vxmax); vx += vinc, xp += Vector<int>.Count)
+ {
+ Vector<float> accumx = vx;
+ Vector<float> accumy = vy;
+
+ Vector<int> viters = Vector<int>.Zero;
+ Vector<int> increment = Vector<int>.One;
+ do
+ {
+ Vector<float> naccumx = accumx * accumx - accumy * accumy;
+ Vector<float> XtimesY = accumx * accumy;
+ Vector<float> naccumy = XtimesY + XtimesY;
+ accumx = naccumx + vx;
+ accumy = naccumy + vy;
+ viters += increment;
+ Vector<float> sqabs = accumx * accumx + accumy * accumy;
+ Vector<int> vCond = Vector.LessThanOrEqual(sqabs, vlimit) &
+ Vector.LessThanOrEqual(viters, vmax_iters);
+ increment = increment & vCond;
+ } while (increment != Vector<int>.Zero);
+
+ viters.ForEach((iter, elemNum) => DrawPixel(xp + elemNum, yp, (int)iter));
+ }
+ });
+ }
+
+ // Render the fractal on multiple threads using the ComplexFloatVec data type
+ // For a well commented version, go see VectorFloatRenderer.RenderSingleThreadedWithADT
+ public void RenderMultiThreadedWithADT(float xmin, float xmax, float ymin, float ymax, float step)
+ {
+ Vector<int> vmax_iters = new Vector<int>(max_iters);
+ Vector<float> vlimit = new Vector<float>(limit);
+ Vector<float> vstep = new Vector<float>(step);
+ Vector<float> vinc = new Vector<float>((float)Vector<float>.Count * step);
+ Vector<float> vxmax = new Vector<float>(xmax);
+ Vector<float> vxmin = VectorHelper.Create(i => xmin + step * i);
+
+ Parallel.For(0, (int)(((ymax - ymin) / step) + .5f), (yp) =>
+ {
+ if (Abort)
+ return;
+
+ Vector<float> vy = new Vector<float>(ymin + step * yp);
+ int xp = 0;
+ for (Vector<float> vx = vxmin; Vector.LessThanOrEqualAny(vx, vxmax); vx += vinc, xp += Vector<int>.Count)
+ {
+ ComplexVecFloat num = new ComplexVecFloat(vx, vy);
+ ComplexVecFloat accum = num;
+
+ Vector<int> viters = Vector<int>.Zero;
+ Vector<int> increment = Vector<int>.One;
+ do
+ {
+ accum = accum.square() + num;
+ viters += increment;
+ Vector<int> vCond = Vector.LessThanOrEqual(accum.sqabs(), vlimit) &
+ Vector.LessThanOrEqual(viters, vmax_iters);
+ increment = increment & vCond;
+ } while (increment != Vector<int>.Zero);
+
+ viters.ForEach((iter, elemNum) => DrawPixel(xp + elemNum, yp, iter));
+ }
+ });
+ }
+ }
+}
diff --git a/tests/src/JIT/Performance/CodeQuality/SIMD/ConsoleMandel/VectorFloatStrict.cs b/tests/src/JIT/Performance/CodeQuality/SIMD/ConsoleMandel/VectorFloatStrict.cs
new file mode 100644
index 0000000000..f4b8ccf9a5
--- /dev/null
+++ b/tests/src/JIT/Performance/CodeQuality/SIMD/ConsoleMandel/VectorFloatStrict.cs
@@ -0,0 +1,191 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//
+
+using System;
+using System.Numerics;
+using System.Threading.Tasks;
+
+namespace Algorithms
+{
+ // This contains renderers that only use Vector<float>'s with no Vector<int> types. It was
+ // originally focused on targeting AVX (not AVX2), because AVX doesn't support 256 bits of
+ // integer values, only floating point values.
+ internal class VectorFloatStrictRenderer : FractalRenderer
+ {
+ private const float limit = 4.0f;
+
+ private static Vector<float> s_dummy;
+
+ static VectorFloatStrictRenderer()
+ {
+ s_dummy = Vector<float>.One;
+ }
+
+ public VectorFloatStrictRenderer(Action<int, int, int> dp, Func<bool> abortFunc)
+ : base(dp, abortFunc)
+ {
+ }
+
+ // Render the fractal on multiple threads using the ComplexFloatVec data type
+ // For a well commented version, go see VectorFloatRenderer.RenderSingleThreadedWithADT in VectorFloat.cs
+ public void RenderMultiThreadedWithADT(float xmin, float xmax, float ymin, float ymax, float step)
+ {
+ Vector<float> vmax_iters = new Vector<float>((float)max_iters);
+ Vector<float> vlimit = new Vector<float>(limit);
+ Vector<float> vstep = new Vector<float>(step);
+ Vector<float> vinc = new Vector<float>((float)Vector<float>.Count * step);
+ Vector<float> vxmax = new Vector<float>(xmax);
+ Vector<float> vxmin = VectorHelper.Create(i => xmin + step * i);
+
+ Parallel.For(0, (int)(((ymax - ymin) / step) + .5f), (yp) =>
+ {
+ if (Abort)
+ return;
+
+ Vector<float> vy = new Vector<float>(ymin + step * yp);
+ int xp = 0;
+ for (Vector<float> vx = vxmin; Vector.LessThanOrEqualAny(vx, vxmax); vx += vinc, xp += Vector<float>.Count)
+ {
+ ComplexVecFloat num = new ComplexVecFloat(vx, vy);
+ ComplexVecFloat accum = num;
+
+ Vector<float> viters = Vector<float>.Zero;
+ Vector<float> increment = Vector<float>.One;
+ do
+ {
+ accum = accum.square() + num;
+ viters += increment;
+ Vector<float> vCond = Vector.LessThanOrEqual<float>(accum.sqabs(), vlimit) &
+ Vector.LessThanOrEqual<float>(viters, vmax_iters);
+ increment = increment & vCond;
+ } while (increment != Vector<float>.Zero);
+
+ viters.ForEach((iter, elemNum) => DrawPixel(xp + elemNum, yp, (int)iter));
+ }
+ });
+ }
+
+ // Render the fractal on multiple threads using raw Vector<float> data types
+ // For a well commented version, go see VectorFloatRenderer.RenderSingleThreadedWithADT in VectorFloat.cs
+ public void RenderMultiThreadedNoADT(float xmin, float xmax, float ymin, float ymax, float step)
+ {
+ Vector<float> vmax_iters = new Vector<float>((float)max_iters);
+ Vector<float> vlimit = new Vector<float>(limit);
+ Vector<float> vstep = new Vector<float>(step);
+ Vector<float> vinc = new Vector<float>((float)Vector<float>.Count * step);
+ Vector<float> vxmax = new Vector<float>(xmax);
+ Vector<float> vxmin = VectorHelper.Create(i => xmin + step * i);
+
+ Parallel.For(0, (int)(((ymax - ymin) / step) + .5f), (yp) =>
+ {
+ if (Abort)
+ return;
+
+ Vector<float> vy = new Vector<float>(ymin + step * yp);
+ int xp = 0;
+ for (Vector<float> vx = vxmin; Vector.LessThanOrEqualAny(vx, vxmax); vx += vinc, xp += Vector<float>.Count)
+ {
+ Vector<float> accumx = vx;
+ Vector<float> accumy = vy;
+
+ Vector<float> viters = Vector<float>.Zero;
+ Vector<float> increment = Vector<float>.One;
+ do
+ {
+ Vector<float> naccumx = accumx * accumx - accumy * accumy;
+ Vector<float> naccumy = accumx * accumy + accumx * accumy;
+ accumx = naccumx + vx;
+ accumy = naccumy + vy;
+ viters += increment;
+ Vector<float> sqabs = accumx * accumx + accumy * accumy;
+ Vector<float> vCond = Vector.LessThanOrEqual<float>(sqabs, vlimit) &
+ Vector.LessThanOrEqual<float>(viters, vmax_iters);
+ increment = increment & vCond;
+ } while (increment != Vector<float>.Zero);
+
+ viters.ForEach((iter, elemNum) => DrawPixel(xp + elemNum, yp, (int)iter));
+ }
+ });
+ }
+
+ // Render the fractal on a single thread using the ComplexFloatVec data type
+ // For a well commented version, go see VectorFloatRenderer.RenderSingleThreadedWithADT in VectorFloat.cs
+ public void RenderSingleThreadedWithADT(float xmin, float xmax, float ymin, float ymax, float step)
+ {
+ Vector<float> vmax_iters = new Vector<float>((float)max_iters);
+ Vector<float> vlimit = new Vector<float>(limit);
+ Vector<float> vstep = new Vector<float>(step);
+ Vector<float> vxmax = new Vector<float>(xmax);
+ Vector<float> vinc = new Vector<float>((float)Vector<float>.Count * step);
+ Vector<float> vxmin = VectorHelper.Create(i => xmin + step * i);
+
+ float y = ymin;
+ int yp = 0;
+ for (Vector<float> vy = new Vector<float>(ymin); y <= ymax && !Abort; vy += vstep, y += step, yp++)
+ {
+ int xp = 0;
+ for (Vector<float> vx = vxmin; Vector.LessThanOrEqualAny(vx, vxmax); vx += vinc, xp += Vector<float>.Count)
+ {
+ ComplexVecFloat num = new ComplexVecFloat(vx, vy);
+ ComplexVecFloat accum = num;
+
+ Vector<float> viters = Vector<float>.Zero;
+ Vector<float> increment = Vector<float>.One;
+ do
+ {
+ accum = accum.square() + num;
+ viters += increment;
+ Vector<float> vCond = Vector.LessThanOrEqual<float>(accum.sqabs(), vlimit) &
+ Vector.LessThanOrEqual<float>(viters, vmax_iters);
+ increment = increment & vCond;
+ } while (increment != Vector<float>.Zero);
+
+ viters.ForEach((iter, elemNum) => DrawPixel(xp + elemNum, yp, (int)iter));
+ }
+ }
+ }
+
+ // Render the fractal on a single thread using raw Vector<float> data types
+ // For a well commented version, go see VectorFloatRenderer.RenderSingleThreadedWithADT in VectorFloat.cs
+ public void RenderSingleThreadedNoADT(float xmin, float xmax, float ymin, float ymax, float step)
+ {
+ Vector<float> vmax_iters = new Vector<float>(max_iters);
+ Vector<float> vlimit = new Vector<float>(limit);
+ Vector<float> vstep = new Vector<float>(step);
+ Vector<float> vxmax = new Vector<float>(xmax);
+ Vector<float> vinc = new Vector<float>((float)Vector<float>.Count * step);
+ Vector<float> vxmin = VectorHelper.Create(i => xmin + step * i);
+
+ float y = ymin;
+ int yp = 0;
+ for (Vector<float> vy = new Vector<float>(ymin); y <= ymax && !Abort; vy += vstep, y += step, yp++)
+ {
+ int xp = 0;
+ for (Vector<float> vx = vxmin; Vector.LessThanOrEqualAny(vx, vxmax); vx += vinc, xp += Vector<float>.Count)
+ {
+ Vector<float> accumx = vx;
+ Vector<float> accumy = vy;
+
+ Vector<float> viters = Vector<float>.Zero;
+ Vector<float> increment = Vector<float>.One;
+ do
+ {
+ Vector<float> naccumx = accumx * accumx - accumy * accumy;
+ Vector<float> naccumy = accumx * accumy + accumx * accumy;
+ accumx = naccumx + vx;
+ accumy = naccumy + vy;
+ viters += increment;
+ Vector<float> sqabs = accumx * accumx + accumy * accumy;
+ Vector<float> vCond = Vector.LessThanOrEqual<float>(sqabs, vlimit) &
+ Vector.LessThanOrEqual<float>(viters, vmax_iters);
+ increment = increment & vCond;
+ } while (increment != Vector<float>.Zero);
+
+ viters.ForEach((iter, elemNum) => DrawPixel(xp + elemNum, yp, (int)iter));
+ }
+ }
+ }
+ }
+}
diff --git a/tests/src/JIT/Performance/CodeQuality/SIMD/ConsoleMandel/VectorHelpers.cs b/tests/src/JIT/Performance/CodeQuality/SIMD/ConsoleMandel/VectorHelpers.cs
new file mode 100644
index 0000000000..cba8ce8194
--- /dev/null
+++ b/tests/src/JIT/Performance/CodeQuality/SIMD/ConsoleMandel/VectorHelpers.cs
@@ -0,0 +1,38 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//
+
+using System;
+using System.Numerics;
+using System.Runtime.CompilerServices;
+
+namespace Algorithms
+{
+ public static class VectorHelper
+ {
+ // Helper to construct a vector from a lambda that takes an
+ // index. It's not efficient, but it's more succint than the
+ // corresponding for loop. Don't use it on a hot code path
+ // (i.e. inside a loop)
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ public static Vector<T> Create<T>(Func<int, T> creator) where T : struct
+ {
+ T[] data = new T[Vector<T>.Count];
+ for (int i = 0; i < data.Length; i++)
+ data[i] = creator(i);
+ return new Vector<T>(data);
+ }
+
+ // Helper to invoke a function for each element of the
+ // vector. This is NOT fast. I just like the way it looks
+ // better than a for loop. Don't use it somewhere that
+ // performance truly matters
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ public static void ForEach<T>(this Vector<T> vec, Action<T, int> op) where T : struct
+ {
+ for (int i = 0; i < Vector<T>.Count; i++)
+ op(vec[i], i);
+ }
+ }
+}
diff --git a/tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/Camera.cs b/tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/Camera.cs
new file mode 100644
index 0000000000..7d96ca769e
--- /dev/null
+++ b/tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/Camera.cs
@@ -0,0 +1,25 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//
+
+internal class Camera
+{
+ public Vector Pos;
+ public Vector Forward;
+ public Vector Up;
+ public Vector Right;
+
+ public Camera(Vector pos, Vector forward, Vector up, Vector right) { Pos = pos; Forward = forward; Up = up; Right = right; }
+
+ public static Camera Create(Vector pos, Vector lookAt)
+ {
+ Vector forward = Vector.Norm(Vector.Minus(lookAt, pos));
+ Vector down = new Vector(0, -1, 0);
+ Vector right = Vector.Times(1.5F, Vector.Norm(Vector.Cross(forward, down)));
+ Vector up = Vector.Times(1.5F, Vector.Norm(Vector.Cross(forward, right)));
+
+ return new Camera(pos, forward, up, right);
+ }
+}
+
diff --git a/tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/Color.cs b/tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/Color.cs
new file mode 100644
index 0000000000..c88b188ec4
--- /dev/null
+++ b/tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/Color.cs
@@ -0,0 +1,143 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//
+
+using System;
+using System.Numerics;
+
+public struct Color
+{
+ private Vector3 _simdVector;
+ public float R { get { return _simdVector.X; } }
+ public float G { get { return _simdVector.Y; } }
+ public float B { get { return _simdVector.Z; } }
+
+ public Color(double r, double g, double b)
+ {
+ _simdVector = new Vector3((float)r, (float)g, (float)b);
+ }
+ public Color(string str)
+ {
+ string[] nums = str.Split(',');
+ if (nums.Length != 3) throw new ArgumentException();
+ _simdVector = new Vector3(float.Parse(nums[0]), float.Parse(nums[1]), float.Parse(nums[2]));
+ }
+
+
+ public static Color Times(double n, Color v)
+ {
+ Color result;
+ result._simdVector = (float)n * v._simdVector;
+ return result;
+ }
+ public static Color Times(Color v1, Color v2)
+ {
+ Color result;
+ result._simdVector = v1._simdVector * v2._simdVector;
+ return result;
+ }
+
+ public static Color Plus(Color v1, Color v2)
+ {
+ Color result;
+ result._simdVector = v1._simdVector + v2._simdVector;
+ return result;
+ }
+ public static Color Minus(Color v1, Color v2)
+ {
+ Color result;
+ result._simdVector = v1._simdVector - v2._simdVector;
+ return result;
+ }
+
+ public static Color Background { get { Color result; result._simdVector = Vector3.Zero; return result; } }
+ public static Color DefaultColor { get { Color result; result._simdVector = Vector3.Zero; return result; } }
+
+ public static float Legalize(float d)
+ {
+ return d > 1 ? 1 : d;
+ }
+
+ public static byte ToByte(float c)
+ {
+ return (byte)(255 * Legalize(c));
+ }
+
+ public static Int32 ToInt32(float c)
+ {
+ Int32 r = (Int32)(255 * c);
+ return (r > 255 ? 255 : r);
+ }
+
+ public Int32 ToInt32()
+ {
+ return (ToInt32(B) | ToInt32(G) << 8 | ToInt32(R) << 16 | 255 << 24);
+ }
+
+ public float Brightness()
+ {
+ float r = (float)R / 255.0f;
+ float g = (float)G / 255.0f;
+ float b = (float)B / 255.0f;
+
+ float max, min;
+
+ max = r; min = r;
+
+ if (g > max) max = g;
+ if (b > max) max = b;
+
+ if (g < min) min = g;
+ if (b < min) min = b;
+
+ return (max + min) / 2;
+ }
+
+ public void ChangeHue(float hue)
+ {
+ float H, S, L, Br;
+
+ Br = Brightness();
+ H = hue;
+ S = 0.9F;
+ L = ((Br - 0.5F) * 0.5F) + 0.5F;
+
+ if (L == 0)
+ {
+ _simdVector = Vector3.Zero;
+ }
+ else
+ {
+ if (S == 0)
+ {
+ _simdVector = new Vector3(L);
+ }
+ else
+ {
+ float temp2 = ((L <= 0.5F) ? L * (1.0F + S) : L + S - (L * S));
+ float temp1 = 2.0F * L - temp2;
+
+ float[] t3 = new float[] { H + 1.0F / 3.0F, H, H - 1.0F / 3.0F };
+ float[] clr = new float[] { 0, 0, 0 };
+
+ for (int i = 0; i < 3; i++)
+ {
+ if (t3[i] < 0) t3[i] += 1.0F;
+ if (t3[i] > 1) t3[i] -= 1.0F;
+ if (6.0 * t3[i] < 1.0)
+ clr[i] = temp1 + (temp2 - temp1) * t3[i] * 6.0F;
+ else if (2.0 * t3[i] < 1.0)
+ clr[i] = temp2;
+ else if (3.0 * t3[i] < 2.0)
+ clr[i] = (temp1 + (temp2 - temp1) * ((2.0F / 3.0F) - t3[i]) * 6.0F);
+ else
+ clr[i] = temp1;
+ }
+
+ _simdVector = new Vector3(clr[0], clr[1], clr[2]);
+ }
+ }
+ }
+}
+
diff --git a/tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/ISect.cs b/tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/ISect.cs
new file mode 100644
index 0000000000..71a8e0a512
--- /dev/null
+++ b/tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/ISect.cs
@@ -0,0 +1,16 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//
+
+internal class ISect
+{
+ public SceneObject Thing;
+ public Ray Ray;
+ public double Dist;
+
+ public ISect(SceneObject thing, Ray ray, double dist) { Thing = thing; Ray = ray; Dist = dist; }
+
+ public static bool IsNull(ISect sect) { return sect == null; }
+ public readonly static ISect Null = null;
+}
diff --git a/tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/Light.cs b/tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/Light.cs
new file mode 100644
index 0000000000..d8f448f1fa
--- /dev/null
+++ b/tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/Light.cs
@@ -0,0 +1,13 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//
+
+internal class Light
+{
+ public Vector Pos;
+ public Color Color;
+
+ public Light(Vector pos, Color color) { Pos = pos; Color = color; }
+}
+
diff --git a/tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/ObjectPool.cs b/tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/ObjectPool.cs
new file mode 100644
index 0000000000..29be23238b
--- /dev/null
+++ b/tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/ObjectPool.cs
@@ -0,0 +1,68 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//
+
+using System.Collections.Generic;
+using System.Diagnostics;
+
+namespace System.Collections.Concurrent
+{
+ /// <summary>Provides a thread-safe object pool.</summary>
+ /// <typeparam name="T">Specifies the type of the elements stored in the pool.</typeparam>
+ [DebuggerDisplay("Count={Count}")]
+ [DebuggerTypeProxy(typeof(IProducerConsumerCollection_DebugView<>))]
+ public sealed class ObjectPool<T> : ProducerConsumerCollectionBase<T>
+ {
+ private readonly Func<T> _generator;
+
+ /// <summary>Initializes an instance of the ObjectPool class.</summary>
+ /// <param name="generator">The function used to create items when no items exist in the pool.</param>
+ public ObjectPool(Func<T> generator) : this(generator, new ConcurrentQueue<T>()) { }
+
+ /// <summary>Initializes an instance of the ObjectPool class.</summary>
+ /// <param name="generator">The function used to create items when no items exist in the pool.</param>
+ /// <param name="collection">The collection used to store the elements of the pool.</param>
+ public ObjectPool(Func<T> generator, IProducerConsumerCollection<T> collection)
+ : base(collection)
+ {
+ if (generator == null) throw new ArgumentNullException("generator");
+ _generator = generator;
+ }
+
+ /// <summary>Adds the provided item into the pool.</summary>
+ /// <param name="item">The item to be added.</param>
+ public void PutObject(T item) { base.TryAdd(item); }
+
+ /// <summary>Gets an item from the pool.</summary>
+ /// <returns>The removed or created item.</returns>
+ /// <remarks>If the pool is empty, a new item will be created and returned.</remarks>
+ public T GetObject()
+ {
+ T value;
+ return base.TryTake(out value) ? value : _generator();
+ }
+
+ /// <summary>Clears the object pool, returning all of the data that was in the pool.</summary>
+ /// <returns>An array containing all of the elements in the pool.</returns>
+ public T[] ToArrayAndClear()
+ {
+ var items = new List<T>();
+ T value;
+ while (base.TryTake(out value)) items.Add(value);
+ return items.ToArray();
+ }
+
+ protected override bool TryAdd(T item)
+ {
+ PutObject(item);
+ return true;
+ }
+
+ protected override bool TryTake(out T item)
+ {
+ item = GetObject();
+ return true;
+ }
+ }
+}
diff --git a/tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/Plane.cs b/tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/Plane.cs
new file mode 100644
index 0000000000..9504a0b116
--- /dev/null
+++ b/tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/Plane.cs
@@ -0,0 +1,25 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//
+
+internal class Plane : SceneObject
+{
+ public Vector Norm;
+ public double Offset;
+
+ public Plane(Vector norm, double offset, Surface surface) : base(surface) { Norm = norm; Offset = offset; }
+
+ public override ISect Intersect(Ray ray)
+ {
+ double denom = Vector.Dot(Norm, ray.Dir);
+ if (denom > 0) return ISect.Null;
+ return new ISect(this, ray, (Vector.Dot(Norm, ray.Start) + Offset) / (-denom));
+ }
+
+ public override Vector Normal(Vector pos)
+ {
+ return Norm;
+ }
+}
+
diff --git a/tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/ProducerConsumerCollectionBase.cs b/tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/ProducerConsumerCollectionBase.cs
new file mode 100644
index 0000000000..fee736397d
--- /dev/null
+++ b/tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/ProducerConsumerCollectionBase.cs
@@ -0,0 +1,107 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//
+
+using System.Collections.Generic;
+using System.Diagnostics;
+
+namespace System.Collections.Concurrent
+{
+ /// <summary>Debug view for the IProducerConsumerCollection.</summary>
+ /// <typeparam name="T">Specifies the type of the data being aggregated.</typeparam>
+ internal sealed class IProducerConsumerCollection_DebugView<T>
+ {
+ private IProducerConsumerCollection<T> _collection;
+
+ public IProducerConsumerCollection_DebugView(IProducerConsumerCollection<T> collection)
+ {
+ _collection = collection;
+ }
+
+ [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
+ public T[] Values { get { return _collection.ToArray(); } }
+ }
+
+ /// <summary>
+ /// Provides a base implementation for producer-consumer collections that wrap other
+ /// producer-consumer collections.
+ /// </summary>
+ /// <typeparam name="T">Specifies the type of elements in the collection.</typeparam>
+ public abstract class ProducerConsumerCollectionBase<T> : IProducerConsumerCollection<T>
+ {
+ private readonly IProducerConsumerCollection<T> _contained;
+
+ /// <summary>Initializes the ProducerConsumerCollectionBase instance.</summary>
+ /// <param name="contained">The collection to be wrapped by this instance.</param>
+ protected ProducerConsumerCollectionBase(IProducerConsumerCollection<T> contained)
+ {
+ if (contained == null) throw new ArgumentNullException("contained");
+ _contained = contained;
+ }
+
+ /// <summary>Gets the contained collection.</summary>
+ protected IProducerConsumerCollection<T> ContainedCollection { get { return _contained; } }
+
+ /// <summary>Attempts to add the specified value to the end of the deque.</summary>
+ /// <param name="item">The item to add.</param>
+ /// <returns>true if the item could be added; otherwise, false.</returns>
+ protected virtual bool TryAdd(T item) { return _contained.TryAdd(item); }
+
+ /// <summary>Attempts to remove and return an item from the collection.</summary>
+ /// <param name="item">
+ /// When this method returns, if the operation was successful, item contains the item removed. If
+ /// no item was available to be removed, the value is unspecified.
+ /// </param>
+ /// <returns>
+ /// true if an element was removed and returned from the collection; otherwise, false.
+ /// </returns>
+ protected virtual bool TryTake(out T item) { return _contained.TryTake(out item); }
+
+ /// <summary>Attempts to add the specified value to the end of the deque.</summary>
+ /// <param name="item">The item to add.</param>
+ /// <returns>true if the item could be added; otherwise, false.</returns>
+ bool IProducerConsumerCollection<T>.TryAdd(T item) { return TryAdd(item); }
+
+ /// <summary>Attempts to remove and return an item from the collection.</summary>
+ /// <param name="item">
+ /// When this method returns, if the operation was successful, item contains the item removed. If
+ /// no item was available to be removed, the value is unspecified.
+ /// </param>
+ /// <returns>
+ /// true if an element was removed and returned from the collection; otherwise, false.
+ /// </returns>
+ bool IProducerConsumerCollection<T>.TryTake(out T item) { return TryTake(out item); }
+
+ /// <summary>Gets the number of elements contained in the collection.</summary>
+ public int Count { get { return _contained.Count; } }
+
+ /// <summary>Creates an array containing the contents of the collection.</summary>
+ /// <returns>The array.</returns>
+ public T[] ToArray() { return _contained.ToArray(); }
+
+ /// <summary>Copies the contents of the collection to an array.</summary>
+ /// <param name="array">The array to which the data should be copied.</param>
+ /// <param name="index">The starting index at which data should be copied.</param>
+ public void CopyTo(T[] array, int index) { _contained.CopyTo(array, index); }
+
+ /// <summary>Copies the contents of the collection to an array.</summary>
+ /// <param name="array">The array to which the data should be copied.</param>
+ /// <param name="index">The starting index at which data should be copied.</param>
+ void ICollection.CopyTo(Array array, int index) { _contained.CopyTo(array, index); }
+
+ /// <summary>Gets an enumerator for the collection.</summary>
+ /// <returns>An enumerator.</returns>
+ public IEnumerator<T> GetEnumerator() { return _contained.GetEnumerator(); }
+
+ /// <summary>Gets an enumerator for the collection.</summary>
+ /// <returns>An enumerator.</returns>
+ IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); }
+
+ /// <summary>Gets whether the collection is synchronized.</summary>
+ bool ICollection.IsSynchronized { get { return _contained.IsSynchronized; } }
+
+ /// <summary>Gets the synchronization root object for the collection.</summary>
+ object ICollection.SyncRoot { get { return _contained.SyncRoot; } }
+ }
+}
diff --git a/tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/Ray.cs b/tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/Ray.cs
new file mode 100644
index 0000000000..a2d91fb07c
--- /dev/null
+++ b/tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/Ray.cs
@@ -0,0 +1,13 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//
+
+internal struct Ray
+{
+ public Vector Start;
+ public Vector Dir;
+
+ public Ray(Vector start, Vector dir) { Start = start; Dir = dir; }
+}
+
diff --git a/tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/RayTracer.csproj b/tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/RayTracer.csproj
new file mode 100644
index 0000000000..00db618453
--- /dev/null
+++ b/tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/RayTracer.csproj
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" />
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <SchemaVersion>2.0</SchemaVersion>
+ <ProjectGuid>{95DFC527-4DC1-495E-97D7-E94EE1F7140D}</ProjectGuid>
+ <OutputType>Exe</OutputType>
+ <AppDesignerFolder>Properties</AppDesignerFolder>
+ <FileAlignment>512</FileAlignment>
+ <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+ <ReferencePath>$(ProgramFiles)\Common Files\microsoft shared\VSTT\11.0\UITestExtensionPackages</ReferencePath>
+ <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir>
+ <NuGetPackageImportStamp>7a9bfb7d</NuGetPackageImportStamp>
+ <NuGetTargetMoniker>.NETStandard,Version=v1.4</NuGetTargetMoniker>
+ </PropertyGroup>
+ <!-- Default configurations to help VS understand the configurations -->
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+ <DebugType>pdbonly</DebugType>
+ <Optimize>true</Optimize>
+ </PropertyGroup>
+ <ItemGroup>
+ <CodeAnalysisDependentAssemblyPaths Condition=" '$(VS100COMNTOOLS)' != '' " Include="$(VS100COMNTOOLS)..\IDE\PrivateAssemblies">
+ <Visible>False</Visible>
+ </CodeAnalysisDependentAssemblyPaths>
+ </ItemGroup>
+ <ItemGroup>
+ <Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="RayTracerBench.cs" />
+ <Compile Include="Camera.cs" />
+ <Compile Include="Color.cs" />
+ <Compile Include="ISect.cs" />
+ <Compile Include="Light.cs" />
+ <Compile Include="ObjectPool.cs" />
+ <Compile Include="Plane.cs" />
+ <Compile Include="ProducerConsumerCollectionBase.cs" />
+ <Compile Include="Ray.cs" />
+ <Compile Include="Raytracer.cs" />
+ <Compile Include="Scene.cs" />
+ <Compile Include="SceneObject.cs" />
+ <Compile Include="Sphere.cs" />
+ <Compile Include="Surface.cs" />
+ <Compile Include="Surfaces.cs" />
+ <Compile Include="Vector.cs" />
+ </ItemGroup>
+ <PropertyGroup>
+ <ProjectJson>$(JitPackagesConfigFileDirectory)benchmark\project.json</ProjectJson>
+ <ProjectLockJson>$(JitPackagesConfigFileDirectory)benchmark\project.lock.json</ProjectLockJson>
+ </PropertyGroup>
+ <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
+ <PropertyGroup Condition=" '$(MsBuildProjectDirOverride)' != '' ">
+ </PropertyGroup>
+</Project>
diff --git a/tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/RayTracerBench.cs b/tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/RayTracerBench.cs
new file mode 100644
index 0000000000..064f860611
--- /dev/null
+++ b/tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/RayTracerBench.cs
@@ -0,0 +1,144 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//
+// Based on the Raytracer example from
+// Samples for Parallel Programming with the .NET Framework
+// https://code.msdn.microsoft.com/windowsdesktop/Samples-for-Parallel-b4b76364
+
+using Microsoft.Xunit.Performance;
+using System;
+using System.Diagnostics;
+using System.Threading;
+using System.Threading.Tasks;
+using System.Collections.Concurrent;
+
+[assembly: OptimizeForBenchmarks]
+[assembly: MeasureInstructionsRetired]
+
+public class RayTracerBench
+{
+#if DEBUG
+
+ private const int RunningTime = 200;
+ private const int Width = 100;
+ private const int Height = 100;
+ private const int Iterations = 1;
+ private const int MaxIterations = 1000;
+
+#else
+
+ private const int RunningTime = 1000;
+ private const int Width = 250;
+ private const int Height = 250;
+ private const int Iterations = 7;
+ private const int MaxIterations = 1000;
+
+#endif
+
+ public RayTracerBench()
+ {
+ _width = Width;
+ _height = Height;
+ _parallel = false;
+ _showThreads = false;
+ _freeBuffers = new ObjectPool<int[]>(() => new int[_width * _height]);
+ }
+
+ private double _framesPerSecond;
+ private bool _parallel;
+ private bool _showThreads;
+ private int _width, _height;
+ private int _degreeOfParallelism = Environment.ProcessorCount;
+ private int _frames;
+ private CancellationTokenSource _cancellation;
+ private ObjectPool<int[]> _freeBuffers;
+
+ private void RenderTest()
+ {
+ _cancellation = new CancellationTokenSource(RunningTime);
+ RenderLoop(MaxIterations);
+ }
+
+ private void RenderBench()
+ {
+ _cancellation = new CancellationTokenSource();
+ RenderLoop(Iterations);
+ }
+
+ private void RenderLoop(int iterations)
+ {
+ // Create a ray tracer, and create a reference to "sphere2" that we are going to bounce
+ var rayTracer = new RayTracer(_width, _height);
+ var scene = rayTracer.DefaultScene;
+ var sphere2 = (Sphere)scene.Things[0]; // The first item is assumed to be our sphere
+ var baseY = sphere2.Radius;
+ sphere2.Center.Y = sphere2.Radius;
+
+ // Timing determines how fast the ball bounces as well as diagnostics frames/second info
+ var renderingTime = new Stopwatch();
+ var totalTime = Stopwatch.StartNew();
+
+ // Keep rendering until the iteration count is hit
+ for (_frames = 0; _frames < iterations; _frames++)
+ {
+ // Or the rendering task has been canceled
+ if (_cancellation.IsCancellationRequested)
+ {
+ break;
+ }
+
+ // Get the next buffer
+ var rgb = _freeBuffers.GetObject();
+
+ // Determine the new position of the sphere based on the current time elapsed
+ double dy2 = 0.8 * Math.Abs(Math.Sin(totalTime.ElapsedMilliseconds * Math.PI / 3000));
+ sphere2.Center.Y = (float)(baseY + dy2);
+
+ // Render the scene
+ renderingTime.Reset();
+ renderingTime.Start();
+ ParallelOptions options = new ParallelOptions
+ {
+ MaxDegreeOfParallelism = _degreeOfParallelism,
+ CancellationToken = _cancellation.Token
+ };
+ if (!_parallel) rayTracer.RenderSequential(scene, rgb);
+ else if (_showThreads) rayTracer.RenderParallelShowingThreads(scene, rgb, options);
+ else rayTracer.RenderParallel(scene, rgb, options);
+ renderingTime.Stop();
+
+ _framesPerSecond = (1000.0 / renderingTime.ElapsedMilliseconds);
+ _freeBuffers.PutObject(rgb);
+ }
+ }
+
+ [Benchmark]
+ public static void Bench()
+ {
+ var m = new RayTracerBench();
+ foreach (var iteration in Benchmark.Iterations)
+ {
+ using (iteration.StartMeasurement())
+ {
+ m.RenderBench();
+ }
+ }
+ }
+
+ public bool Run()
+ {
+ RenderTest();
+ Console.WriteLine("{0} frames, {1} frames/sec",
+ _frames,
+ _framesPerSecond.ToString("F2"));
+ return true;
+ }
+
+ public static int Main(string[] args)
+ {
+ var r = new RayTracerBench();
+ bool result = r.Run();
+ return (result ? 100 : -1);
+ }
+}
diff --git a/tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/Raytracer.cs b/tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/Raytracer.cs
new file mode 100644
index 0000000000..c32782ee18
--- /dev/null
+++ b/tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/Raytracer.cs
@@ -0,0 +1,196 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//
+
+using System;
+using System.Collections.Generic;
+using System.Threading;
+using System.Threading.Tasks;
+using System.Numerics;
+
+internal sealed class RayTracer
+{
+ private int _screenWidth;
+ private int _screenHeight;
+ private const int MaxDepth = 5;
+
+ public RayTracer(int screenWidth, int screenHeight)
+ {
+ _screenWidth = screenWidth;
+ _screenHeight = screenHeight;
+ }
+
+ internal void RenderSequential(Scene scene, Int32[] rgb)
+ {
+ for (int y = 0; y < _screenHeight; y++)
+ {
+ int stride = y * _screenWidth;
+ Camera camera = scene.Camera;
+ for (int x = 0; x < _screenWidth; x++)
+ {
+ Color color = TraceRay(new Ray(camera.Pos, GetPoint(x, y, camera)), scene, 0);
+ rgb[x + stride] = color.ToInt32();
+ }
+ }
+ }
+
+ internal void RenderParallel(Scene scene, Int32[] rgb, ParallelOptions options)
+ {
+ Parallel.For(0, _screenHeight, options, y =>
+ {
+ int stride = y * _screenWidth;
+ Camera camera = scene.Camera;
+ for (int x = 0; x < _screenWidth; x++)
+ {
+ Color color = TraceRay(new Ray(camera.Pos, GetPoint(x, y, camera)), scene, 0);
+ rgb[x + stride] = color.ToInt32();
+ }
+ });
+ }
+
+ internal void RenderParallelShowingThreads(Scene scene, Int32[] rgb, ParallelOptions options)
+ {
+ int id = 0;
+ Parallel.For<float>(0, _screenHeight, options, () => GetHueShift(Interlocked.Increment(ref id)), (y, state, hue) =>
+ {
+ int stride = y * _screenWidth;
+ Camera camera = scene.Camera;
+ for (int x = 0; x < _screenWidth; x++)
+ {
+ Color color = TraceRay(new Ray(camera.Pos, GetPoint(x, y, camera)), scene, 0);
+ color.ChangeHue(hue);
+ rgb[x + stride] = color.ToInt32();
+ }
+ return hue;
+ },
+ hue => Interlocked.Decrement(ref id));
+ }
+
+ private Dictionary<int, float> _numToHueShiftLookup = new Dictionary<int, float>();
+ private Random _rand = new Random();
+
+ private float GetHueShift(int id)
+ {
+ float shift;
+ lock (_numToHueShiftLookup)
+ {
+ if (!_numToHueShiftLookup.TryGetValue(id, out shift))
+ {
+ shift = (float)_rand.NextDouble();
+ _numToHueShiftLookup.Add(id, shift);
+ }
+ }
+ return shift;
+ }
+
+ internal readonly Scene DefaultScene = CreateDefaultScene();
+
+ private static Scene CreateDefaultScene()
+ {
+ SceneObject[] things = {
+ new Sphere( new Vector(-0.5,1,1.5), 0.5, Surfaces.MatteShiny),
+ new Sphere( new Vector(0,1,-0.25), 1, Surfaces.Shiny),
+ new Plane( new Vector(0,1,0), 0, Surfaces.CheckerBoard)
+ };
+ Light[] lights = {
+ new Light(new Vector(-2,2.5,0),new Color(.5,.45,.41)),
+ new Light(new Vector(2,4.5,2), new Color(.99,.95,.8))
+ };
+ Camera camera = Camera.Create(new Vector(2.75, 2, 3.75), new Vector(-0.6, .5, 0));
+
+ return new Scene(things, lights, camera);
+ }
+
+
+ private ISect MinIntersection(Ray ray, Scene scene)
+ {
+ ISect min = ISect.Null;
+ foreach (SceneObject obj in scene.Things)
+ {
+ ISect isect = obj.Intersect(ray);
+ if (!ISect.IsNull(isect))
+ {
+ if (ISect.IsNull(min) || min.Dist > isect.Dist)
+ {
+ min = isect;
+ }
+ }
+ }
+ return min;
+ }
+
+ private double TestRay(Ray ray, Scene scene)
+ {
+ ISect isect = MinIntersection(ray, scene);
+ if (ISect.IsNull(isect))
+ return 0;
+ return isect.Dist;
+ }
+
+ private Color TraceRay(Ray ray, Scene scene, int depth)
+ {
+ ISect isect = MinIntersection(ray, scene);
+ if (ISect.IsNull(isect))
+ return Color.Background;
+ return Shade(isect, scene, depth);
+ }
+
+ private Color GetNaturalColor(SceneObject thing, Vector pos, Vector norm, Vector rd, Scene scene)
+ {
+ Color ret = new Color(0, 0, 0);
+ foreach (Light light in scene.Lights)
+ {
+ Vector ldis = Vector.Minus(light.Pos, pos);
+ Vector livec = Vector.Norm(ldis);
+ double neatIsect = TestRay(new Ray(pos, livec), scene);
+ bool isInShadow = !((neatIsect > Vector.Mag(ldis)) || (neatIsect == 0));
+ if (!isInShadow)
+ {
+ float illum = Vector.Dot(livec, norm);
+ Color lcolor = illum > 0 ? Color.Times(illum, light.Color) : new Color(0, 0, 0);
+ float specular = Vector.Dot(livec, Vector.Norm(rd));
+ Color scolor = specular > 0 ? Color.Times(Math.Pow(specular, thing.Surface.Roughness), light.Color) : new Color(0, 0, 0);
+ ret = Color.Plus(ret, Color.Plus(Color.Times(thing.Surface.Diffuse(pos), lcolor),
+ Color.Times(thing.Surface.Specular(pos), scolor)));
+ }
+ }
+ return ret;
+ }
+
+ private Color GetReflectionColor(SceneObject thing, Vector pos, Vector norm, Vector rd, Scene scene, int depth)
+ {
+ return Color.Times(thing.Surface.Reflect(pos), TraceRay(new Ray(pos, rd), scene, depth + 1));
+ }
+
+ private Color Shade(ISect isect, Scene scene, int depth)
+ {
+ Vector d = isect.Ray.Dir;
+ Vector pos = Vector.Plus(Vector.Times(isect.Dist, isect.Ray.Dir), isect.Ray.Start);
+ Vector normal = isect.Thing.Normal(pos);
+ Vector reflectDir = Vector.Minus(d, Vector.Times(2 * Vector.Dot(normal, d), normal));
+ Color ret = Color.DefaultColor;
+ ret = Color.Plus(ret, GetNaturalColor(isect.Thing, pos, normal, reflectDir, scene));
+ if (depth >= MaxDepth)
+ {
+ return Color.Plus(ret, new Color(.5, .5, .5));
+ }
+ return Color.Plus(ret, GetReflectionColor(isect.Thing, Vector.Plus(pos, Vector.Times(.001, reflectDir)), normal, reflectDir, scene, depth));
+ }
+
+ private double RecenterX(double x)
+ {
+ return (x - (_screenWidth / 2.0)) / (2.0 * _screenWidth);
+ }
+ private double RecenterY(double y)
+ {
+ return -(y - (_screenHeight / 2.0)) / (2.0 * _screenHeight);
+ }
+
+ private Vector GetPoint(double x, double y, Camera camera)
+ {
+ return Vector.Norm(Vector.Plus(camera.Forward, Vector.Plus(Vector.Times(RecenterX(x), camera.Right),
+ Vector.Times(RecenterY(y), camera.Up))));
+ }
+}
+
diff --git a/tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/Scene.cs b/tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/Scene.cs
new file mode 100644
index 0000000000..28bf788188
--- /dev/null
+++ b/tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/Scene.cs
@@ -0,0 +1,24 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//
+
+using System.Collections.Generic;
+
+internal class Scene
+{
+ public SceneObject[] Things;
+ public Light[] Lights;
+ public Camera Camera;
+
+ public Scene(SceneObject[] things, Light[] lights, Camera camera) { Things = things; Lights = lights; Camera = camera; }
+
+ public IEnumerable<ISect> Intersect(Ray r)
+ {
+ foreach (SceneObject obj in Things)
+ {
+ yield return obj.Intersect(r);
+ }
+ }
+}
+
diff --git a/tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/SceneObject.cs b/tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/SceneObject.cs
new file mode 100644
index 0000000000..8b4adea04f
--- /dev/null
+++ b/tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/SceneObject.cs
@@ -0,0 +1,14 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//
+
+internal abstract class SceneObject
+{
+ public Surface Surface;
+ public abstract ISect Intersect(Ray ray);
+ public abstract Vector Normal(Vector pos);
+
+ public SceneObject(Surface surface) { Surface = surface; }
+}
+
diff --git a/tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/Sphere.cs b/tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/Sphere.cs
new file mode 100644
index 0000000000..5a92f9481a
--- /dev/null
+++ b/tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/Sphere.cs
@@ -0,0 +1,38 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//
+
+using System;
+
+internal class Sphere : SceneObject
+{
+ public Vector Center;
+ public float Radius;
+
+ public Sphere(Vector center, double radius, Surface surface) : base(surface) { Center = center; Radius = (float)radius; }
+
+ public override ISect Intersect(Ray ray)
+ {
+ Vector eo = Vector.Minus(Center, ray.Start);
+ float v = Vector.Dot(eo, ray.Dir);
+ float dist;
+ if (v < 0)
+ {
+ dist = 0;
+ }
+ else
+ {
+ double disc = Math.Pow(Radius, 2) - (Vector.Dot(eo, eo) - Math.Pow(v, 2));
+ dist = disc < 0 ? 0 : v - (float)Math.Sqrt(disc);
+ }
+ if (dist == 0) return ISect.Null;
+ return new ISect(this, ray, dist);
+ }
+
+ public override Vector Normal(Vector pos)
+ {
+ return Vector.Norm(Vector.Minus(pos, Center));
+ }
+}
+
diff --git a/tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/Surface.cs b/tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/Surface.cs
new file mode 100644
index 0000000000..c33899edd7
--- /dev/null
+++ b/tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/Surface.cs
@@ -0,0 +1,26 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//
+
+using System;
+
+internal class Surface
+{
+ public Func<Vector, Color> Diffuse;
+ public Func<Vector, Color> Specular;
+ public Func<Vector, double> Reflect;
+ public double Roughness;
+
+ public Surface(Func<Vector, Color> Diffuse,
+ Func<Vector, Color> Specular,
+ Func<Vector, double> Reflect,
+ double Roughness)
+ {
+ this.Diffuse = Diffuse;
+ this.Specular = Specular;
+ this.Reflect = Reflect;
+ this.Roughness = Roughness;
+ }
+}
+
diff --git a/tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/Surfaces.cs b/tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/Surfaces.cs
new file mode 100644
index 0000000000..3342e1b7a6
--- /dev/null
+++ b/tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/Surfaces.cs
@@ -0,0 +1,44 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//
+
+using System;
+
+internal static class Surfaces
+{
+ // Only works with X-Z plane.
+ public static readonly Surface CheckerBoard =
+ new Surface(
+ delegate (Vector pos)
+ {
+ return ((Math.Floor(pos.Z) + Math.Floor(pos.X)) % 2 != 0)
+ ? new Color(1, 1, 1)
+ : new Color(0.02, 0.0, 0.14);
+ },
+ delegate (Vector pos) { return new Color(1, 1, 1); },
+ delegate (Vector pos)
+ {
+ return ((Math.Floor(pos.Z) + Math.Floor(pos.X)) % 2 != 0)
+ ? .1
+ : .5;
+ },
+ 150);
+
+
+
+ public static readonly Surface Shiny =
+ new Surface(
+ delegate (Vector pos) { return new Color(1, 1, 1); },
+ delegate (Vector pos) { return new Color(.5, .5, .5); },
+ delegate (Vector pos) { return .7; },
+ 250);
+
+ public static readonly Surface MatteShiny =
+ new Surface(
+ delegate (Vector pos) { return new Color(1, 1, 1); },
+ delegate (Vector pos) { return new Color(.25, .25, .25); },
+ delegate (Vector pos) { return .7; },
+ 250);
+}
+
diff --git a/tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/Vector.cs b/tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/Vector.cs
new file mode 100644
index 0000000000..c6ba48312b
--- /dev/null
+++ b/tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/Vector.cs
@@ -0,0 +1,77 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//
+
+using System;
+using System.Numerics;
+using System.Runtime.CompilerServices;
+
+internal struct Vector
+{
+ private Vector3 _simdVector;
+ public float X { get { return _simdVector.X; } }
+ public float Y { get { return _simdVector.Y; } set { _simdVector = new Vector3(_simdVector.X, value, _simdVector.Z); } }
+ public float Z { get { return _simdVector.Z; } }
+
+ public Vector(double x, double y, double z)
+ {
+ _simdVector = new Vector3((float)x, (float)y, (float)z);
+ }
+ public Vector(string str)
+ {
+ string[] nums = str.Split(',');
+ if (nums.Length != 3) throw new ArgumentException();
+ _simdVector = new Vector3(float.Parse(nums[0]), float.Parse(nums[1]), float.Parse(nums[2]));
+ }
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ public static Vector Times(double n, Vector v)
+ {
+ Vector result;
+ result._simdVector = (float)n * v._simdVector;
+ return result;
+ }
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ public static Vector Minus(Vector v1, Vector v2)
+ {
+ Vector result;
+ result._simdVector = v1._simdVector - v2._simdVector;
+ return result;
+ }
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ public static Vector Plus(Vector v1, Vector v2)
+ {
+ Vector result;
+ result._simdVector = v1._simdVector + v2._simdVector;
+ return result;
+ }
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ public static float Dot(Vector v1, Vector v2)
+ {
+ return Vector3.Dot(v1._simdVector, v2._simdVector);
+ }
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ public static float Mag(Vector v) { return (float)Math.Sqrt(Dot(v, v)); }
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ public static Vector Norm(Vector v)
+ {
+ float mag = Mag(v);
+ float div = mag == 0 ? float.PositiveInfinity : 1 / mag;
+ return Times(div, v);
+ }
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ public static Vector Cross(Vector v1, Vector v2)
+ {
+ return new Vector(((v1.Y * v2.Z) - (v1.Z * v2.Y)),
+ ((v1.Z * v2.X) - (v1.X * v2.Z)),
+ ((v1.X * v2.Y) - (v1.Y * v2.X)));
+ }
+ [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+ public static bool Equals(Vector v1, Vector v2)
+ {
+ return v1._simdVector.Equals(v2._simdVector);
+ }
+
+ public static Vector Null { get { Vector result; result._simdVector = Vector3.Zero; return result; } }
+}
+