Skip to content

Commit

Permalink
merge from perf refactor branch
Browse files Browse the repository at this point in the history
  • Loading branch information
Jack Dermody committed Apr 16, 2024
2 parents 562200e + 90ed92c commit 54bce96
Show file tree
Hide file tree
Showing 577 changed files with 53,778 additions and 38,792 deletions.
4 changes: 2 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,9 @@
/BrightAPI/UI/.parcel-cache
/BrightAPI/UI/dist
/BrightAPI/UI/node_modules
/BrightData.Table/bin
/BrightData.Table/obj
/Benchmarks/bin
/Benchmarks/obj
/BrightData.Parquet/bin
/BrightData.Parquet/obj
/BrightData.Table/bin
/BrightData.Table/obj
19 changes: 19 additions & 0 deletions Benchmarks/Benchmarks.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="0.13.12" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\BrightData\BrightData.csproj" />
</ItemGroup>

</Project>
214 changes: 214 additions & 0 deletions Benchmarks/Dot.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86;
using BenchmarkDotNet.Attributes;

#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.

namespace Benchmarks
{
public unsafe class Dot
{
const int Length = 32 * 1024;
float[] data1, data2;
float* _ptr1, _ptr2;

[GlobalSetup]
public void GlobalSetup()
{
data1 = Enumerable.Range(0, Length).Select(x => (float)x).ToArray();
data2 = Enumerable.Range(0, Length).Select(x => (float)(x + 1)).ToArray();
_ptr1 = GetAligned(data1);
_ptr2 = GetAligned(data2);
}

[GlobalCleanup]
public void GlobalCleanup()
{
NativeMemory.AlignedFree(_ptr1);
NativeMemory.AlignedFree(_ptr2);
}

[Benchmark(Baseline = true)]
public float SimpleDot() => SimpleDot(data1, data2);

[Benchmark]
public float VectorDot() => VectorDot(data1, data2);

[Benchmark]
public float VectorDotAligned() => VectorAligned(data1, data2);

[Benchmark]
public float VectorDotAligned2() => VectorAligned2(_ptr1, _ptr2, Length);

[Benchmark]
public float VectorDotAligned3() => VectorAligned3(_ptr1, _ptr2, Length);

public static float SimpleDot(Span<float> data1, Span<float> data2)
{
var ret = 0f;
var size = data1.Length;
for (var i = 0; i < size; i++)
ret += data1[i] * data2[i];
return ret;
}

public static float* GetAligned(Span<float> data)
{
var size = (uint)(data.Length * sizeof(float));
var ptr = (float*)NativeMemory.AlignedAlloc(size, 32);
fixed(float* source = data)
NativeMemory.Copy(source, ptr, size);
return ptr;
}

public static float VectorDot(Span<float> data1, Span<float> data2)
{
const int VectorSize = 256 / sizeof(float) / 8;
fixed (float* ptr1 = data1)
fixed (float* ptr2 = data2) {
var size = data1.Length;
float* p1 = ptr1, end1 = ptr1 + size;
float* p2 = ptr2;
var vectorSum = Vector256<float>.Zero;
for(int i = 0, numVectors = size / VectorSize; i < numVectors; i++) {
var left = Avx.LoadVector256(p1);
p1 += VectorSize;
var right = Avx.LoadVector256(p2);
p2 += VectorSize;
vectorSum += left * right;
}

// sum the result vector
var temp = stackalloc float[VectorSize];
Avx.Store(temp, vectorSum);
var ret = 0f;
for (var i = 0; i < VectorSize; i++)
ret += temp[i];

// sum any remaining values
while (p1 < end1)
ret += *p1++ * *p2++;

return ret;
}
}

public static float VectorAligned(Span<float> data1, Span<float> data2)
{
const int VectorSize = 256 / sizeof(float) / 8;
var ptr1 = GetAligned(data1);
var ptr2 = GetAligned(data2);

try {
var size = data1.Length;
float* p1 = ptr1, end1 = ptr1 + size;
float* p2 = ptr2;
var vectorSum = Vector256<float>.Zero;
for (int i = 0, numVectors = size / VectorSize; i < numVectors; i++) {
var left = Avx.LoadAlignedVector256(p1);
p1 += VectorSize;
var right = Avx.LoadAlignedVector256(p2);
p2 += VectorSize;
vectorSum += left * right;
}

// sum the result vector
var temp = stackalloc float[VectorSize];
Avx.Store(temp, vectorSum);
var ret = 0f;
for (var i = 0; i < VectorSize; i++)
ret += temp[i];

// sum any remaining values
while (p1 < end1)
ret += *p1++ * *p2++;
return ret;
}
finally {
NativeMemory.AlignedFree(ptr1);
NativeMemory.AlignedFree(ptr2);
}
}

public static float VectorAligned2(float* ptr1, float* ptr2, int size)
{
const int VectorSize = 256 / sizeof(float) / 8;

float* p1 = ptr1, end1 = ptr1 + size;
float* p2 = ptr2;
var vectorSum = Vector256<float>.Zero;
for (int i = 0, numVectors = size / VectorSize; i < numVectors; i++) {
var left = Avx.LoadAlignedVector256(p1);
p1 += VectorSize;
var right = Avx.LoadAlignedVector256(p2);
p2 += VectorSize;
vectorSum += left * right;
}

// sum the result vector
var temp = stackalloc float[VectorSize];
Avx.Store(temp, vectorSum);
var ret = 0f;
for (var i = 0; i < VectorSize; i++)
ret += temp[i];

// sum any remaining values
while (p1 < end1)
ret += *p1++ * *p2++;
return ret;
}

public static float VectorAligned3(float* ptr1, float* ptr2, int size)
{
const int VectorSize = 256 / sizeof(float) / 8;

float* p1 = ptr1, end1 = ptr1 + size;
float* p2 = ptr2;
var vectorSum = Vector256<float>.Zero;

// bulk vector sum
var vectorIndex = 0;
var numVectors = size / VectorSize;
for (var numBulkVectors = numVectors / 4 * 4; vectorIndex < numBulkVectors; vectorIndex += 4) {
var a1 = Avx.LoadAlignedVector256(p1);
var b1 = Avx.LoadAlignedVector256(p1 + VectorSize);
var c1 = Avx.LoadAlignedVector256(p1 + VectorSize * 2);
var d1 = Avx.LoadAlignedVector256(p1 + VectorSize * 3);
p1 += VectorSize * 4;

var a2 = Avx.LoadAlignedVector256(p2);
var b2 = Avx.LoadAlignedVector256(p2 + VectorSize);
var c2 = Avx.LoadAlignedVector256(p2 + VectorSize * 2);
var d2 = Avx.LoadAlignedVector256(p2 + VectorSize * 3);
p2 += VectorSize * 4;

vectorSum += a1 * a2;
vectorSum += b1 * b2;
vectorSum += c1 * c2;
vectorSum += d1 * d2;
}

// vector sum
for (; vectorIndex < numVectors; vectorIndex++) {
var left = Avx.LoadAlignedVector256(p1);
p1 += VectorSize;
var right = Avx.LoadAlignedVector256(p2);
p2 += VectorSize;
vectorSum += left * right;
}

// sum the result vector
var temp = stackalloc float[VectorSize];
Avx.Store(temp, vectorSum);
var ret = 0f;
for (var i = 0; i < VectorSize; i++)
ret += temp[i];

// sum any remaining values
while (p1 < end1)
ret += *p1++ * *p2++;
return ret;
}
}
}
52 changes: 52 additions & 0 deletions Benchmarks/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
using System.Runtime.InteropServices;
using BenchmarkDotNet.Running;

