diff options
author | Fei Peng <fei.peng@intel.com> | 2018-08-06 15:10:50 -0700 |
---|---|---|
committer | Tanner Gooding <tagoo@outlook.com> | 2018-08-10 13:26:03 -0700 |
commit | b9028b31caba6e69d1fab99bd1219ae879782802 (patch) | |
tree | 1dddf91ee9606cf661f5b52525fa04279bb735b4 /tests/src/JIT/Performance | |
parent | a69e1653ed0734cb0d0192abe302978ee0115b4d (diff) | |
download | coreclr-b9028b31caba6e69d1fab99bd1219ae879782802.tar.gz coreclr-b9028b31caba6e69d1fab99bd1219ae879782802.tar.bz2 coreclr-b9028b31caba6e69d1fab99bd1219ae879782802.zip |
Add SoA raytracer as a CQ test for Intel hardware intrinsic
Diffstat (limited to 'tests/src/JIT/Performance')
20 files changed, 1364 insertions, 0 deletions
diff --git a/tests/src/JIT/Performance/CodeQuality/HWIntrinsic/X86/PacketTracer/Camera.cs b/tests/src/JIT/Performance/CodeQuality/HWIntrinsic/X86/PacketTracer/Camera.cs new file mode 100644 index 0000000000..42f3c8baac --- /dev/null +++ b/tests/src/JIT/Performance/CodeQuality/HWIntrinsic/X86/PacketTracer/Camera.cs @@ -0,0 +1,30 @@ +// 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.Runtime.Intrinsics.X86; +using static System.Runtime.Intrinsics.X86.Avx; +using System.Runtime.Intrinsics; +internal class Camera +{ + + public Camera(VectorPacket256 pos, VectorPacket256 forward, VectorPacket256 up, VectorPacket256 right) { Pos = pos; Forward = forward; Up = up; Right = right; } + + public VectorPacket256 Pos; + public VectorPacket256 Forward; + public VectorPacket256 Up; + public VectorPacket256 Right; + + public static Camera Create(VectorPacket256 pos, VectorPacket256 lookAt) + { + VectorPacket256 forward = (lookAt - pos).Normalize(); + VectorPacket256 down = new VectorPacket256(SetZeroVector256<float>(), SetAllVector256<float>(-1), SetZeroVector256<float>()); + Vector256<float> OnePointFive = SetAllVector256<float>(1.5f); + VectorPacket256 right = OnePointFive * VectorPacket256.CrossProduct(forward, down).Normalize(); + VectorPacket256 up = OnePointFive * VectorPacket256.CrossProduct(forward, right).Normalize(); + + return new Camera(pos, forward, up, right); + } + +} diff --git a/tests/src/JIT/Performance/CodeQuality/HWIntrinsic/X86/PacketTracer/Color.cs b/tests/src/JIT/Performance/CodeQuality/HWIntrinsic/X86/PacketTracer/Color.cs new file mode 100644 index 0000000000..85d19d6891 --- /dev/null +++ b/tests/src/JIT/Performance/CodeQuality/HWIntrinsic/X86/PacketTracer/Color.cs @@ -0,0 +1,20 @@ +// 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 Color +{ + public float R { get; } + public float G { get; } + public float B { get; } + + public static readonly Color Background = new Color(0, 0, 0); + + public Color(float _r, float _g, float _b) + { + R = _r; + G = _g; + B = _b; + } +} diff --git a/tests/src/JIT/Performance/CodeQuality/HWIntrinsic/X86/PacketTracer/ColorPacket.cs b/tests/src/JIT/Performance/CodeQuality/HWIntrinsic/X86/PacketTracer/ColorPacket.cs new file mode 100644 index 0000000000..3aa36171aa --- /dev/null +++ b/tests/src/JIT/Performance/CodeQuality/HWIntrinsic/X86/PacketTracer/ColorPacket.cs @@ -0,0 +1,58 @@ +// 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 static System.Runtime.Intrinsics.X86.Avx; +using System.Runtime.Intrinsics.X86; +using System.Runtime.Intrinsics; +using System.Runtime.CompilerServices; + +using ColorPacket256 = VectorPacket256; + +internal static class ColorPacket256Helper +{ + + private static readonly Vector256<float> One = SetAllVector256<float>(1.0f); + private static readonly Vector256<float> Max = SetAllVector256<float>(255.0f); + public static Int32RGBPacket256 ConvertToIntRGB(this VectorPacket256 colors) + { + + var rsMask = Compare(colors.Xs, One, FloatComparisonMode.GreaterThanOrderedNonSignaling); + var gsMask = Compare(colors.Ys, One, FloatComparisonMode.GreaterThanOrderedNonSignaling); + var bsMask = Compare(colors.Zs, One, FloatComparisonMode.GreaterThanOrderedNonSignaling); + + var rs = BlendVariable(colors.Xs, One, rsMask); + var gs = BlendVariable(colors.Ys, One, gsMask); + var bs = BlendVariable(colors.Zs, One, bsMask); + + var rsInt = ConvertToVector256Int32(Multiply(rs, Max)); + var gsInt = ConvertToVector256Int32(Multiply(gs, Max)); + var bsInt = ConvertToVector256Int32(Multiply(bs, Max)); + + return new Int32RGBPacket256(rsInt, gsInt, bsInt); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ColorPacket256 Times(ColorPacket256 left, ColorPacket256 right) + { + return new VectorPacket256(Multiply(left.Xs, right.Xs), Multiply(left.Ys, right.Ys), Multiply(left.Zs, right.Zs)); + } + + public static readonly ColorPacket256 BackgroundColor = new ColorPacket256(SetZeroVector256<float>()); + public static readonly ColorPacket256 DefaultColor = new ColorPacket256(SetZeroVector256<float>()); +} + +internal struct Int32RGBPacket256 +{ + public Vector256<int> Rs; + public Vector256<int> Gs; + public Vector256<int> Bs; + + public Int32RGBPacket256(Vector256<int> rs, Vector256<int> gs, Vector256<int> bs) + { + Rs = rs; + Gs = gs; + Bs = bs; + } +} diff --git a/tests/src/JIT/Performance/CodeQuality/HWIntrinsic/X86/PacketTracer/Intersections.cs b/tests/src/JIT/Performance/CodeQuality/HWIntrinsic/X86/PacketTracer/Intersections.cs new file mode 100644 index 0000000000..54e4d2d4dc --- /dev/null +++ b/tests/src/JIT/Performance/CodeQuality/HWIntrinsic/X86/PacketTracer/Intersections.cs @@ -0,0 +1,43 @@ +// 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 static System.Runtime.Intrinsics.X86.Avx; +using static System.Runtime.Intrinsics.X86.Avx2; +using System.Runtime.Intrinsics.X86; +using System.Runtime.Intrinsics; +using System.Runtime.CompilerServices; +using System; + +internal struct Intersections +{ + public Vector256<float> Distances; + public Vector256<int> ThingIndices; + + public static readonly Vector256<float> NullDistance = SetAllVector256<float>(float.MaxValue); + public static readonly Vector256<int> NullIndex = SetAllVector256<int>(-1); + + public Intersections(Vector256<float> dis, Vector256<int> things) + { + Distances = dis; + ThingIndices = things; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool AllNullIntersections() + { + return AllNullIntersections(Distances); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool AllNullIntersections(Vector256<float> dis) + { + var cmp = Compare(dis, NullDistance, FloatComparisonMode.EqualOrderedNonSignaling); + var zero = SetZeroVector256<int>(); + // efficiently generate an all-one mask vector by lower latency AVX2 ComapreEqual + var mask = Avx2.CompareEqual(zero, zero); + return TestC(cmp, StaticCast<int, float>(mask)); + } + +} diff --git a/tests/src/JIT/Performance/CodeQuality/HWIntrinsic/X86/PacketTracer/LightPacket.cs b/tests/src/JIT/Performance/CodeQuality/HWIntrinsic/X86/PacketTracer/LightPacket.cs new file mode 100644 index 0000000000..0b12ca434a --- /dev/null +++ b/tests/src/JIT/Performance/CodeQuality/HWIntrinsic/X86/PacketTracer/LightPacket.cs @@ -0,0 +1,20 @@ +// 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.Runtime.CompilerServices; +using ColorPacket256 = VectorPacket256; + +internal class LightPacket256 +{ + public VectorPacket256 Positions; + public ColorPacket256 Colors; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public LightPacket256(Vector pos, Color col) + { + Positions = new VectorPacket256(pos.X, pos.Y, pos.Z); + Colors = new ColorPacket256(col.R, col.G, col.B); + } +} diff --git a/tests/src/JIT/Performance/CodeQuality/HWIntrinsic/X86/PacketTracer/ObjectPacket.cs b/tests/src/JIT/Performance/CodeQuality/HWIntrinsic/X86/PacketTracer/ObjectPacket.cs new file mode 100644 index 0000000000..85bd1a0800 --- /dev/null +++ b/tests/src/JIT/Performance/CodeQuality/HWIntrinsic/X86/PacketTracer/ObjectPacket.cs @@ -0,0 +1,18 @@ +// 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.Runtime.Intrinsics; + +internal abstract class ObjectPacket256 +{ + public Surface Surface { get; } + public abstract Vector256<float> Intersect(RayPacket256 rayPacket256); + public abstract VectorPacket256 Normals(VectorPacket256 pos); + + public ObjectPacket256(Surface surface) + { + Surface = surface; + } +} diff --git a/tests/src/JIT/Performance/CodeQuality/HWIntrinsic/X86/PacketTracer/ObjectPool.cs b/tests/src/JIT/Performance/CodeQuality/HWIntrinsic/X86/PacketTracer/ObjectPool.cs new file mode 100644 index 0000000000..29be23238b --- /dev/null +++ b/tests/src/JIT/Performance/CodeQuality/HWIntrinsic/X86/PacketTracer/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/HWIntrinsic/X86/PacketTracer/PacketTracer.cs b/tests/src/JIT/Performance/CodeQuality/HWIntrinsic/X86/PacketTracer/PacketTracer.cs new file mode 100644 index 0000000000..ba52a472a0 --- /dev/null +++ b/tests/src/JIT/Performance/CodeQuality/HWIntrinsic/X86/PacketTracer/PacketTracer.cs @@ -0,0 +1,243 @@ +// 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.Runtime.Intrinsics.X86; +using static System.Runtime.Intrinsics.X86.Avx; +using static System.Runtime.Intrinsics.X86.Avx2; +using static System.Runtime.Intrinsics.X86.Sse2; +using System.Runtime.Intrinsics; +using System; + +using ColorPacket256 = VectorPacket256; + +internal class Packet256Tracer +{ + public int Width { get; } + public int Height { get; } + private static readonly int MaxDepth = 5; + + private static readonly Vector256<float> SevenToZero = SetVector256(7f, 6f, 5f, 4f, 3f, 2f, 1f, 0f); + + public Packet256Tracer(int width, int height) + { + if ((width % VectorPacket256.Packet256Size) != 0) + { + width += VectorPacket256.Packet256Size - (width % VectorPacket256.Packet256Size); + } + Width = width; + Height = height; + } + + internal unsafe void RenderVectorized(Scene scene, int* rgb) + { + Camera camera = scene.Camera; + // Iterate y then x in order to preserve cache locality. + for (int y = 0; y < Height; y++) + { + int stride = y * Width; + for (int x = 0; x < Width; x += VectorPacket256.Packet256Size) + { + float fx = x; + Vector256<float> Xs = Add(SetAllVector256(fx), SevenToZero); + var dirs = GetPoints(Xs, SetAllVector256<float>(y), camera); + var rayPacket256 = new RayPacket256(camera.Pos, dirs); + var SoAcolors = TraceRay(rayPacket256, scene, depth: 0); + + var AoS = SoAcolors.Transpose(); + var intAoS = AoS.ConvertToIntRGB(); + + int* output = &rgb[(x + stride) * 3]; // Each pixel has 3 fields (RGB) + { + Store(output, intAoS.Rs); + Store(output + 8, intAoS.Gs); + Store(output + 16, intAoS.Bs); + } + + } + } + + } + + private ColorPacket256 TraceRay(RayPacket256 rayPacket256, Scene scene, int depth) + { + var isect = MinIntersections(rayPacket256, scene); + if (isect.AllNullIntersections()) + { + return ColorPacket256Helper.BackgroundColor; + } + var color = Shade(isect, rayPacket256, scene, depth); + var isNull = Compare(isect.Distances, Intersections.NullDistance, FloatComparisonMode.EqualOrderedNonSignaling); + var backgroundColor = ColorPacket256Helper.BackgroundColor.Xs; + return new ColorPacket256(BlendVariable(color.Xs, backgroundColor, isNull), + BlendVariable(color.Ys, backgroundColor, isNull), + BlendVariable(color.Zs, backgroundColor, isNull)); + } + + private Vector256<float> TestRay(RayPacket256 rayPacket256, Scene scene) + { + var isect = MinIntersections(rayPacket256, scene); + if (isect.AllNullIntersections()) + { + return SetZeroVector256<float>(); + } + var isNull = Compare(isect.Distances, Intersections.NullDistance, FloatComparisonMode.EqualOrderedNonSignaling); + return BlendVariable(isect.Distances, SetZeroVector256<float>(), isNull); + } + + private Intersections MinIntersections(RayPacket256 rayPacket256, Scene scene) + { + Intersections mins = new Intersections(Intersections.NullDistance, Intersections.NullIndex); + for (int i = 0; i < scene.Things.Length; i++) + { + Vector256<float> distance = scene.Things[i].Intersect(rayPacket256); + + if (!Intersections.AllNullIntersections(distance)) + { + var notNullMask = Compare(distance, Intersections.NullDistance, FloatComparisonMode.NotEqualOrderedNonSignaling); + var nullMinMask = Compare(mins.Distances, Intersections.NullDistance, FloatComparisonMode.EqualOrderedNonSignaling); + + var lessMinMask = Compare(mins.Distances, distance, FloatComparisonMode.GreaterThanOrderedNonSignaling); + var minMask = And(notNullMask, Or(nullMinMask, lessMinMask)); + var minDis = BlendVariable(mins.Distances, distance, minMask); + var minIndices = StaticCast<float, int>(BlendVariable(StaticCast<int, float>(mins.ThingIndices), + StaticCast<int, float>(SetAllVector256<int>(i)), + minMask)); + mins.Distances = minDis; + mins.ThingIndices = minIndices; + } + } + return mins; + } + + private ColorPacket256 Shade(Intersections isect, RayPacket256 rays, Scene scene, int depth) + { + + var ds = rays.Dirs; + var pos = isect.Distances * ds + rays.Starts; + var normals = scene.Normals(isect.ThingIndices, pos); + var reflectDirs = ds - (Multiply(VectorPacket256.DotProduct(normals, ds), SetAllVector256<float>(2)) * normals); + var colors = GetNaturalColor(isect.ThingIndices, pos, normals, reflectDirs, scene); + + if (depth >= MaxDepth) + { + return colors + new ColorPacket256(.5f, .5f, .5f); + } + + return colors + GetReflectionColor(isect.ThingIndices, pos + (SetAllVector256<float>(0.001f) * reflectDirs), normals, reflectDirs, scene, depth); + } + + private ColorPacket256 GetNaturalColor(Vector256<int> things, VectorPacket256 pos, VectorPacket256 norms, VectorPacket256 rds, Scene scene) + { + var colors = ColorPacket256Helper.DefaultColor; + for (int i = 0; i < scene.Lights.Length; i++) + { + var lights = scene.Lights[i]; + var zero = SetZeroVector256<float>(); + var colorPacket = lights.Colors; + var ldis = lights.Positions - pos; + var livec = ldis.Normalize(); + var neatIsectDis = TestRay(new RayPacket256(pos, livec), scene); + + // is in shadow? + var mask1 = Compare(neatIsectDis, ldis.Lengths, FloatComparisonMode.LessThanOrEqualOrderedNonSignaling); + var mask2 = Compare(neatIsectDis, zero, FloatComparisonMode.NotEqualOrderedNonSignaling); + var isInShadow = And(mask1, mask2); + + Vector256<float> illum = VectorPacket256.DotProduct(livec, norms); + Vector256<float> illumGraterThanZero = Compare(illum, zero, FloatComparisonMode.GreaterThanOrderedNonSignaling); + var tmpColor1 = illum * colorPacket; + var defaultRGB = zero; + Vector256<float> lcolorR = BlendVariable(defaultRGB, tmpColor1.Xs, illumGraterThanZero); + Vector256<float> lcolorG = BlendVariable(defaultRGB, tmpColor1.Ys, illumGraterThanZero); + Vector256<float> lcolorB = BlendVariable(defaultRGB, tmpColor1.Zs, illumGraterThanZero); + ColorPacket256 lcolor = new ColorPacket256(lcolorR, lcolorG, lcolorB); + + Vector256<float> specular = VectorPacket256.DotProduct(livec, rds.Normalize()); + Vector256<float> specularGraterThanZero = Compare(specular, zero, FloatComparisonMode.GreaterThanOrderedNonSignaling); + + var difColor = new ColorPacket256(1, 1, 1); + var splColor = new ColorPacket256(1, 1, 1); + var roughness = SetAllVector256<float>(1); + + for (int j = 0; j < scene.Things.Length; j++) + { + Vector256<float> thingMask = StaticCast<int, float>(CompareEqual(things, SetAllVector256<int>(j))); + var rgh = SetAllVector256<float>(scene.Things[j].Surface.Roughness); + var dif = scene.Things[j].Surface.Diffuse(pos); + var spl = scene.Things[j].Surface.Specular; + + roughness = BlendVariable(roughness, rgh, thingMask); + + difColor.Xs = BlendVariable(difColor.Xs, dif.Xs, thingMask); + difColor.Ys = BlendVariable(difColor.Ys, dif.Ys, thingMask); + difColor.Zs = BlendVariable(difColor.Zs, dif.Zs, thingMask); + + splColor.Xs = BlendVariable(splColor.Xs, spl.Xs, thingMask); + splColor.Ys = BlendVariable(splColor.Ys, spl.Ys, thingMask); + splColor.Zs = BlendVariable(splColor.Zs, spl.Zs, thingMask); + } + + var tmpColor2 = VectorMath.Pow(specular, roughness) * colorPacket; + Vector256<float> scolorR = BlendVariable(defaultRGB, tmpColor2.Xs, specularGraterThanZero); + Vector256<float> scolorG = BlendVariable(defaultRGB, tmpColor2.Ys, specularGraterThanZero); + Vector256<float> scolorB = BlendVariable(defaultRGB, tmpColor2.Zs, specularGraterThanZero); + ColorPacket256 scolor = new ColorPacket256(scolorR, scolorG, scolorB); + + var oldColor = colors; + + colors = colors + ColorPacket256Helper.Times(difColor, lcolor) + ColorPacket256Helper.Times(splColor, scolor); + + colors = new ColorPacket256(BlendVariable(colors.Xs, oldColor.Xs, isInShadow), BlendVariable(colors.Ys, oldColor.Ys, isInShadow), BlendVariable(colors.Zs, oldColor.Zs, isInShadow)); + + } + return colors; + } + + private ColorPacket256 GetReflectionColor(Vector256<int> things, VectorPacket256 pos, VectorPacket256 norms, VectorPacket256 rds, Scene scene, int depth) + { + return scene.Reflect(things, pos) * TraceRay(new RayPacket256(pos, rds), scene, depth + 1); + } + + private readonly static Vector256<float> ConstTwo = SetAllVector256(2.0f); + + private VectorPacket256 GetPoints(Vector256<float> x, Vector256<float> y, Camera camera) + { + var widthVector = SetAllVector256<float>(Width); + var heightVector = SetAllVector256<float>(Height); + + var widthRate1 = Divide(widthVector, ConstTwo); + var widthRate2 = Multiply(widthVector, ConstTwo); + + var heightRate1 = Divide(heightVector, ConstTwo); + var heightRate2 = Multiply(heightVector, ConstTwo); + + var recenteredX = Divide(Subtract(x, widthRate1), widthRate2); + var recenteredY = Subtract(SetZeroVector256<float>(), Divide(Subtract(y, heightRate1), heightRate2)); + + var result = camera.Forward + (recenteredX * camera.Right) + (recenteredY * camera.Up); + + return result.Normalize(); + } + + internal readonly Scene DefaultScene = CreateDefaultScene(); + + private static Scene CreateDefaultScene() + { + ObjectPacket256[] things = { + new SpherePacket256(new VectorPacket256(-0.5f, 1f, 1.5f), SetAllVector256(0.5f), Surfaces.MatteShiny), + new SpherePacket256(new VectorPacket256(0f, 1f, -0.25f), SetAllVector256(1f), Surfaces.Shiny), + new PlanePacket256((new VectorPacket256(0, 1, 0)), SetAllVector256(0f), Surfaces.CheckerBoard) + }; + + LightPacket256[] lights = { + new LightPacket256(new Vector(-2f,2.5f,0f),new Color(.5f,.45f,.41f)), + new LightPacket256(new Vector(2,4.5f,2), new Color(.99f,.95f,.8f)) + }; + + Camera camera = Camera.Create(new VectorPacket256(2.75f, 2f, 3.75f), new VectorPacket256(-0.6f, .5f, 0f)); + + return new Scene(things, lights, camera); + } +} diff --git a/tests/src/JIT/Performance/CodeQuality/HWIntrinsic/X86/PacketTracer/PacketTracer.csproj b/tests/src/JIT/Performance/CodeQuality/HWIntrinsic/X86/PacketTracer/PacketTracer.csproj new file mode 100644 index 0000000000..54118803a9 --- /dev/null +++ b/tests/src/JIT/Performance/CodeQuality/HWIntrinsic/X86/PacketTracer/PacketTracer.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.1</SchemaVersion> + <ProjectGuid>{95DFC527-4DC1-495E-97D7-E94EE1F7140D}</ProjectGuid> + <OutputType>Exe</OutputType> + <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids> + <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir> + <NuGetTargetMoniker>.NETStandard,Version=v1.4</NuGetTargetMoniker> + <NuGetTargetMonikerShort>netstandard1.4</NuGetTargetMonikerShort> + <AllowUnsafeBlocks>true</AllowUnsafeBlocks> + </PropertyGroup> + <!-- Default configurations to help VS understand the configurations --> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "></PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' " /> + <PropertyGroup> + <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="Program.cs" /> + <Compile Include="Camera.cs" /> + <Compile Include="Color.cs" /> + <Compile Include="ColorPacket.cs" /> + <Compile Include="Intersections.cs" /> + <Compile Include="LightPacket.cs" /> + <Compile Include="ObjectPacket.cs" /> + <Compile Include="ObjectPool.cs" /> + <Compile Include="PacketTracer.cs" /> + <Compile Include="PlanePacket.cs" /> + <Compile Include="ProducerConsumerCollectionBase.cs" /> + <Compile Include="RayPacket.cs" /> + <Compile Include="Scene.cs" /> + <Compile Include="SpherePacket.cs" /> + <Compile Include="Surface.cs" /> + <Compile Include="Surfaces.cs" /> + <Compile Include="Vector.cs" /> + <Compile Include="VectorPacket.cs" /> + <Compile Include="VectorMath.cs" /> + </ItemGroup> + <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" /> + <PropertyGroup Condition=" '$(MsBuildProjectDirOverride)' != '' "></PropertyGroup> + <PropertyGroup> + <ProjectAssetsFile>$(JitPackagesConfigFileDirectory)benchmark+intrinsic\obj\project.assets.json</ProjectAssetsFile> + </PropertyGroup> +</Project> diff --git a/tests/src/JIT/Performance/CodeQuality/HWIntrinsic/X86/PacketTracer/PlanePacket.cs b/tests/src/JIT/Performance/CodeQuality/HWIntrinsic/X86/PacketTracer/PlanePacket.cs new file mode 100644 index 0000000000..0269367955 --- /dev/null +++ b/tests/src/JIT/Performance/CodeQuality/HWIntrinsic/X86/PacketTracer/PlanePacket.cs @@ -0,0 +1,35 @@ +// 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.Runtime.Intrinsics.X86; +using static System.Runtime.Intrinsics.X86.Avx; +using System.Runtime.Intrinsics; +using System.Runtime.CompilerServices; + +internal sealed class PlanePacket256 : ObjectPacket256 +{ + public VectorPacket256 Norms; + public Vector256<float> Offsets; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public PlanePacket256(VectorPacket256 norms, Vector256<float> offsets, Surface surface) : base(surface) + { + Norms = norms; + Offsets = offsets; + } + + public override VectorPacket256 Normals(VectorPacket256 pos) + { + return Norms; + } + + public override Vector256<float> Intersect(RayPacket256 rayPacket256) + { + var denom = VectorPacket256.DotProduct(Norms, rayPacket256.Dirs); + var dist = Divide(Add(VectorPacket256.DotProduct(Norms, rayPacket256.Starts), Offsets), Subtract(SetZeroVector256<float>(), denom)); + var gtMask = Compare(denom, SetZeroVector256<float>(), FloatComparisonMode.GreaterThanOrderedNonSignaling); + return BlendVariable(dist, Intersections.NullDistance, gtMask); + } +} diff --git a/tests/src/JIT/Performance/CodeQuality/HWIntrinsic/X86/PacketTracer/ProducerConsumerCollectionBase.cs b/tests/src/JIT/Performance/CodeQuality/HWIntrinsic/X86/PacketTracer/ProducerConsumerCollectionBase.cs new file mode 100644 index 0000000000..fee736397d --- /dev/null +++ b/tests/src/JIT/Performance/CodeQuality/HWIntrinsic/X86/PacketTracer/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/HWIntrinsic/X86/PacketTracer/Program.cs b/tests/src/JIT/Performance/CodeQuality/HWIntrinsic/X86/PacketTracer/Program.cs new file mode 100644 index 0000000000..3ae923a9b1 --- /dev/null +++ b/tests/src/JIT/Performance/CodeQuality/HWIntrinsic/X86/PacketTracer/Program.cs @@ -0,0 +1,177 @@ +// 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.Diagnostics; +using System.Threading; +using System.Threading.Tasks; +using System.Collections.Concurrent; +using System.Runtime.Intrinsics.X86; +//using Microsoft.Xunit.Performance; + +//[assembly: OptimizeForBenchmarks] + +class Program +{ +#if DEBUG + + private const int RunningTime = 200; + private const int Width = 248; + private const int Height = 248; + private const int Iterations = 1; + private const int MaxIterations = 1000; + +#else + + private const int RunningTime = 1000; + private const int Width = 248; + private const int Height = 248; + private const int Iterations = 7; + private const int MaxIterations = 1000; + +#endif + + private double _framesPerSecond; + private bool _parallel; + private bool _showThreads; + private static int _width, _height; + private int _degreeOfParallelism = Environment.ProcessorCount; + private int _frames; + private CancellationTokenSource _cancellation; + private ObjectPool<int[]> _freeBuffers; + + public Program() + { + _width = Width; + _height = Height; + _parallel = false; + _showThreads = false; + _freeBuffers = new ObjectPool<int[]>(() => new int[_width * 3 * _height]); // Each pixel has 3 fields (RGB) + } + + static unsafe int Main(string[] args) + { + if (Avx2.IsSupported) + { + var r = new Program(); + // We can use `RenderTo` to generate a picture in a PPM file for debugging + // r.RenderTo("./pic.ppm", true); + bool result = r.Run(); + return (result ? 100 : -1); + } + return 100; + } + + private void RenderTest() + { + _cancellation = new CancellationTokenSource(RunningTime); + RenderLoop(MaxIterations); + } + + private void RenderBench() + { + _cancellation = new CancellationTokenSource(); + RenderLoop(Iterations); + } + + private unsafe void RenderLoop(int iterations) + { + // Create a ray tracer, and create a reference to "sphere2" that we are going to bounce + var packetTracer = new Packet256Tracer(_width, _height); + var scene = packetTracer.DefaultScene; + var sphere2 = (SpherePacket256)scene.Things[0]; // The first item is assumed to be our sphere + var baseY = sphere2.Radiuses; + sphere2.Centers.Ys = sphere2.Radiuses; + + // 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 rgbBuffer = _freeBuffers.GetObject(); + + // Determine the new position of the sphere based on the current time elapsed + float dy2 = 0.8f * MathF.Abs(MathF.Sin((float)(totalTime.ElapsedMilliseconds * Math.PI / 3000))); + sphere2.Centers.Ys = Avx.Add(baseY, Avx.SetAllVector256(dy2)); + + // Render the scene + renderingTime.Reset(); + renderingTime.Start(); + ParallelOptions options = new ParallelOptions + { + MaxDegreeOfParallelism = _degreeOfParallelism, + CancellationToken = _cancellation.Token + }; + fixed (int* ptr = rgbBuffer) + { + packetTracer.RenderVectorized(scene, ptr); + } + + renderingTime.Stop(); + + _framesPerSecond = (1000.0 / renderingTime.ElapsedMilliseconds); + _freeBuffers.PutObject(rgbBuffer); + } + } + + public bool Run() + { + RenderTest(); + Console.WriteLine("{0} frames, {1} frames/sec", + _frames, + _framesPerSecond.ToString("F2")); + return true; + } + + private unsafe void RenderTo(string fileName, bool wirteToFile) + { + var packetTracer = new Packet256Tracer(_width, _height); + var scene = packetTracer.DefaultScene; + var rgb = new int[_width * 3 * _height]; + Stopwatch stopWatch = new Stopwatch(); + stopWatch.Start(); + fixed (int* ptr = rgb) + { + packetTracer.RenderVectorized(scene, ptr); + } + stopWatch.Stop(); + TimeSpan ts = stopWatch.Elapsed; + string elapsedTime = String.Format("{0:00}:{1:00}:{2:00}.{3:00}", + ts.Hours, ts.Minutes, ts.Seconds, + ts.Milliseconds / 10); + Console.WriteLine("RunTime " + elapsedTime); + + if (wirteToFile) + { + using (var file = new System.IO.StreamWriter(fileName)) + { + file.WriteLine("P3"); + file.WriteLine(_width + " " + _height); + file.WriteLine("255"); + + for (int i = 0; i < _height; i++) + { + for (int j = 0; j < _width; j++) + { + // Each pixel has 3 fields (RGB) + int pos = (i * _width + j) * 3; + file.Write(rgb[pos] + " " + rgb[pos + 1] + " " + rgb[pos + 2] + " "); + } + file.WriteLine(); + } + } + + } + } +} diff --git a/tests/src/JIT/Performance/CodeQuality/HWIntrinsic/X86/PacketTracer/RayPacket.cs b/tests/src/JIT/Performance/CodeQuality/HWIntrinsic/X86/PacketTracer/RayPacket.cs new file mode 100644 index 0000000000..75559c3461 --- /dev/null +++ b/tests/src/JIT/Performance/CodeQuality/HWIntrinsic/X86/PacketTracer/RayPacket.cs @@ -0,0 +1,19 @@ +// 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.Runtime.Intrinsics.X86; +using System.Runtime.Intrinsics; + +internal class RayPacket256 +{ + public VectorPacket256 Starts; + public VectorPacket256 Dirs; + + public RayPacket256(VectorPacket256 starts, VectorPacket256 dirs) + { + Starts = starts; + Dirs = dirs; + } +} diff --git a/tests/src/JIT/Performance/CodeQuality/HWIntrinsic/X86/PacketTracer/Scene.cs b/tests/src/JIT/Performance/CodeQuality/HWIntrinsic/X86/PacketTracer/Scene.cs new file mode 100644 index 0000000000..9edbb946b1 --- /dev/null +++ b/tests/src/JIT/Performance/CodeQuality/HWIntrinsic/X86/PacketTracer/Scene.cs @@ -0,0 +1,51 @@ +// 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.Runtime.Intrinsics.X86; +using static System.Runtime.Intrinsics.X86.Avx; +using static System.Runtime.Intrinsics.X86.Avx2; +using System.Runtime.Intrinsics; +using System.Runtime.CompilerServices; +using System; + +internal class Scene +{ + public ObjectPacket256[] Things; + public LightPacket256[] Lights; + public Camera Camera; + + public Scene(ObjectPacket256[] things, LightPacket256[] lights, Camera camera) { Things = things; Lights = lights; Camera = camera; } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public VectorPacket256 Normals(Vector256<int> things, VectorPacket256 pos) + { + VectorPacket256 norms = new VectorPacket256(1, 1, 1); + + for (int i = 0; i < Things.Length; i++) + { + Vector256<float> mask = StaticCast<int, float>(CompareEqual(things, SetAllVector256<int>(i))); + var n = Things[i].Normals(pos); + norms.Xs = BlendVariable(norms.Xs, n.Xs, mask); + norms.Ys = BlendVariable(norms.Ys, n.Ys, mask); + norms.Zs = BlendVariable(norms.Zs, n.Zs, mask); + } + + return norms; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector256<float> Reflect(Vector256<int> things, VectorPacket256 pos) + { + Vector256<float> rfl = SetAllVector256<float>(1); + for (int i = 0; i < Things.Length; i++) + { + Vector256<float> mask = StaticCast<int, float>(CompareEqual(things, SetAllVector256<int>(i))); + rfl = BlendVariable(rfl, Things[i].Surface.Reflect(pos), mask); + } + return rfl; + } + +} diff --git a/tests/src/JIT/Performance/CodeQuality/HWIntrinsic/X86/PacketTracer/SpherePacket.cs b/tests/src/JIT/Performance/CodeQuality/HWIntrinsic/X86/PacketTracer/SpherePacket.cs new file mode 100644 index 0000000000..b1e2b496a7 --- /dev/null +++ b/tests/src/JIT/Performance/CodeQuality/HWIntrinsic/X86/PacketTracer/SpherePacket.cs @@ -0,0 +1,40 @@ +// 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.Runtime.Intrinsics.X86; +using static System.Runtime.Intrinsics.X86.Avx; +using System.Runtime.Intrinsics; +using System.Runtime.CompilerServices; + +internal sealed class SpherePacket256 : ObjectPacket256 +{ + public VectorPacket256 Centers; + public Vector256<float> Radiuses; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public SpherePacket256(VectorPacket256 centers, Vector256<float> radiuses, Surface surface) : base(surface) + { + Centers = centers; + Radiuses = radiuses; + } + + public override VectorPacket256 Normals(VectorPacket256 pos) + { + return (pos - Centers).Normalize(); + } + + public override Vector256<float> Intersect(RayPacket256 rayPacket256) + { + var eo = Centers - rayPacket256.Starts; + var v = VectorPacket256.DotProduct(eo, rayPacket256.Dirs); + var zero = SetZeroVector256<float>(); + var vLessZeroMask = Compare(v, zero, FloatComparisonMode.LessThanOrderedNonSignaling); + var discs = Subtract(Multiply(Radiuses, Radiuses), Subtract(VectorPacket256.DotProduct(eo, eo), Multiply(v, v))); + var discLessZeroMask = Compare(discs, zero, FloatComparisonMode.LessThanOrderedNonSignaling); + var dists = BlendVariable(Subtract(v, Sqrt(discs)), zero, Or(vLessZeroMask, discLessZeroMask)); + var isZero = Compare(dists, zero, FloatComparisonMode.EqualOrderedNonSignaling); + return BlendVariable(dists, Intersections.NullDistance, isZero); + } +} diff --git a/tests/src/JIT/Performance/CodeQuality/HWIntrinsic/X86/PacketTracer/Surface.cs b/tests/src/JIT/Performance/CodeQuality/HWIntrinsic/X86/PacketTracer/Surface.cs new file mode 100644 index 0000000000..35e04bd842 --- /dev/null +++ b/tests/src/JIT/Performance/CodeQuality/HWIntrinsic/X86/PacketTracer/Surface.cs @@ -0,0 +1,27 @@ +// 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.Runtime.Intrinsics; +using System; +using ColorPacket256 = VectorPacket256; + +internal class Surface +{ + public Func<VectorPacket256, ColorPacket256> Diffuse; + public VectorPacket256 Specular; + public Func<VectorPacket256, Vector256<float>> Reflect; + public float Roughness; + + public Surface(Func<VectorPacket256, ColorPacket256> Diffuse, + VectorPacket256 Specular, + Func<VectorPacket256, Vector256<float>> Reflect, + float Roughness) + { + this.Diffuse = Diffuse; + this.Specular = Specular; + this.Reflect = Reflect; + this.Roughness = Roughness; + } +} diff --git a/tests/src/JIT/Performance/CodeQuality/HWIntrinsic/X86/PacketTracer/Surfaces.cs b/tests/src/JIT/Performance/CodeQuality/HWIntrinsic/X86/PacketTracer/Surfaces.cs new file mode 100644 index 0000000000..7e467ae34e --- /dev/null +++ b/tests/src/JIT/Performance/CodeQuality/HWIntrinsic/X86/PacketTracer/Surfaces.cs @@ -0,0 +1,61 @@ +// 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.Runtime.Intrinsics; +using System.Runtime.Intrinsics.X86; +using static System.Runtime.Intrinsics.X86.Avx; +using ColorPacket256 = VectorPacket256; + +using System; + +internal static class Surfaces +{ + + private static readonly ColorPacket256 White = new ColorPacket256(SetAllVector256(1.0f)); + private static readonly ColorPacket256 Black = new ColorPacket256(0.02f, 0.0f, 0.14f); + // Only works with X-Z plane. + public static readonly Surface CheckerBoard = + new Surface( + delegate (VectorPacket256 pos) + { + var floored = ConvertToVector256Int32(Add(Floor(pos.Zs), Floor(pos.Xs))); + var modMask = SetAllVector256<int>(1); + var evenMaskint = Avx2.And(floored, modMask); + var evenMask = Avx2.CompareEqual(evenMaskint, modMask); + + var resultX = BlendVariable(Black.Xs, White.Xs, StaticCast<int, float>(evenMask)); + var resultY = BlendVariable(Black.Ys, White.Ys, StaticCast<int, float>(evenMask)); + var resultZ = BlendVariable(Black.Zs, White.Zs, StaticCast<int, float>(evenMask)); + + return new ColorPacket256(resultX, resultY, resultZ); + }, + new VectorPacket256(1f, 1f, 1f), + delegate (VectorPacket256 pos) + { + var floored = ConvertToVector256Int32(Add(Floor(pos.Zs), Floor(pos.Xs))); + var modMask = SetAllVector256<int>(1); + var evenMaskUint = Avx2.And(floored, modMask); + var evenMask = Avx2.CompareEqual(evenMaskUint, modMask); + + return BlendVariable(SetAllVector256(0.5f), SetAllVector256(0.1f), StaticCast<int, float>(evenMask)); + }, + 150f); + + + + public static readonly Surface Shiny = + new Surface( + delegate (VectorPacket256 pos) { return new VectorPacket256(1f, 1f, 1f); }, + new VectorPacket256(.5f, .5f, .5f), + delegate (VectorPacket256 pos) { return SetAllVector256<float>(0.7f); }, + 250f); + + public static readonly Surface MatteShiny = + new Surface( + delegate (VectorPacket256 pos) { return new VectorPacket256(1f, 1f, 1f); }, + new VectorPacket256(.25f, .25f, .25f), + delegate (VectorPacket256 pos) { return SetAllVector256<float>(0.7f); }, + 250f); +} diff --git a/tests/src/JIT/Performance/CodeQuality/HWIntrinsic/X86/PacketTracer/Vector.cs b/tests/src/JIT/Performance/CodeQuality/HWIntrinsic/X86/PacketTracer/Vector.cs new file mode 100644 index 0000000000..ec27d823f8 --- /dev/null +++ b/tests/src/JIT/Performance/CodeQuality/HWIntrinsic/X86/PacketTracer/Vector.cs @@ -0,0 +1,18 @@ +// 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 Vector +{ + public float X { get; set; } + public float Y { get; set; } + public float Z { get; set; } + + public Vector(float x, float y, float z) + { + X = x; + Y = y; + Z = z; + } +} diff --git a/tests/src/JIT/Performance/CodeQuality/HWIntrinsic/X86/PacketTracer/VectorMath.cs b/tests/src/JIT/Performance/CodeQuality/HWIntrinsic/X86/PacketTracer/VectorMath.cs new file mode 100644 index 0000000000..51c87605aa --- /dev/null +++ b/tests/src/JIT/Performance/CodeQuality/HWIntrinsic/X86/PacketTracer/VectorMath.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 static System.Runtime.Intrinsics.X86.Avx; +using System.Runtime.Intrinsics.X86; +using System.Runtime.Intrinsics; +using System.Runtime.CompilerServices; +using System; + +public static class VectorMath +{ + static readonly Vector256<float> MaxValue = SetAllVector256<float>(88.0f); + static readonly Vector256<float> MinValue = SetAllVector256<float>(-88.0f); + static readonly Vector256<float> Log2 = SetAllVector256<float>(1.44269502f); + static readonly Vector256<float> C1 = SetAllVector256<float>(0.693359375f); + static readonly Vector256<float> C2 = SetAllVector256<float>(-0.0002121944417f); + static readonly Vector256<float> P0 = SetAllVector256<float>(0.0001987569121f); + static readonly Vector256<float> P1 = SetAllVector256<float>(0.001398199936f); + static readonly Vector256<float> P2 = SetAllVector256<float>(0.008333452046f); + static readonly Vector256<float> P3 = SetAllVector256<float>(0.04166579619f); + static readonly Vector256<float> P4 = SetAllVector256<float>(0.1666666567f); + static readonly Vector256<float> LogP0 = SetAllVector256<float>(0.07037683576f); + static readonly Vector256<float> LogP1 = SetAllVector256<float>(-0.1151461005f); + static readonly Vector256<float> LogP2 = SetAllVector256<float>(0.1167699844f); + static readonly Vector256<float> LogP3 = SetAllVector256<float>(-0.1242014095f); + static readonly Vector256<float> LogP4 = SetAllVector256<float>(0.1424932331f); + static readonly Vector256<float> LogP5 = SetAllVector256<float>(-0.1666805744f); + static readonly Vector256<float> LogP6 = SetAllVector256<float>(0.2000071406f); + static readonly Vector256<float> LogP7 = SetAllVector256<float>(-0.2499999404f); + static readonly Vector256<float> LogP8 = SetAllVector256<float>(0.3333333135f); + static readonly Vector256<float> LogQ1 = SetAllVector256<float>(-0.0002121944417f); + static readonly Vector256<float> LogQ2 = SetAllVector256<float>(0.693359375f); + static readonly Vector256<float> Point5 = SetAllVector256<float>(0.5f); + static readonly Vector256<float> Sqrthf = SetAllVector256<float>(0.7071067691f); + static readonly Vector256<float> One = SetAllVector256<float>(1.0f); + static readonly Vector256<int> Ox7 = SetAllVector256<int>(127); + static readonly Vector256<int> MinNormPos = SetAllVector256<int>(8388608); + static readonly Vector256<int> MantMask = SetAllVector256<int>(-2139095041); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256<float> Pow(Vector256<float> left, Vector256<float> right) + { + return Exp(Multiply(right, Log(left))); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256<float> Exp(Vector256<float> value) + { + value = Min(value, MaxValue); + value = Max(value, MinValue); + Vector256<float> fx = Multiply(value, Log2); + fx = Floor(Add(fx, Point5)); + + Vector256<float> tmp = Multiply(fx, C1); + Vector256<float> z = Multiply(fx, C2); + Vector256<float> x = Subtract(value, tmp); + x = Subtract(x, z); + z = Multiply(x, x); + Vector256<float> y = P0; + y = Add(Multiply(y, x), P1); + y = Add(Multiply(y, x), P2); + y = Add(Multiply(y, x), P3); + y = Add(Multiply(y, x), P4); + y = Add(Multiply(y, x), Point5); + y = Add(Add(Multiply(y, z), x), One); + + Vector256<int> pow2n = ConvertToVector256Int32(fx); + pow2n = Avx2.Add(pow2n, Ox7); + pow2n = Avx2.ShiftLeftLogical(pow2n, 23); + + return Multiply(y, StaticCast<int, float>(pow2n)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256<float> Log(Vector256<float> value) + { + Vector256<float> invalidMask = Compare(value, SetZeroVector256<float>(), FloatComparisonMode.LessThanOrEqualOrderedNonSignaling); + Vector256<float> x = Max(value, StaticCast<int, float>(MinNormPos)); + Vector256<int> ei = Avx2.ShiftRightLogical(StaticCast<float, int>(x), 23); + x = Or(And(x, StaticCast<int, float>(MantMask)), Point5); + ei = Avx2.Subtract(ei, Ox7); + Vector256<float> e = Add(ConvertToVector256Single(ei), One); + Vector256<float> mask = Compare(x, Sqrthf, FloatComparisonMode.LessThanOrderedNonSignaling); + Vector256<float> tmp = And(x, mask); + x = Subtract(x, One); + e = Subtract(e, And(One, mask)); + x = Add(x, tmp); + Vector256<float> z = Multiply(x, x); + Vector256<float> y = LogP0; + y = Add(Multiply(y, x), LogP1); + y = Add(Multiply(y, x), LogP2); + y = Add(Multiply(y, x), LogP3); + y = Add(Multiply(y, x), LogP4); + y = Add(Multiply(y, x), LogP5); + y = Add(Multiply(y, x), LogP6); + y = Add(Multiply(y, x), LogP7); + y = Add(Multiply(y, x), LogP8); + y = Multiply(Multiply(y, x), z); + y = Add(y, Multiply(e, LogQ1)); + y = Subtract(y, Multiply(z, Point5)); + x = Add(Add(x, y), Multiply(e, LogQ2)); + return Or(x, invalidMask); + } + +} diff --git a/tests/src/JIT/Performance/CodeQuality/HWIntrinsic/X86/PacketTracer/VectorPacket.cs b/tests/src/JIT/Performance/CodeQuality/HWIntrinsic/X86/PacketTracer/VectorPacket.cs new file mode 100644 index 0000000000..e0c2cb5239 --- /dev/null +++ b/tests/src/JIT/Performance/CodeQuality/HWIntrinsic/X86/PacketTracer/VectorPacket.cs @@ -0,0 +1,164 @@ +// 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 static System.Runtime.Intrinsics.X86.Avx; +using static System.Runtime.Intrinsics.X86.Sse; +using System.Runtime.Intrinsics.X86; +using System.Runtime.Intrinsics; +using System.Runtime.CompilerServices; +using System; + +internal class VectorPacket256 +{ + public Vector256<float> Xs; + public Vector256<float> Ys; + public Vector256<float> Zs; + public Vector256<float> Lengths + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + return Sqrt(DotProduct(this, this)); + } + } + + + public readonly static int Packet256Size = 8; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public VectorPacket256(Vector256<float> init) + { + Xs = init; + Ys = init; + Zs = init; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public VectorPacket256(float xs, float ys, float zs) + { + Xs = SetAllVector256(xs); + Ys = SetAllVector256(ys); + Zs = SetAllVector256(zs); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public VectorPacket256(Vector256<float> _Xs, Vector256<float> _ys, Vector256<float> _Zs) + { + Xs = _Xs; + Ys = _ys; + Zs = _Zs; + } + + // Convert AoS vectors to SoA Packet256 + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe VectorPacket256(float* vectors) + { + Vector256<float> m03 = ExtendToVector256<float>(LoadVector128(&vectors[0])); // load lower halves + Vector256<float> m14 = ExtendToVector256<float>(LoadVector128(&vectors[4])); + Vector256<float> m25 = ExtendToVector256<float>(LoadVector128(&vectors[8])); + m03 = InsertVector128(m03, &vectors[12], 1); // load higher halves + m14 = InsertVector128(m14, &vectors[16], 1); + m25 = InsertVector128(m25, &vectors[20], 1); + + var xy = Shuffle(m14, m25, 2 << 6 | 1 << 4 | 3 << 2 | 2); + var yz = Shuffle(m03, m14, 1 << 6 | 0 << 4 | 2 << 2 | 1); + var _Xs = Shuffle(m03, xy, 2 << 6 | 0 << 4 | 3 << 2 | 0); + var _ys = Shuffle(yz, xy, 3 << 6 | 1 << 4 | 2 << 2 | 0); + var _Zs = Shuffle(yz, m25, 3 << 6 | 0 << 4 | 3 << 2 | 1); + + Xs = _Xs; + Ys = _ys; + Zs = _Zs; + } + + // Convert SoA VectorPacket256 to AoS + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public VectorPacket256 Transpose() + { + var rxy = Shuffle(Xs, Ys, 2 << 6 | 0 << 4 | 2 << 2 | 0); + var ryz = Shuffle(Ys, Zs, 3 << 6 | 1 << 4 | 3 << 2 | 1); + var rzx = Shuffle(Zs, Xs, 3 << 6 | 1 << 4 | 2 << 2 | 0); + + var r03 = Shuffle(rxy, rzx, 2 << 6 | 0 << 4 | 2 << 2 | 0); + var r14 = Shuffle(ryz, rxy, 3 << 6 | 1 << 4 | 2 << 2 | 0); + var r25 = Shuffle(rzx, ryz, 3 << 6 | 1 << 4 | 3 << 2 | 1); + + var m0 = GetLowerHalf<float>(r03); + var m1 = GetLowerHalf<float>(r14); + var m2 = GetLowerHalf<float>(r25); + var m3 = ExtractVector128(r03, 1); + var m4 = ExtractVector128(r14, 1); + var m5 = ExtractVector128(r25, 1); + + var _Xs = SetHighLow(m1, m0); + var _ys = SetHighLow(m3, m2); + var _Zs = SetHighLow(m5, m4); + + return new VectorPacket256(_Xs, _ys, _Zs); + } + + // Convert SoA VectorPacket256 to an incomplete AoS + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public VectorPacket256 FastTranspose() + { + var rxy = Shuffle(Xs, Ys, 2 << 6 | 0 << 4 | 2 << 2 | 0); + var ryz = Shuffle(Ys, Zs, 3 << 6 | 1 << 4 | 3 << 2 | 1); + var rzx = Shuffle(Zs, Xs, 3 << 6 | 1 << 4 | 2 << 2 | 0); + + var r03 = Shuffle(rxy, rzx, 2 << 6 | 0 << 4 | 2 << 2 | 0); + var r14 = Shuffle(ryz, rxy, 3 << 6 | 1 << 4 | 2 << 2 | 0); + var r25 = Shuffle(rzx, ryz, 3 << 6 | 1 << 4 | 3 << 2 | 1); + + return new VectorPacket256(r03, r14, r25); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static VectorPacket256 operator +(VectorPacket256 left, VectorPacket256 right) + { + return new VectorPacket256(Add(left.Xs, right.Xs), Add(left.Ys, right.Ys), Add(left.Zs, right.Zs)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static VectorPacket256 operator -(VectorPacket256 left, VectorPacket256 right) + { + return new VectorPacket256(Subtract(left.Xs, right.Xs), Subtract(left.Ys, right.Ys), Subtract(left.Zs, right.Zs)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static VectorPacket256 operator /(VectorPacket256 left, VectorPacket256 right) + { + return new VectorPacket256(Divide(left.Xs, right.Xs), Divide(left.Ys, right.Ys), Divide(left.Zs, right.Zs)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256<float> DotProduct(VectorPacket256 left, VectorPacket256 right) + { + var x2 = Multiply(left.Xs, right.Xs); + var y2 = Multiply(left.Ys, right.Ys); + var z2 = Multiply(left.Zs, right.Zs); + return Add(Add(x2, y2), z2); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static VectorPacket256 CrossProduct(VectorPacket256 left, VectorPacket256 right) + { + return new VectorPacket256(Subtract(Multiply(left.Ys, right.Zs), Multiply(left.Zs, right.Ys)), + Subtract(Multiply(left.Zs, right.Xs), Multiply(left.Xs, right.Zs)), + Subtract(Multiply(left.Xs, right.Ys), Multiply(left.Ys, right.Xs))); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static VectorPacket256 operator *(Vector256<float> left, VectorPacket256 right) + { + return new VectorPacket256(Multiply(left, right.Xs), Multiply(left, right.Ys), Multiply(left, right.Zs)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public VectorPacket256 Normalize() + { + var length = this.Lengths; + return new VectorPacket256(Divide(Xs, length), Divide(Ys, length), Divide(Zs, length)); + } +} |