C# 提高 SIMD Sum 的性能
C# Improve performance of SIMD Sum
我正在编写一个 SIMD 库并试图榨取每一点性能。
我已经将 array
就地转换为 Span<Vector<int>>
,而不是创建新对象。
目标数组很大(超过 1000 个元素)。
有没有更有效的数组求和方法?
欢迎提出想法。
public static int Sum(int[] array)
{
Vector<int> vSum = Vector<T>.Zero;
int sum;
int i;
Span<Vector<int>> vsArray = MemoryMarshal.Cast<int, Vector<int>>(array);
for (i = 0; i < vsArray.Length; i++)
{
vSum += vsArray[i];
}
sum = Vector.Dot(vSum, Vector<int>.One);
i *= Vector<int>.Count;
for (; i < array.Length; i++)
{
sum += array[i];
}
return sum;
}
你的代码很好。只能提高 4%,方法如下:
// Test result: only 4% win on my PC.
[MethodImpl( MethodImplOptions.AggressiveInlining )]
static int sumUnsafeAvx2( int[] array )
{
unsafe
{
fixed( int* sourcePointer = array )
{
int* pointerEnd = sourcePointer + array.Length;
int* pointerEndAligned = sourcePointer + ( array.Length - array.Length % 16 );
Vector256<int> sumLow = Vector256<int>.Zero;
Vector256<int> sumHigh = sumLow;
int* pointer;
for( pointer = sourcePointer; pointer < pointerEndAligned; pointer += 16 )
{
var a = Avx.LoadVector256( pointer );
var b = Avx.LoadVector256( pointer + 8 );
sumLow = Avx2.Add( sumLow, a );
sumHigh = Avx2.Add( sumHigh, b );
}
sumLow = Avx2.Add( sumLow, sumHigh );
Vector128<int> res4 = Sse2.Add( sumLow.GetLower(), sumLow.GetUpper() );
res4 = Sse2.Add( res4, Sse2.Shuffle( res4, 0x4E ) );
res4 = Sse2.Add( res4, Sse2.Shuffle( res4, 1 ) );
int scalar = res4.ToScalar();
for( ; pointer < pointerEnd; pointer++ )
scalar += *pointer;
return scalar;
}
}
}
明确地说,我不建议做我上面写的。不是为了 4% 的改进。不安全的代码是不安全的。你的版本在没有 AVX2 的情况下也可以工作,并且如果可用的话可以从 AVX512 中获益,我的优化版本在没有 AVX2 的情况下会崩溃,并且即使硬件支持它也不会使用 AVX512。
我正在编写一个 SIMD 库并试图榨取每一点性能。
我已经将 array
就地转换为 Span<Vector<int>>
,而不是创建新对象。
目标数组很大(超过 1000 个元素)。
有没有更有效的数组求和方法?
欢迎提出想法。
public static int Sum(int[] array)
{
Vector<int> vSum = Vector<T>.Zero;
int sum;
int i;
Span<Vector<int>> vsArray = MemoryMarshal.Cast<int, Vector<int>>(array);
for (i = 0; i < vsArray.Length; i++)
{
vSum += vsArray[i];
}
sum = Vector.Dot(vSum, Vector<int>.One);
i *= Vector<int>.Count;
for (; i < array.Length; i++)
{
sum += array[i];
}
return sum;
}
你的代码很好。只能提高 4%,方法如下:
// Test result: only 4% win on my PC.
[MethodImpl( MethodImplOptions.AggressiveInlining )]
static int sumUnsafeAvx2( int[] array )
{
unsafe
{
fixed( int* sourcePointer = array )
{
int* pointerEnd = sourcePointer + array.Length;
int* pointerEndAligned = sourcePointer + ( array.Length - array.Length % 16 );
Vector256<int> sumLow = Vector256<int>.Zero;
Vector256<int> sumHigh = sumLow;
int* pointer;
for( pointer = sourcePointer; pointer < pointerEndAligned; pointer += 16 )
{
var a = Avx.LoadVector256( pointer );
var b = Avx.LoadVector256( pointer + 8 );
sumLow = Avx2.Add( sumLow, a );
sumHigh = Avx2.Add( sumHigh, b );
}
sumLow = Avx2.Add( sumLow, sumHigh );
Vector128<int> res4 = Sse2.Add( sumLow.GetLower(), sumLow.GetUpper() );
res4 = Sse2.Add( res4, Sse2.Shuffle( res4, 0x4E ) );
res4 = Sse2.Add( res4, Sse2.Shuffle( res4, 1 ) );
int scalar = res4.ToScalar();
for( ; pointer < pointerEnd; pointer++ )
scalar += *pointer;
return scalar;
}
}
}
明确地说,我不建议做我上面写的。不是为了 4% 的改进。不安全的代码是不安全的。你的版本在没有 AVX2 的情况下也可以工作,并且如果可用的话可以从 AVX512 中获益,我的优化版本在没有 AVX2 的情况下会崩溃,并且即使硬件支持它也不会使用 AVX512。