namespace Benchmarks
{
public class Program
{
static void Main(string[] args)
{
BenchmarkSetProperty();
BenchmarkSum();
BenchmarkDot();
}

static void BenchmarkSetProperty()
{
BenchmarkRunner.Run<SetPropertyFromExpression>();
}

static void BenchmarkSum()
{
var data1 = Enumerable.Range(0, 256).Select(x => (float)x).ToArray();
Console.WriteLine($"Sum Baseline: {Sum.SimpleSum(data1):N0}");
Console.WriteLine($"Vector Sum: {Sum.VectorSum(data1):N0}");
Console.WriteLine($"Vector Sum 2: {Sum.VectorSum2(data1):N0}");
Console.WriteLine($"Vector Sum 3: {Sum.VectorSum3(data1):N0}");
Console.WriteLine($"Vector Aligned: {Sum.VectorAligned(data1):N0}");
Console.WriteLine($"Vector Bulk Aligned:{Sum.VectorBulkAligned(data1):N0}");
BenchmarkRunner.Run<Sum>();
}

static unsafe void BenchmarkDot()
{
var data1 = Enumerable.Range(0, 256).Select(x => (float)x).ToArray();
var data2 = Enumerable.Range(0, 256).Select(x => (float)(x+1)).ToArray();
var alignedPtr1 = Dot.GetAligned(data1);
var alignedPtr2 = Dot.GetAligned(data2);
try {
Console.WriteLine($"Baseline: {Dot.SimpleDot(data1, data2):N0}");
Console.WriteLine($"Vector Dot: {Dot.VectorDot(data1, data2):N0}");
Console.WriteLine($"Vector Dot Aligned: {Dot.VectorAligned(data1, data2):N0}");
Console.WriteLine($"Vector Dot Aligned2:{Dot.VectorAligned2(alignedPtr1, alignedPtr2, 256):N0}");
Console.WriteLine($"Vector Dot Aligned3:{Dot.VectorAligned3(alignedPtr1, alignedPtr2, 256):N0}");
BenchmarkRunner.Run<Dot>();
}
finally {
NativeMemory.Free(alignedPtr1);
NativeMemory.Free(alignedPtr2);
}
}
}
}
87 changes: 87 additions & 0 deletions Benchmarks/SetPropertyFromExpression.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
using BenchmarkDotNet.Attributes;
using System.Linq.Expressions;
using System.Reflection;
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.

