Improve performance and safety
This commit is contained in:
@@ -1,44 +1,41 @@
|
||||
#define VECTOR_BENCHMARK
|
||||
#define NOISE_BENCHMARK
|
||||
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using Misaki.HighPerformance.Mathematics;
|
||||
using System.Numerics;
|
||||
using System.Runtime.Intrinsics;
|
||||
|
||||
namespace Misaki.HighPerformance.Test.Benchmark;
|
||||
|
||||
public class MathematicsBenchmark
|
||||
{
|
||||
#if VECTOR_BENCHMARK
|
||||
private Vector2 _v2a = new Vector2(1, 2);
|
||||
private Vector2 _v2b = new Vector2(3, 4);
|
||||
private Vector4 _va = new Vector4(1, 2, 1, 2);
|
||||
private Vector4 _vb = new Vector4(3, 4, 3, 4);
|
||||
|
||||
private float2 _f2a = new float2(1, 2);
|
||||
private float2 _f2b = new float2(3, 4);
|
||||
private float4 _fa = new float4(1, 2, 1, 2);
|
||||
private float4 _fb = new float4(3, 4, 3, 4);
|
||||
|
||||
[Benchmark]
|
||||
public Vector2 VectorAdd()
|
||||
public Vector4 VectorAdd()
|
||||
{
|
||||
var v = new Vector2(0, 0);
|
||||
|
||||
for (var i = 0; i < 10; i++)
|
||||
{
|
||||
v = _v2a + _v2b;
|
||||
_va += _vb;
|
||||
}
|
||||
|
||||
return v;
|
||||
return _va;
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public float2 float2Add()
|
||||
public float4 floatAdd()
|
||||
{
|
||||
var v = new float2(0, 0);
|
||||
|
||||
for (var i = 0; i < 10; i++)
|
||||
{
|
||||
v = _f2a + _f2b;
|
||||
_fa += _fb;
|
||||
}
|
||||
|
||||
return v;
|
||||
return _fa;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -47,7 +44,7 @@ public class MathematicsBenchmark
|
||||
private const int _SIZE = 32;
|
||||
|
||||
[Benchmark]
|
||||
public void VectorNoise()
|
||||
public unsafe void VectorNoise()
|
||||
{
|
||||
var buf = stackalloc float[_SIZE * _SIZE];
|
||||
var job = new Misaki.HighPerformance.Test.Jobs.NoiseJobVector
|
||||
@@ -64,7 +61,7 @@ public class MathematicsBenchmark
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void MathNoise()
|
||||
public unsafe void MathNoise()
|
||||
{
|
||||
var buf = stackalloc float[_SIZE * _SIZE];
|
||||
var job = new Misaki.HighPerformance.Test.Jobs.NoiseJobMath
|
||||
@@ -79,6 +76,24 @@ public class MathematicsBenchmark
|
||||
job.Execute(i, 0);
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
// This is 10x faster than VectorNoise and MathNoise, but writing a burst like compiler to compile MathNoise into this is incredibly hard.
|
||||
public unsafe void MathVNoise()
|
||||
{
|
||||
var buf = stackalloc float[_SIZE * _SIZE];
|
||||
var job = new Misaki.HighPerformance.Test.Jobs.NoiseJobMathV
|
||||
{
|
||||
buffers = buf,
|
||||
width = _SIZE,
|
||||
height = _SIZE,
|
||||
};
|
||||
|
||||
for (var i = 0; i < _SIZE * _SIZE / 8; i++)
|
||||
{
|
||||
job.Execute(i, 0);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if MATRIX_BENCHMARK
|
||||
|
||||
@@ -2,6 +2,8 @@ using Misaki.HighPerformance.Jobs;
|
||||
using Misaki.HighPerformance.Mathematics;
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.Intrinsics;
|
||||
using System.Runtime.Intrinsics.X86;
|
||||
|
||||
namespace Misaki.HighPerformance.Test.Jobs;
|
||||
|
||||
@@ -12,7 +14,7 @@ internal unsafe struct NoiseJobVector : IJobParallelFor
|
||||
public int height;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static float Frac(float x)
|
||||
public static float Frac(float x)
|
||||
{
|
||||
return x - MathF.Truncate(x);
|
||||
}
|
||||
@@ -87,4 +89,102 @@ internal unsafe struct NoiseJobMath : IJobParallelFor
|
||||
var uv = new float2(x, y) / new float2(width, height);
|
||||
buffers[loopIndex] = GradientNoise(uv);
|
||||
}
|
||||
}
|
||||
|
||||
internal unsafe struct NoiseJobMathV : IJobParallelFor
|
||||
{
|
||||
public float* buffers;
|
||||
public int width;
|
||||
public int height;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static Vector256<float> Mod289(Vector256<float> x)
|
||||
{
|
||||
var div = x / Vector256.Create(289.0f);
|
||||
var flr = Vector256.Floor(div);
|
||||
return x - (flr * Vector256.Create(289.0f));
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static Vector256<float> Fade(Vector256<float> t)
|
||||
{
|
||||
return t * t * t * (t * (t * Vector256.Create(6.0f) - Vector256.Create(15.0f)) + Vector256.Create(10.0f));
|
||||
}
|
||||
|
||||
// HELPER: Calculate gradients for 8 pixels at once
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static Vector256<float> GradDot(Vector256<float> ix, Vector256<float> iy, Vector256<float> fx, Vector256<float> fy)
|
||||
{
|
||||
var hx = Mod289(ix);
|
||||
var hy = Mod289(iy);
|
||||
|
||||
var p = hx * Vector256.Create(34.0f) + Vector256.Create(1.0f);
|
||||
p = Mod289(p * hx + hy);
|
||||
p = p * Vector256.Create(34.0f) + Vector256.Create(1.0f);
|
||||
p = Mod289(p * hx);
|
||||
|
||||
var r = (p / 41.0f);
|
||||
r = (r - Vector256.Floor(r)) * 2.0f - Vector256<float>.One;
|
||||
|
||||
var gx = r - Vector256.Floor(r + Vector256.Create(0.5f));
|
||||
var gy = Vector256.Abs(r) - Vector256.Create(0.5f);
|
||||
|
||||
// Normalize
|
||||
var len = Vector256.Sqrt(gx * gx + gy * gy);
|
||||
gx /= len;
|
||||
gy /= len;
|
||||
|
||||
return gx * fx + gy * fy;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Vector256<float> GradientNoiseAVX(Vector256<float> uvX, Vector256<float> uvY)
|
||||
{
|
||||
var ipX = Vector256.Floor(uvX);
|
||||
var ipY = Vector256.Floor(uvY);
|
||||
var fpX = uvX - ipX;
|
||||
var fpY = uvY - ipY;
|
||||
|
||||
var uX = Fade(fpX);
|
||||
var uY = Fade(fpY);
|
||||
|
||||
var d00 = GradDot(ipX, ipY, fpX, fpY);
|
||||
var d01 = GradDot(ipX, ipY + Vector256<float>.One, fpX, fpY - Vector256<float>.One);
|
||||
var d10 = GradDot(ipX + Vector256<float>.One, ipY, fpX - Vector256<float>.One, fpY);
|
||||
var d11 = GradDot(ipX + Vector256<float>.One, ipY + Vector256<float>.One, fpX - Vector256<float>.One, fpY - Vector256<float>.One);
|
||||
|
||||
var lerpX1 = d00 + (d10 - d00) * uX;
|
||||
var lerpX2 = d01 + (d11 - d01) * uX;
|
||||
|
||||
return lerpX1 + (lerpX2 - lerpX1) * uY;
|
||||
}
|
||||
|
||||
public void Execute(int loopIndex, int threadIndex)
|
||||
{
|
||||
// ---------------------------------------------------------
|
||||
// IMPORTANT: Loop Stride is now 8!
|
||||
// ---------------------------------------------------------
|
||||
int baseIndex = loopIndex * 8;
|
||||
|
||||
// Safety check
|
||||
if (baseIndex + 7 >= width * height)
|
||||
return;
|
||||
|
||||
// Calculate Coords
|
||||
int y = baseIndex / width;
|
||||
int x = baseIndex % width;
|
||||
|
||||
// Sequence: 0, 1, 2, 3, 4, 5, 6, 7
|
||||
var vSeqX = Vector256.Create(0f, 1f, 2f, 3f, 4f, 5f, 6f, 7f);
|
||||
var vBaseX = Vector256.Create((float)x) + vSeqX;
|
||||
var vBaseY = Vector256.Create((float)y);
|
||||
|
||||
var vWidth = Vector256.Create((float)width);
|
||||
var vHeight = Vector256.Create((float)height);
|
||||
|
||||
var result = GradientNoiseAVX(vBaseX / vWidth, vBaseY / vHeight);
|
||||
|
||||
// Store 8 floats (32 bytes)
|
||||
Avx.Store(buffers + baseIndex, result);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
using Misaki.HighPerformance.Image;
|
||||
using Misaki.HighPerformance;
|
||||
using Misaki.HighPerformance.LowLevel;
|
||||
using Misaki.HighPerformance.LowLevel.Utilities;
|
||||
|
||||
//BenchmarkDotNet.Running.BenchmarkRunner.Run<Misaki.HighPerformance.Test.Benchmark.ParallelNoiseBenchmark>();
|
||||
BenchmarkDotNet.Running.BenchmarkRunner.Run<Misaki.HighPerformance.Test.Benchmark.MathematicsBenchmark>();
|
||||
|
||||
//using Misaki.HighPerformance.Collections;
|
||||
//using Misaki.HighPerformance.LowLevel.Buffer;
|
||||
@@ -38,11 +40,14 @@ using Misaki.HighPerformance.Image;
|
||||
// 4, 3, 2, 1);
|
||||
//Console.WriteLine(Matrix4x4.Multiply(ma, mb));
|
||||
|
||||
//int[] arr1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
|
||||
//int[] arr2 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
|
||||
|
||||
const string _IMAGE_PATH = "C:/Users/Misaki/Downloads/Im/119683453_p2.jpg";
|
||||
|
||||
using var stream = File.OpenRead(_IMAGE_PATH);
|
||||
var imageInfo = ImageInfo.FromStream(stream);
|
||||
using var image = ImageResult.FromStream(stream);
|
||||
|
||||
Console.WriteLine($"{imageInfo.Width}x{imageInfo.Height} {imageInfo.ColorComponents}");
|
||||
//unsafe
|
||||
//{
|
||||
// fixed (int* p1 = arr1)
|
||||
// fixed (int* p2 = arr2)
|
||||
// {
|
||||
// Console.WriteLine(MemoryUtility.MemCmp(p1, p2, (nuint)(arr1.Length * sizeof(int))));
|
||||
// }
|
||||
//}
|
||||
@@ -20,6 +20,12 @@ public class TestUnsafeArray
|
||||
_arr.Dispose();
|
||||
}
|
||||
|
||||
[GlobalTestCleanup]
|
||||
public static void GlobalCleanup(TestContext ctx)
|
||||
{
|
||||
AllocationManager.Dispose();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestIndexAccess()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user