Files
QWERTYkez.Mensura/QWERTYkez.Mensura/Extensions/CollectionsRootOfSquareExtensions.cs
2026-06-12 23:34:00 +07:00

260 lines
10 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System.Runtime.Intrinsics.X86;
namespace QWERTYkez.Mensura.Extensions;
internal static partial class CollectionsRootOfSquareExtensions
{
// === SqrtCore === SIMD
internal static unsafe void SqrtCore<T, R>(this ReadOnlySpan<T> units, int len, Span<R> destination)
where T : struct, IMensuraUnit, IEquatable<T>
where R : struct, IMensuraUnit, IEquatable<R>
{
ReadOnlySpan<double> srcDouble = MemoryMarshal.Cast<T, double>(units);
Span<double> dstDouble = MemoryMarshal.Cast<R, double>(destination);
int i = 0;
ref double srcRef = ref MemoryMarshal.GetReference(srcDouble);
ref double dstRef = ref MemoryMarshal.GetReference(dstDouble);
// 1. ПУТЬ AVX2 / AVX (x64 Процессоры) — обрабатываем по 4 элемента double за такт
if (Avx.IsSupported && len >= 4)
{
int simdEnd = len & ~3;
for (; i < simdEnd; i += 4)
{
ref double currentSrc = ref Unsafe.Add(ref srcRef, i);
ref double currentDst = ref Unsafe.Add(ref dstRef, i);
double* pSrc = (double*)Unsafe.AsPointer(ref currentSrc);
double* pDst = (double*)Unsafe.AsPointer(ref currentDst);
// Используем Load вместо LoadVector256 (Load безопасен к невыровненной по 32-байтам памяти)
var v = Avx.LoadVector256(pSrc);
var sqrtV = Avx.Sqrt(v);
Avx.Store(pDst, sqrtV);
}
}
// 2. КРОССПЛАТФОРМЕННЫЙ VECTOR (ARM64 / Apple Silicon / NEON / SIMD Fallback)
else if (Vector.IsHardwareAccelerated && len >= Vector<double>.Count)
{
int vCount = Vector<double>.Count;
int simdEnd = len & ~(vCount - 1);
for (; i < simdEnd; i += vCount)
{
var v = new Vector<double>(srcDouble.Slice(i, vCount));
var sqrtV = Vector.SquareRoot(v);
sqrtV.CopyTo(dstDouble.Slice(i, vCount));
}
}
// 3. ХВОСТ МАССИВА (или работа на старых CPU)
for (; i < len; i++)
{
Unsafe.Add(ref dstRef, i) = Math.Sqrt(Unsafe.Add(ref srcRef, i));
}
}
internal static void SqrtCore<T, R>(this ReadOnlySpan<T?> units, int len, Span<R?> destination)
where T : struct, IMensuraUnit, IEquatable<T>
where R : struct, IMensuraUnit, IEquatable<R>
{
ref var srcRef = ref MemoryMarshal.GetReference(units);
ref var dstRef = ref MemoryMarshal.GetReference(destination);
int i = 0;
int unrollEnd = len & ~3;
// ОСНОВНОЙ ЦИКЛ: Конвейерное чтение по 4 элемента (Развертка цикла повышает ILP процессора)
for (; i < unrollEnd; i += 4)
{
ref readonly T? u0 = ref Unsafe.Add(ref srcRef, i);
ref readonly T? u1 = ref Unsafe.Add(ref srcRef, i + 1);
ref readonly T? u2 = ref Unsafe.Add(ref srcRef, i + 2);
ref readonly T? u3 = ref Unsafe.Add(ref srcRef, i + 3);
ref var d0 = ref Unsafe.Add(ref dstRef, i);
ref var d1 = ref Unsafe.Add(ref dstRef, i + 1);
ref var d2 = ref Unsafe.Add(ref dstRef, i + 2);
ref var d3 = ref Unsafe.Add(ref dstRef, i + 3);
d0 = Math.Sqrt(u0.Protected()).ToUnit<R>();
d1 = Math.Sqrt(u1.Protected()).ToUnit<R>();
d2 = Math.Sqrt(u2.Protected()).ToUnit<R>();
d3 = Math.Sqrt(u3.Protected()).ToUnit<R>();
}
// ХВОСТ ЦИКЛА: Остаток элементов
for (; i < len; i++)
{
ref readonly T? unit = ref Unsafe.Add(ref srcRef, i);
ref var dst = ref Unsafe.Add(ref dstRef, i);
dst = Math.Sqrt(unit.Protected()).ToUnit<R>();
}
}
// === ReadOnlySpan API ===
internal static void Sqrt<T, R>(this ReadOnlySpan<T> units, Span<R> destination)
where T : struct, IMensuraUnit, IEquatable<T>
where R : struct, IMensuraUnit, IEquatable<R>
{
if (units.IsEmpty) return;
int len = units.Length;
if (len > destination.Length)
throw new ArgumentException("Целевой буфер destination меньше исходного source.");
units.SqrtCore(len, destination);
}
internal static void Sqrt<T, R>(this ReadOnlySpan<T?> units, Span<R?> destination)
where T : struct, IMensuraUnit, IEquatable<T>
where R : struct, IMensuraUnit, IEquatable<R>
{
if (units.IsEmpty) return;
int len = units.Length;
if (len > destination.Length)
throw new ArgumentException("Целевой буфер destination меньше исходного source.");
units.SqrtCore(len, destination);
}
// === Array API ===
internal static R[] Sqrt<T, R>(this T[] units)
where T : struct, IMensuraUnit, IEquatable<T>
where R : struct, IMensuraUnit, IEquatable<R>
{
if (units is null) return null!;
if (units.Length == 0) return [];
var result = new R[units.Length];
units.AsSpan().SqrtCore(units.Length, result.AsSpan());
return result;
}
internal static R?[] Sqrt<T, R>(this T?[] units)
where T : struct, IMensuraUnit, IEquatable<T>
where R : struct, IMensuraUnit, IEquatable<R>
{
if (units is null) return null!;
if (units.Length == 0) return [];
var result = new R?[units.Length];
units.AsSpan().SqrtCore(units.Length, result.AsSpan());
return result;
}
// === List API ===
internal static List<R> Sqrt<T, R>(this List<T> units)
where T : struct, IMensuraUnit, IEquatable<T>
where R : struct, IMensuraUnit, IEquatable<R>
{
if (units is null) return null!;
int count = units.Count;
if (count == 0) return [];
var result = new R[count];
CollectionsMarshal.AsSpan(units).SqrtCore(count, result.AsSpan());
return result.WrapAsList<R, R>();
}
internal static List<R?> Sqrt<T, R>(this List<T?> units)
where T : struct, IMensuraUnit, IEquatable<T>
where R : struct, IMensuraUnit, IEquatable<R>
{
if (units is null) return null!;
int count = units.Count;
if (count == 0) return [];
var result = new R?[count];
CollectionsMarshal.AsSpan(units).SqrtCore(count, result.AsSpan());
return result.WrapAsList<R, R>();
}
// === IReadOnlyCollection ===
internal static void Sqrt<T, R>(this IReadOnlyCollection<T> units, Span<R> destination)
where T : struct, IMensuraUnit, IEquatable<T>
where R : struct, IMensuraUnit, IEquatable<R>
{
if (units is null) return;
int count = units.Count;
if (count == 0) return;
if (destination.Length < count)
throw new ArgumentException("Destination too short");
if (units is T[] array) { array.AsSpan().SqrtCore(count, destination); return; }
if (units is List<T> list) { CollectionsMarshal.AsSpan(list).SqrtCore(count, destination); return; }
int i = 0;
ref R dstRef = ref MemoryMarshal.GetReference(destination);
foreach (T item in units)
{
if ((uint)i >= (uint)count) break;
Unsafe.Add(ref dstRef, i++) = Math.Sqrt(item.ToDouble()).ToUnit<R>();
}
}
internal static void Sqrt<T, R>(this IReadOnlyCollection<T?> units, Span<R?> destination)
where T : struct, IMensuraUnit, IEquatable<T>
where R : struct, IMensuraUnit, IEquatable<R>
{
if (units is null) return;
int count = units.Count;
if (count == 0) return;
if (destination.Length < count)
throw new ArgumentException("Destination too short");
if (units is T?[] array) { array.AsSpan().SqrtCore(count, destination); return; }
if (units is List<T?> list) { CollectionsMarshal.AsSpan(list).SqrtCore(count, destination); return; }
int i = 0;
ref R? dstRef = ref MemoryMarshal.GetReference(destination);
foreach (T? item in units)
{
if ((uint)i >= (uint)count) break;
Unsafe.Add(ref dstRef, i++) = Math.Sqrt(item.Protected()).ToUnit<R>();
}
}
// === IEnumerable Iterators ===
internal static IEnumerable<R> SqrtIterator<T, R>(this IEnumerable<T> units)
where T : struct, IMensuraUnit, IEquatable<T>
where R : struct, IMensuraUnit, IEquatable<R>
{
foreach (var item in units)
yield return Math.Sqrt(item.ToDouble()).ToUnit<R>();
}
internal static IEnumerable<R?> SqrtNullableIterator<T, R>(this IEnumerable<T?> units)
where T : struct, IMensuraUnit, IEquatable<T>
where R : struct, IMensuraUnit, IEquatable<R>
{
foreach (var item in units)
yield return Math.Sqrt(item.Protected()).ToUnit<R>();
}
// === IEnumerable API ===
internal static IEnumerable<R> Sqrt<T, R>(this IEnumerable<T> units)
where T : struct, IMensuraUnit, IEquatable<T>
where R : struct, IMensuraUnit, IEquatable<R>
{
if (units is null) return null!;
if (units is T[] array) return array.Sqrt<T, R>();
if (units is List<T> list) return list.Sqrt<T, R>();
if (units is IReadOnlyCollection<T> roc)
{
var arr = roc.ToArray();
arr.SqrtCore(arr.Length, arr);
return arr.ReCast<T, R>();
}
else return SqrtIterator<T, R>(units);
}
internal static IEnumerable<R?> Sqrt<T, R>(this IEnumerable<T?> units)
where T : struct, IMensuraUnit, IEquatable<T>
where R : struct, IMensuraUnit, IEquatable<R>
{
if (units is null) return null!;
if (units is T?[] array) return array.Sqrt<T, R>();
if (units is List<T?> list) return list.Sqrt<T, R>();
if (units is IReadOnlyCollection<T?> roc)
{
var arr = roc.ToArray();
arr.SqrtCore(arr.Length, arr);
return arr.ReCast<T, R>();
}
else return SqrtNullableIterator<T, R>(units);
}
}