namespace Benchmarks
{
public class SetPropertyFromExpression
{
struct TestStruct
{
public int SomeField;

public readonly override string ToString() => $"{SomeField}";
}

[Params(100, 1000, 10000)]
public int Size { get; set; }

TestStruct[] _structs;
int[] _values;

[GlobalSetup]
public void SetupData()
{
_structs = new TestStruct[Size];
_values = Enumerable.Range(0, Size).ToArray();
}


[Benchmark(Baseline = true)]
public void Direct()
{
for(var i = 0; i < _structs.Length; i++)
_structs[i].SomeField = _values[i];
}

[Benchmark]
public void SetPropertyDirect()
{
SetPropertyDirect(_values, _structs, (ref TestStruct obj, int val) => obj.SomeField = val);
}

[Benchmark]
public void ViaSetter()
{
SetPropertyViaSetter<TestStruct, int>(_values, _structs, x => x.SomeField);
}

[Benchmark]
public void ViaReflection()
{
SetPropertyViaReflection<TestStruct, int>(_values, _structs, x => x.SomeField);
}

public delegate void SetPropertyDelegate<T, in PT>(ref T item, PT value);
public static void SetPropertyViaSetter<T, P>(ReadOnlySpan<P> input, Span<T> output, Expression<Func<T, P>> property) where P : notnull
{
var prop = ((MemberExpression)property.Body).Member;
var typeParam = Expression.Parameter(typeof(T).MakeByRefType());
var valueParam = Expression.Parameter(typeof(P));
var setter = Expression.Lambda<SetPropertyDelegate<T, P>>(Expression.Assign(Expression.MakeMemberAccess(typeParam, prop), valueParam), typeParam, valueParam).Compile();

var index = 0;
foreach (ref var item in output)
setter(ref item, input[index++]);
}

public static void SetPropertyViaReflection<T, P>(ReadOnlySpan<P> input, Span<T> output, Expression<Func<T, P>> property) where P : notnull
{
var prop = (FieldInfo)((MemberExpression)property.Body).Member;

var index = 0;
foreach (ref var item in output) {
var reference = __makeref(item);
prop.SetValueDirect(reference, input[index++]);
}
}

public static void SetPropertyDirect<T, P>(ReadOnlySpan<P> input, Span<T> output, SetPropertyDelegate<T, P> setProperty) where P : notnull
{
var index = 0;
foreach (ref var item in output)
setProperty(ref item, input[index++]);
}
}
}
Loading

0 comments on commit 54bce96

Please sign in to comment.