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 000000000000..0990cc0c9818 --- /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(SetAllVector256(0), SetAllVector256(-1), SetAllVector256(0)); + VectorPacket256 right = SetAllVector256(1.5f) * VectorPacket256.CrossProduct(forward, down).Normalize(); + VectorPacket256 up = SetAllVector256(1.5f) * 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 000000000000..c2a3bb169287 --- /dev/null +++ b/tests/src/JIT/Performance/CodeQuality/HWIntrinsic/X86/PacketTracer/Color.cs @@ -0,0 +1,24 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +// + +using System.Runtime.Intrinsics; +using static System.Runtime.Intrinsics.X86.Avx; +using System.Runtime.CompilerServices; + +internal struct Color +{ + public float R {get; private set;} + public float G {get; private set;} + public float B {get; private set;} + + public static readonly Color Background = new Color(0, 0, 0); + + public Color(float _r, float _g, float _b) + { + R = _r; + G = _g; + B = _b; + } +} \ No newline at end of file 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 000000000000..1611a98ac4f0 --- /dev/null +++ b/tests/src/JIT/Performance/CodeQuality/HWIntrinsic/X86/PacketTracer/ColorPacket.cs @@ -0,0 +1,55 @@ +// 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 ColorPacket256 = VectorPacket256; + +internal static class ColorPacket256Helper +{ + public static Int32RGBPacket256 ConvertToIntRGB(this VectorPacket256 colors) + { + var one = SetAllVector256(1.0f); + var max = SetAllVector256(255.0f); + + 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); + } + + 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 ColorPacket256 BackgroundColor = new ColorPacket256(SetZeroVector256()); + public static ColorPacket256 DefaultColor = new ColorPacket256(SetZeroVector256()); +} + +internal struct Int32RGBPacket256 +{ + public Vector256 Rs; + public Vector256 Gs; + public Vector256 Bs; + + public Int32RGBPacket256(Vector256 _rs, Vector256 _gs, Vector256_bs) + { + Rs = _rs; + Gs = _gs; + Bs = _bs; + } +} \ No newline at end of file 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 000000000000..de4d379dbbfa --- /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 Distances; + public Vector256 ThingIndeces; + + public static readonly Vector256 NullDistance = SetAllVector256(float.MaxValue); + public static readonly Vector256 NullIndex = SetAllVector256(-1); + + public Intersections(Vector256 dis, Vector256 things) + { + Distances = dis; + ThingIndeces = things; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool AllNullIntersections() + { + return AllNullIntersections(Distances); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool AllNullIntersections(Vector256 dis) + { + var cmp = Compare(dis, NullDistance, FloatComparisonMode.EqualOrderedNonSignaling); + var zero = SetZeroVector256(); + var mask = Avx2.CompareEqual(zero, zero); + return TestC(cmp, StaticCast(mask)); + } + + +} \ No newline at end of file diff --git a/tests/src/JIT/Performance/CodeQuality/HWIntrinsic/X86/PacketTracer/Light.cs b/tests/src/JIT/Performance/CodeQuality/HWIntrinsic/X86/PacketTracer/Light.cs new file mode 100644 index 000000000000..5eb591ff5033 --- /dev/null +++ b/tests/src/JIT/Performance/CodeQuality/HWIntrinsic/X86/PacketTracer/Light.cs @@ -0,0 +1,13 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +// + +internal class Light +{ + public Vector Pos; + public Color Color; + + public Light(Vector pos, Color color) { Pos = pos; Color = color; } + public LightPacket256 ToPacket256() { return new LightPacket256(Pos, Color);} +} \ No newline at end of file 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 000000000000..4b7179329757 --- /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); + } +} \ No newline at end of file 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 000000000000..ba60ae5d472e --- /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; private set;} + public abstract Vector256 Intersect(RayPacket256 rayPacket256); + public abstract VectorPacket256 Normals(VectorPacket256 pos); + + public ObjectPacket256(Surface surface) + { + Surface = surface; + } +} \ No newline at end of file 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 000000000000..29be23238b2b --- /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 +{ + /// Provides a thread-safe object pool. + /// Specifies the type of the elements stored in the pool. + [DebuggerDisplay("Count={Count}")] + [DebuggerTypeProxy(typeof(IProducerConsumerCollection_DebugView<>))] + public sealed class ObjectPool : ProducerConsumerCollectionBase + { + private readonly Func _generator; + + /// Initializes an instance of the ObjectPool class. + /// The function used to create items when no items exist in the pool. + public ObjectPool(Func generator) : this(generator, new ConcurrentQueue()) { } + + /// Initializes an instance of the ObjectPool class. + /// The function used to create items when no items exist in the pool. + /// The collection used to store the elements of the pool. + public ObjectPool(Func generator, IProducerConsumerCollection collection) + : base(collection) + { + if (generator == null) throw new ArgumentNullException("generator"); + _generator = generator; + } + + /// Adds the provided item into the pool. + /// The item to be added. + public void PutObject(T item) { base.TryAdd(item); } + + /// Gets an item from the pool. + /// The removed or created item. + /// If the pool is empty, a new item will be created and returned. + public T GetObject() + { + T value; + return base.TryTake(out value) ? value : _generator(); + } + + /// Clears the object pool, returning all of the data that was in the pool. + /// An array containing all of the elements in the pool. + public T[] ToArrayAndClear() + { + var items = new List(); + 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 000000000000..56edbbca6ed2 --- /dev/null +++ b/tests/src/JIT/Performance/CodeQuality/HWIntrinsic/X86/PacketTracer/PacketTracer.cs @@ -0,0 +1,254 @@ +// 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; private set; } + public int Hight { get; private set; } + private static readonly int MaxDepth = 5; + + private static readonly Vector256 SevenToZero = SetVector256(7f, 6f, 5f, 4f, 3f, 2f, 1f, 0f); + + public Packet256Tracer(int _width, int _hight) + { + if (_width % VectorPacket256.Packet256Size != 0) + { + _width += VectorPacket256.Packet256Size - _width % VectorPacket256.Packet256Size; + } + Width = _width; + Hight = _hight; + } + + internal unsafe void RenderVectorized(Scene scene, int* rgb) + { + Camera camera = scene.Camera; + for (int y = 0; y < Hight; y++) + { + int stride = y * Width; + for (int x = 0; x < Width; x += VectorPacket256.Packet256Size) + { + float fx = (float)x; + Vector256 Xs = Add(SetAllVector256(fx), SevenToZero); + var dirs = GetPoints(Xs, SetAllVector256(y), camera); + var rayPacket256 = new RayPacket256(camera.Pos, dirs); + var SoAcolors = TraceRay(rayPacket256, scene, 0); + + var AoS = SoAcolors.FastTranspose(); + var intAoS = AoS.ConvertToIntRGB(); + + int* output = &rgb[x + stride]; + { + Store(output, GetLowerHalf(intAoS.Rs)); + Store(output + 4, GetLowerHalf(intAoS.Gs)); + Store(output + 8, GetLowerHalf(intAoS.Bs)); + Avx2.ExtractVector128(output + 12, intAoS.Rs, 1); + Avx2.ExtractVector128(output + 16, intAoS.Gs, 1); + Avx2.ExtractVector128(output + 20, intAoS.Bs, 1); + } + /* + var AoS = SoAcolors.Transpose(); + var intAoS = AoS.ConvertToIntRGB(); + + int* output = &rgb[x + stride]; + { + 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.Xs, backgroundColor, isNull)); + } + + private Vector256 TestRay(RayPacket256 rayPacket256, Scene scene) + { + var isect = MinIntersections(rayPacket256, scene); + if (isect.AllNullIntersections()) + { + return SetZeroVector256(); + } + var isNull = Compare(isect.Distances, Intersections.NullDistance, FloatComparisonMode.EqualOrderedNonSignaling); + return BlendVariable(isect.Distances, SetZeroVector256(), 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 distance = scene.Things[i].ToPacket256().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 minIndeces = StaticCast(BlendVariable(StaticCast(mins.ThingIndeces), + StaticCast(SetAllVector256(i)), + minMask)); + mins.Distances = minDis; + mins.ThingIndeces = minIndeces; + } + } + 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.ThingIndeces, pos); + var reflectDirs = ds - (Multiply(VectorPacket256.DotProduct(normals, ds), SetAllVector256(2)) * normals); + var colors = GetNaturalColor(isect.ThingIndeces, pos, normals, reflectDirs, scene); + + if (depth >= MaxDepth) + { + return colors + new ColorPacket256(.5f, .5f, .5f); + } + + return colors + GetReflectionColor(isect.ThingIndeces, pos + (SetAllVector256(0.001f) * reflectDirs), normals, reflectDirs, scene, depth); + } + + private ColorPacket256 GetNaturalColor(Vector256 things, VectorPacket256 pos, VectorPacket256 norms, VectorPacket256 rds, Scene scene) + { + var colors = ColorPacket256Helper.DefaultColor; + for (int i = 0; i < scene.Lights.Length; i++) + { + var light = scene.Lights[i]; + var lights = light.ToPacket256(); + var zero = SetZeroVector256(); + 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 illum = VectorPacket256.DotProduct(livec, norms); + Vector256 illumGraterThanZero = Compare(illum, zero, FloatComparisonMode.GreaterThanOrderedNonSignaling); + var tmpColor1 = illum * colorPacket; + var defaultRGB = zero; + Vector256 lcolorR = BlendVariable(defaultRGB, tmpColor1.Xs, illumGraterThanZero); + Vector256 lcolorG = BlendVariable(defaultRGB, tmpColor1.Ys, illumGraterThanZero); + Vector256 lcolorB = BlendVariable(defaultRGB, tmpColor1.Zs, illumGraterThanZero); + ColorPacket256 lcolor = new ColorPacket256(lcolorR, lcolorG, lcolorB); + + Vector256 specular = VectorPacket256.DotProduct(livec, rds.Normalize()); + Vector256 specularGraterThanZero = Compare(specular, zero, FloatComparisonMode.GreaterThanOrderedNonSignaling); + + var difColor = new ColorPacket256(1, 1, 1); + var splColor = new ColorPacket256(1, 1, 1); + var roughness = SetAllVector256(1); + + for (int j = 0; j < scene.Things.Length; j++) + { + Vector256 thingMask = StaticCast(CompareEqual(things, SetAllVector256(j))); + var rgh = SetAllVector256(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 scolorR = BlendVariable(defaultRGB, tmpColor2.Xs, specularGraterThanZero); + Vector256 scolorG = BlendVariable(defaultRGB, tmpColor2.Ys, specularGraterThanZero); + Vector256 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 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 VectorPacket256 GetPoints(Vector256 x, Vector256 y, Camera camera) + { + float widthRate1 = Width / 2.0f; + float widthRate2 = Width * 2.0f; + + float hightRate1 = Hight / 2.0f; + float hightRate2 = Hight * 2.0f; + + var recenteredX = Divide(Subtract(x, SetAllVector256(widthRate1)), SetAllVector256(widthRate2)); + var recenteredY = Subtract(SetZeroVector256(), Divide(Subtract(y, SetAllVector256(hightRate1)), SetAllVector256(hightRate2))); + + var result = camera.Forward + + (recenteredX * camera.Right) + + (recenteredY * camera.Up); + + return result.Normalize(); + } + + internal readonly Scene DefaultScene = CreateDefaultScene(); + + private static Scene CreateDefaultScene() + { + SceneObject[] things = { + new Sphere(new Vector(-0.5f, 1f, 1.5f), 0.5f, Surfaces.MatteShiny), + new Sphere(new Vector(0f, 1f, -0.25f), 1f, Surfaces.Shiny), + new Plane((new Vector(0, 1, 0)), 0, Surfaces.CheckerBoard) + }; + + Light[] lights = { + new Light(new Vector(-2f,2.5f,0f),new Color(.5f,.45f,.41f)), + new Light(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); + } +} \ No newline at end of file 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 000000000000..a82d4b21bf95 --- /dev/null +++ b/tests/src/JIT/Performance/CodeQuality/HWIntrinsic/X86/PacketTracer/PacketTracer.csproj @@ -0,0 +1,62 @@ + + + + + Debug + AnyCPU + 2.0 + {95DFC527-4DC1-495E-97D7-E94EE1F7140D} + Exe + {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + ..\..\ + .NETStandard,Version=v1.4 + netstandard1.4 + true + + + + + + pdbonly + true + + + + + False + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + $(JitPackagesConfigFileDirectory)benchmark\obj\project.assets.json + + diff --git a/tests/src/JIT/Performance/CodeQuality/HWIntrinsic/X86/PacketTracer/Plane.cs b/tests/src/JIT/Performance/CodeQuality/HWIntrinsic/X86/PacketTracer/Plane.cs new file mode 100644 index 000000000000..14f195176805 --- /dev/null +++ b/tests/src/JIT/Performance/CodeQuality/HWIntrinsic/X86/PacketTracer/Plane.cs @@ -0,0 +1,17 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +// + +internal class Plane : SceneObject +{ + public Vector Norm; + public float Offset; + + public Plane(Vector norm, float offset, Surface surface) : base(surface) { Norm = norm; Offset = offset; } + + public override ObjectPacket256 ToPacket256() + { + return new PlanePacket256(this); + } +} \ No newline at end of file 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 000000000000..edab2db08c30 --- /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 Offsets; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public PlanePacket256(Plane plane) : base(plane.Surface) + { + Norms = new VectorPacket256(SetAllVector256(plane.Norm.X), SetAllVector256(plane.Norm.Y), SetAllVector256(plane.Norm.Z)); + Offsets = SetAllVector256(plane.Offset); + } + + public override VectorPacket256 Normals(VectorPacket256 pos) + { + return Norms; + } + + public override Vector256 Intersect(RayPacket256 rayPacket256) + { + var denom = VectorPacket256.DotProduct(Norms, rayPacket256.Dirs); + var dist = Divide(Add(VectorPacket256.DotProduct(Norms, rayPacket256.Starts), Offsets), Subtract(SetZeroVector256(), denom)); + var gtMask = Compare(denom, SetZeroVector256(), FloatComparisonMode.GreaterThanOrderedNonSignaling); + return BlendVariable(dist, Intersections.NullDistance, gtMask); + } +} \ No newline at end of file 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 000000000000..fee736397d61 --- /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 +{ + /// Debug view for the IProducerConsumerCollection. + /// Specifies the type of the data being aggregated. + internal sealed class IProducerConsumerCollection_DebugView + { + private IProducerConsumerCollection _collection; + + public IProducerConsumerCollection_DebugView(IProducerConsumerCollection collection) + { + _collection = collection; + } + + [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)] + public T[] Values { get { return _collection.ToArray(); } } + } + + /// + /// Provides a base implementation for producer-consumer collections that wrap other + /// producer-consumer collections. + /// + /// Specifies the type of elements in the collection. + public abstract class ProducerConsumerCollectionBase : IProducerConsumerCollection + { + private readonly IProducerConsumerCollection _contained; + + /// Initializes the ProducerConsumerCollectionBase instance. + /// The collection to be wrapped by this instance. + protected ProducerConsumerCollectionBase(IProducerConsumerCollection contained) + { + if (contained == null) throw new ArgumentNullException("contained"); + _contained = contained; + } + + /// Gets the contained collection. + protected IProducerConsumerCollection ContainedCollection { get { return _contained; } } + + /// Attempts to add the specified value to the end of the deque. + /// The item to add. + /// true if the item could be added; otherwise, false. + protected virtual bool TryAdd(T item) { return _contained.TryAdd(item); } + + /// Attempts to remove and return an item from the collection. + /// + /// 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. + /// + /// + /// true if an element was removed and returned from the collection; otherwise, false. + /// + protected virtual bool TryTake(out T item) { return _contained.TryTake(out item); } + + /// Attempts to add the specified value to the end of the deque. + /// The item to add. + /// true if the item could be added; otherwise, false. + bool IProducerConsumerCollection.TryAdd(T item) { return TryAdd(item); } + + /// Attempts to remove and return an item from the collection. + /// + /// 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. + /// + /// + /// true if an element was removed and returned from the collection; otherwise, false. + /// + bool IProducerConsumerCollection.TryTake(out T item) { return TryTake(out item); } + + /// Gets the number of elements contained in the collection. + public int Count { get { return _contained.Count; } } + + /// Creates an array containing the contents of the collection. + /// The array. + public T[] ToArray() { return _contained.ToArray(); } + + /// Copies the contents of the collection to an array. + /// The array to which the data should be copied. + /// The starting index at which data should be copied. + public void CopyTo(T[] array, int index) { _contained.CopyTo(array, index); } + + /// Copies the contents of the collection to an array. + /// The array to which the data should be copied. + /// The starting index at which data should be copied. + void ICollection.CopyTo(Array array, int index) { _contained.CopyTo(array, index); } + + /// Gets an enumerator for the collection. + /// An enumerator. + public IEnumerator GetEnumerator() { return _contained.GetEnumerator(); } + + /// Gets an enumerator for the collection. + /// An enumerator. + IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } + + /// Gets whether the collection is synchronized. + bool ICollection.IsSynchronized { get { return _contained.IsSynchronized; } } + + /// Gets the synchronization root object for the collection. + 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 000000000000..8994cf783b62 --- /dev/null +++ b/tests/src/JIT/Performance/CodeQuality/HWIntrinsic/X86/PacketTracer/Program.cs @@ -0,0 +1,167 @@ +// 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 _freeBuffers; + + public Program() + { + _width = Width; + _height = Height; + _parallel = false; + _showThreads = false; + _freeBuffers = new ObjectPool(() => new int[_width * 3 * _height]); + } + + static unsafe int Main(string[] args) + { + if (Avx2.IsSupported) + { + var r = new Program(); + bool result = r.Run(); + // r.RenderTo("./pic.ppm"); + 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 = (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 rgbBuffer = _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 + }; + 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) + { + var packetTracer = new Packet256Tracer(_width, _height); + var scene = packetTracer.DefaultScene; + + var sphere2 = (Sphere)scene.Things[0]; + var baseY = sphere2.Radius; + sphere2.Center.Y = sphere2.Radius; + + var rgb = new int[_width * 3 * _height]; + fixed (int* ptr = rgb) + { + packetTracer.RenderVectorized(scene, ptr); + } + + 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++) + { + int pos = i * _height + j; + 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 000000000000..7b6fde7fd486 --- /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 struct RayPacket256 +{ + public VectorPacket256 Starts; + public VectorPacket256 Dirs; + + public RayPacket256(VectorPacket256 _starts, VectorPacket256 _dirs) + { + Starts = _starts; + Dirs = _dirs; + } +} \ No newline at end of file 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 000000000000..1244363fb863 --- /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 SceneObject[] Things; + public Light[] Lights; + public Camera Camera; + + public Scene(SceneObject[] things, Light[] lights, Camera camera) { Things = things; Lights = lights; Camera = camera; } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public VectorPacket256 Normals(Vector256 things, VectorPacket256 pos) + { + VectorPacket256 norms = new VectorPacket256(1, 1, 1); + + for (int i = 0; i < Things.Length; i++) + { + Vector256 mask = StaticCast(CompareEqual(things, SetAllVector256(i))); + var n = Things[i].ToPacket256().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 Reflect(Vector256 things, VectorPacket256 pos) + { + Vector256 rfl = SetAllVector256(1); + for (int i = 0; i < Things.Length; i++) + { + Vector256 mask = StaticCast(CompareEqual(things, SetAllVector256(i))); + rfl = BlendVariable(rfl, Things[i].Surface.Reflect(pos), mask); + } + return rfl; + } + +} \ No newline at end of file diff --git a/tests/src/JIT/Performance/CodeQuality/HWIntrinsic/X86/PacketTracer/SceneObject.cs b/tests/src/JIT/Performance/CodeQuality/HWIntrinsic/X86/PacketTracer/SceneObject.cs new file mode 100644 index 000000000000..6dbf1d98ffe4 --- /dev/null +++ b/tests/src/JIT/Performance/CodeQuality/HWIntrinsic/X86/PacketTracer/SceneObject.cs @@ -0,0 +1,12 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +// + +internal abstract class SceneObject +{ + public Surface Surface; + public abstract ObjectPacket256 ToPacket256(); + + public SceneObject(Surface surface) { Surface = surface; } +} diff --git a/tests/src/JIT/Performance/CodeQuality/HWIntrinsic/X86/PacketTracer/Sphere.cs b/tests/src/JIT/Performance/CodeQuality/HWIntrinsic/X86/PacketTracer/Sphere.cs new file mode 100644 index 000000000000..98220a69fa71 --- /dev/null +++ b/tests/src/JIT/Performance/CodeQuality/HWIntrinsic/X86/PacketTracer/Sphere.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 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 ObjectPacket256 ToPacket256() + { + return new SpherePacket256(this); + } + +} \ No newline at end of file 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 000000000000..a2fc47c20d70 --- /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 Radiuses; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public SpherePacket256(Sphere sphere) : base(sphere.Surface) + { + Centers = new VectorPacket256(SetAllVector256(sphere.Center.X), SetAllVector256(sphere.Center.Y), SetAllVector256(sphere.Center.Z)); + Radiuses = SetAllVector256(sphere.Radius); + } + + public override VectorPacket256 Normals(VectorPacket256 pos) + { + return (pos - Centers).Normalize(); + } + + public override Vector256 Intersect(RayPacket256 rayPacket256) + { + var eo = Centers - rayPacket256.Starts; + var v = VectorPacket256.DotProduct(eo, rayPacket256.Dirs); + var zero = SetZeroVector256(); + 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); + } +} \ No newline at end of file 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 000000000000..979a67280135 --- /dev/null +++ b/tests/src/JIT/Performance/CodeQuality/HWIntrinsic/X86/PacketTracer/Surface.cs @@ -0,0 +1,28 @@ +// 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 Diffuse; + public VectorPacket256 Specular; + public Func> Reflect; + public float Roughness; + + public Surface(Func Diffuse, + VectorPacket256 Specular, + Func> 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 000000000000..15781881404d --- /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 +{ + // 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(1); + var evenMaskint = Avx2.And(floored, modMask); + var evenMask = Avx2.CompareEqual(evenMaskint, modMask); + + var white = new ColorPacket256(SetAllVector256(1.0f)); + var black = new ColorPacket256(0.02f, 0.0f, 0.14f); + + var resultX = BlendVariable(black.Xs, white.Xs, StaticCast(evenMask)); + var resultY = BlendVariable(black.Ys, white.Ys, StaticCast(evenMask)); + var resultZ = BlendVariable(black.Zs, white.Zs, StaticCast(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(1); + var evenMaskUint = Avx2.And(floored, modMask); + var evenMask = Avx2.CompareEqual(evenMaskUint, modMask); + + return BlendVariable(SetAllVector256(0.5f), SetAllVector256(0.1f), StaticCast(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(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(0.7f); }, + 250f); +} \ No newline at end of file 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 000000000000..9aa287b455d3 --- /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; + } +} \ No newline at end of file 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 000000000000..9c100e8c3410 --- /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 MaxValue = SetAllVector256(88.0f); + static readonly Vector256 MinValue = SetAllVector256(-88.0f); + static readonly Vector256 Log2 = SetAllVector256(1.44269502f); + static readonly Vector256 C1 = SetAllVector256(0.693359375f); + static readonly Vector256 C2 = SetAllVector256(-0.0002121944417f); + static readonly Vector256 P0 = SetAllVector256(0.0001987569121f); + static readonly Vector256 P1 = SetAllVector256(0.001398199936f); + static readonly Vector256 P2 = SetAllVector256(0.008333452046f); + static readonly Vector256 P3 = SetAllVector256(0.04166579619f); + static readonly Vector256 P4 = SetAllVector256(0.1666666567f); + static readonly Vector256 LogP0 = SetAllVector256(0.07037683576f); + static readonly Vector256 LogP1 = SetAllVector256(-0.1151461005f); + static readonly Vector256 LogP2 = SetAllVector256(0.1167699844f); + static readonly Vector256 LogP3 = SetAllVector256(-0.1242014095f); + static readonly Vector256 LogP4 = SetAllVector256(0.1424932331f); + static readonly Vector256 LogP5 = SetAllVector256(-0.1666805744f); + static readonly Vector256 LogP6 = SetAllVector256(0.2000071406f); + static readonly Vector256 LogP7 = SetAllVector256(-0.2499999404f); + static readonly Vector256 LogP8 = SetAllVector256(0.3333333135f); + static readonly Vector256 LogQ1 = SetAllVector256(-0.0002121944417f); + static readonly Vector256 LogQ2 = SetAllVector256(0.693359375f); + static readonly Vector256 Point5 = SetAllVector256(0.5f); + static readonly Vector256 Sqrthf = SetAllVector256(0.7071067691f); + static readonly Vector256 One = SetAllVector256(1.0f); + static readonly Vector256 Ox7 = SetAllVector256(127); + static readonly Vector256 MinNormPos = SetAllVector256(8388608); + static readonly Vector256 MantMask = SetAllVector256(-2139095041); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 Pow(Vector256 left, Vector256 right) + { + return Exp(Multiply(right, Log(left))); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 Exp(Vector256 value) + { + value = Min(value, MaxValue); + value = Max(value, MinValue); + Vector256 fx = Multiply(value, Log2); + fx = Floor(Add(fx, Point5)); + + Vector256 tmp = Multiply(fx, C1); + Vector256 z = Multiply(fx, C2); + Vector256 x = Subtract(value, tmp); + x = Subtract(x, z); + z = Multiply(x, x); + Vector256 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 pow2n = ConvertToVector256Int32(fx); + pow2n = Avx2.Add(pow2n, Ox7); + pow2n = Avx2.ShiftLeftLogical(pow2n, 23); + + return Multiply(y, StaticCast(pow2n)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 Log(Vector256 value) + { + Vector256 invalidMask = Compare(value, SetZeroVector256(), FloatComparisonMode.LessThanOrEqualOrderedNonSignaling); + Vector256 x = Max(value, StaticCast(MinNormPos)); + Vector256 ei = Avx2.ShiftRightLogical(StaticCast(x), 23); + x = Or(And(x, StaticCast(MantMask)), Point5); + ei = Avx2.Subtract(ei, Ox7); + Vector256 e = Add(ConvertToVector256Single(ei), One); + Vector256 mask = Compare(x, Sqrthf, FloatComparisonMode.LessThanOrderedNonSignaling); + Vector256 tmp = And(x, mask); + x = Subtract(x, One); + e = Subtract(e, And(One, mask)); + x = Add(x, tmp); + Vector256 z = Multiply(x, x); + Vector256 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); + } + +} \ No newline at end of file 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 000000000000..800fa4855c79 --- /dev/null +++ b/tests/src/JIT/Performance/CodeQuality/HWIntrinsic/X86/PacketTracer/VectorPacket.cs @@ -0,0 +1,169 @@ +// 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; +using System.Runtime.CompilerServices; +using System; + +internal class VectorPacket256 +{ + public Vector256 Xs; + public Vector256 Ys; + public Vector256 Zs; + public Vector256 Lengths + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + var x2 = Multiply(Xs, Xs); + var y2 = Multiply(Ys, Ys); + var z2 = Multiply(Zs, Zs); + + var l2 = Add(x2, y2); + l2 = Add(l2, z2); + return Sqrt(l2); + } + } + + + public readonly static int Packet256Size = 8; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public VectorPacket256(Vector256 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 _Xs, Vector256 _ys, Vector256 _Zs) + { + Xs = _Xs; + Ys = _ys; + Zs = _Zs; + } + + // Convert AoS vectors to SoA Packet256 + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe VectorPacket256(float* vectors) + { + Vector256 m03 = ExtendToVector256(LoadVector128(&vectors[0])); // load lower halves + Vector256 m14 = ExtendToVector256(LoadVector128(&vectors[4])); + Vector256 m25 = ExtendToVector256(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(r03); + var m1 = GetLowerHalf(r14); + var m2 = GetLowerHalf(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 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 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)); + } +} \ No newline at end of file diff --git a/tests/src/JIT/config/benchmark/benchmark.csproj b/tests/src/JIT/config/benchmark/benchmark.csproj index 4d49fdee3ead..b084ff43cb79 100644 --- a/tests/src/JIT/config/benchmark/benchmark.csproj +++ b/tests/src/JIT/config/benchmark/benchmark.csproj @@ -103,6 +103,9 @@ $(XunitPackageVersion) + + $(MicrosoftPrivateCoreFxNETCoreAppPackageVersion) + netstandard1.4