Add the RayTracer benchmark
authorAndy Ayers <andya@microsoft.com>
Wed, 20 Jan 2016 22:10:27 +0000 (14:10 -0800)
committerAndy Ayers <andya@microsoft.com>
Thu, 21 Jan 2016 20:54:54 +0000 (12:54 -0800)
17 files changed:
tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/Camera.cs [new file with mode: 0644]
tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/Color.cs [new file with mode: 0644]
tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/ISect.cs [new file with mode: 0644]
tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/Light.cs [new file with mode: 0644]
tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/ObjectPool.cs [new file with mode: 0644]
tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/Plane.cs [new file with mode: 0644]
tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/ProducerConsumerCollectionBase.cs [new file with mode: 0644]
tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/Ray.cs [new file with mode: 0644]
tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/RayTracer.csproj [new file with mode: 0644]
tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/RayTracerBench.cs [new file with mode: 0644]
tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/Raytracer.cs [new file with mode: 0644]
tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/Scene.cs [new file with mode: 0644]
tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/SceneObject.cs [new file with mode: 0644]
tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/Sphere.cs [new file with mode: 0644]
tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/Surface.cs [new file with mode: 0644]
tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/Surfaces.cs [new file with mode: 0644]
tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/Vector.cs [new file with mode: 0644]

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 (file)
index 0000000..f7482be
--- /dev/null
@@ -0,0 +1,24 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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 (file)
index 0000000..278e3d7
--- /dev/null
@@ -0,0 +1,142 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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 (file)
index 0000000..819dcb4
--- /dev/null
@@ -0,0 +1,15 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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 (file)
index 0000000..fa884ab
--- /dev/null
@@ -0,0 +1,12 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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 (file)
index 0000000..2b88c82
--- /dev/null
@@ -0,0 +1,67 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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 (file)
index 0000000..6a16fcb
--- /dev/null
@@ -0,0 +1,24 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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 (file)
index 0000000..2abf0f3
--- /dev/null
@@ -0,0 +1,106 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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 (file)
index 0000000..9472002
--- /dev/null
@@ -0,0 +1,12 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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 (file)
index 0000000..3399cef
--- /dev/null
@@ -0,0 +1,60 @@
+<?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>
+  </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>
+    <None Include="$(JitPackagesConfigFileDirectory)benchmark\project.json" />
+  </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 (file)
index 0000000..03f694d
--- /dev/null
@@ -0,0 +1,143 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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 (file)
index 0000000..d9ab5ef
--- /dev/null
@@ -0,0 +1,195 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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 (file)
index 0000000..77a65d1
--- /dev/null
@@ -0,0 +1,23 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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 (file)
index 0000000..e7ab24f
--- /dev/null
@@ -0,0 +1,13 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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 (file)
index 0000000..93b17d6
--- /dev/null
@@ -0,0 +1,37 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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 (file)
index 0000000..fac64e5
--- /dev/null
@@ -0,0 +1,25 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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 (file)
index 0000000..14ecd24
--- /dev/null
@@ -0,0 +1,43 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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 (file)
index 0000000..49ee439
--- /dev/null
@@ -0,0 +1,76 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license 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; } }
+}
+