diff --git a/QWERTYkez.Mensura/Extensions/CastExtensions.cs b/QWERTYkez.Mensura/Extensions/CastExtensions.cs index e0edffd..e0dd07f 100644 --- a/QWERTYkez.Mensura/Extensions/CastExtensions.cs +++ b/QWERTYkez.Mensura/Extensions/CastExtensions.cs @@ -23,7 +23,7 @@ internal static partial class CastExtensions { // Берем адрес управляемого объекта List в памяти // Объект передается по ref-ссылке, преобразуется в указатель - ref var mimic = ref Unsafe.As, ListLayoutMimic>(ref list); + ref var mimic = ref Unsafe.As, Mimics>(ref list); // Меняем приватный размер напрямую в памяти объекта! mimic.Size = count; @@ -40,7 +40,7 @@ internal static partial class CastExtensions var list = new List(0); // Получаем внутреннюю структуру списка - ref var mimic = ref Unsafe.As, ListLayoutMimic>(ref list); + ref var mimic = ref Unsafe.As, Mimics>(ref list); // Подменяем массив и устанавливаем размер mimic.Items = array; @@ -59,7 +59,7 @@ internal static partial class CastExtensions var list = new List(0); // Получаем внутреннюю структуру списка - ref var mimic = ref Unsafe.As, ListLayoutMimic>(ref list); + ref var mimic = ref Unsafe.As, Mimics>(ref list); // Подменяем массив и устанавливаем размер mimic.Items = array; @@ -77,7 +77,7 @@ internal static partial class CastExtensions var list = new List(0); // Получаем внутреннюю структуру списка - ref var mimic = ref Unsafe.As, ListLayoutMimic>(ref list); + ref var mimic = ref Unsafe.As, Mimics>(ref list); // Подменяем массив и устанавливаем размер mimic.Items = array; @@ -95,7 +95,7 @@ internal static partial class CastExtensions var list = new List(0); // Получаем внутреннюю структуру списка - ref var mimic = ref Unsafe.As, ListLayoutMimic>(ref list); + ref var mimic = ref Unsafe.As, Mimics>(ref list); // Подменяем массив и устанавливаем размер mimic.Items = array; @@ -112,7 +112,7 @@ internal static partial class CastExtensions var list = new List(0); // Получаем внутреннюю структуру списка - ref var mimic = ref Unsafe.As, ListLayoutMimic>(ref list); + ref var mimic = ref Unsafe.As, Mimics>(ref list); // Подменяем массив и устанавливаем размер mimic.Items = array; @@ -129,7 +129,7 @@ internal static partial class CastExtensions var list = new List(0); // Получаем внутреннюю структуру списка - ref var mimic = ref Unsafe.As, ListLayoutMimic>(ref list); + ref var mimic = ref Unsafe.As, Mimics>(ref list); // Подменяем массив и устанавливаем размер mimic.Items = array; diff --git a/QWERTYkez.Mensura/Extensions/CollectionsPow2Extensions.cs b/QWERTYkez.Mensura/Extensions/CollectionsPow2Extensions.cs new file mode 100644 index 0000000..0e0e963 --- /dev/null +++ b/QWERTYkez.Mensura/Extensions/CollectionsPow2Extensions.cs @@ -0,0 +1,309 @@ +namespace QWERTYkez.Mensura.Extensions; + +internal static partial class CollectionsPow2Extensions +{ + [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static double QuickPow2(this double base_Value) => base_Value * base_Value; + + + // === ВТОРАЯ СТЕПЕНЬ ========================================== + + // === Pow2Core2 === SIMD + internal static void Pow2Core2(this ReadOnlySpan units, int len, Span destination) + where T : struct, IMensuraUnit, IEquatable + where R : struct, IMensuraUnit, IEquatable + { + // Делаем реинтерпретацию памяти без выделений и копирований + ReadOnlySpan srcDouble = MemoryMarshal.Cast(units[..len]); + Span dstDouble = MemoryMarshal.Cast(destination[..len]); + + int i = 0; + + // Используем SIMD. Vector автоматически выберет AVX2 (4 элемента) или AVX-512 (8 элементов) + if (Vector.IsHardwareAccelerated && len >= Vector.Count) + { + ref double srcStart = ref MemoryMarshal.GetReference(srcDouble); + ref double dstStart = ref MemoryMarshal.GetReference(dstDouble); + int limit = len - Vector.Count; + + for (; i <= limit; i += Vector.Count) + { + ref double srcCurrent = ref Unsafe.Add(ref srcStart, i); + ref double dstCurrent = ref Unsafe.Add(ref dstStart, i); + + // Загружаем вектор, умножаем сам на себя (квадрат) и сохраняем + Vector v = Unsafe.As>(ref srcCurrent); + Unsafe.As>(ref dstCurrent) = v * v; + } + } + + // Добираем остаток элементов, если длина не кратна размеру вектора + for (; i < len; i++) + { + double val = srcDouble[i]; + dstDouble[i] = val * val; + } + } + internal static void Pow2Core2(this ReadOnlySpan units, int len, Span destination) + where T : struct, IMensuraUnit, IEquatable + where R : struct, IMensuraUnit, IEquatable + { + // 1. Получаем низкоуровневые ссылки на первый элемент каждого Span + ref T? srcRef = ref MemoryMarshal.GetReference(units); + ref R? dstRef = ref MemoryMarshal.GetReference(destination); + + // 2. Делаем каст самих ref-ссылок в наш мимик. Компилятор это пропускает. + ref NullableDoubleMimic srcStart = ref Unsafe.As(ref srcRef); + ref NullableDoubleMimic dstStart = ref Unsafe.As(ref dstRef); + + int i = 0; + int limit = len - 4; + + // Loop Unrolling (Развёртка) + for (; i <= limit; i += 4) + { + ref var s0 = ref Unsafe.Add(ref srcStart, i); + ref var s1 = ref Unsafe.Add(ref srcStart, i + 1); + ref var s2 = ref Unsafe.Add(ref srcStart, i + 2); + ref var s3 = ref Unsafe.Add(ref srcStart, i + 3); + + ref var d0 = ref Unsafe.Add(ref dstStart, i); + ref var d1 = ref Unsafe.Add(ref dstStart, i + 1); + ref var d2 = ref Unsafe.Add(ref dstStart, i + 2); + ref var d3 = ref Unsafe.Add(ref dstStart, i + 3); + + d0.Value = s0.Value * s0.Value; + d0.HasValue = s0.HasValue; + + d1.Value = s1.Value * s1.Value; + d1.HasValue = s1.HasValue; + + d2.Value = s2.Value * s2.Value; + d2.HasValue = s2.HasValue; + + d3.Value = s3.Value * s3.Value; + d3.HasValue = s3.HasValue; + } + + // Добираем остатки + for (; i < len; i++) + { + ref var s = ref Unsafe.Add(ref srcStart, i); + ref var d = ref Unsafe.Add(ref dstStart, i); + + d.Value = s.Value * s.Value; + d.HasValue = s.HasValue; + } + } + + + // === ReadOnlySpan + internal static void Pow2(this ReadOnlySpan units, Span destination) + where T : struct, IMensuraUnit, IEquatable + where R : struct, IMensuraUnit, IEquatable + { + int len = units.Length; + if (len == 0) return; + if (len > destination.Length) + throw new ArgumentException("Целевой буфер destination меньше исходного source."); + + units.Pow2Core2(len, destination); + } + internal static void Pow2(this ReadOnlySpan units, Span destination) + where T : struct, IMensuraUnit, IEquatable + where R : struct, IMensuraUnit, IEquatable + { + int len = units.Length; + if (len == 0) return; + if (len > destination.Length) + throw new ArgumentException("Целевой буфер destination меньше исходного source."); + + units.Pow2Core2(len, destination); + } + + // === Array === + internal static R[] Pow2(this T[] units) + where T : struct, IMensuraUnit, IEquatable + where R : struct, IMensuraUnit, IEquatable + { + if (units is null) return null!; + int len = units.Length; + if (len == 0) return []; + + var result = new R[len]; + Pow2(units, result); + return result; + } + internal static R?[] Pow2(this T?[] units) + where T : struct, IMensuraUnit, IEquatable + where R : struct, IMensuraUnit, IEquatable + { + if (units is null) return null!; + int len = units.Length; + if (len == 0) return []; + + var result = new R?[len]; + Pow2(units, result); + return result; + } + + // === List === + internal static List Pow2(this List units) + where T : struct, IMensuraUnit, IEquatable + where R : struct, IMensuraUnit, IEquatable + { + if (units is null) return null!; + int count = units.Count; + if (count == 0) return []; + + var resultArray = new R[count]; + Pow2(CollectionsMarshal.AsSpan(units), resultArray); + return resultArray.WrapAsList(); + } + internal static List Pow2(this List units) + where T : struct, IMensuraUnit, IEquatable + where R : struct, IMensuraUnit, IEquatable + { + if (units is null) return null!; + int count = units.Count; + if (count == 0) return []; + + var resultArray = new R?[count]; + Pow2(CollectionsMarshal.AsSpan(units), resultArray); + return resultArray.WrapAsList(); + } + + // === ICollection === + internal static void Pow2(this ICollection units, Span destination) + where T : struct, IMensuraUnit, IEquatable + where R : struct, IMensuraUnit, IEquatable + { + 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.Pow2(destination); return; } + if (units is List list) { CollectionsMarshal.AsSpan(list).Pow2(destination); return; } + + int i = 0; + foreach (var item in units) + destination[i++] = item.ToDouble().QuickPow2().ToUnit(); + } + internal static void Pow2(this ICollection units, Span destination) + where T : struct, IMensuraUnit, IEquatable + where R : struct, IMensuraUnit, IEquatable + { + 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.Pow2(destination); return; } + if (units is List list) { CollectionsMarshal.AsSpan(list).Pow2(destination); return; } + + int i = 0; + foreach (var item in units) + destination[i++] = item.HasValue + ? item.Value.ToDouble().QuickPow2().ToUnit() : null; + } + + // === IReadOnlyCollection === + internal static void Pow2(this IReadOnlyCollection units, Span destination) + where T : struct, IMensuraUnit, IEquatable + where R : struct, IMensuraUnit, IEquatable + { + 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.Pow2(destination); return; } + if (units is List list) { CollectionsMarshal.AsSpan(list).Pow2(destination); return; } + + int i = 0; + foreach (var item in units) + destination[i++] = item.ToDouble().QuickPow2().ToUnit(); + } + internal static void Pow2(this IReadOnlyCollection units, Span destination) + where T : struct, IMensuraUnit, IEquatable + where R : struct, IMensuraUnit, IEquatable + { + 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.Pow2(destination); return; } + if (units is List list) { CollectionsMarshal.AsSpan(list).Pow2(destination); return; } + + int i = 0; + foreach (var item in units) + destination[i++] = item.HasValue + ? item.Value.ToDouble().QuickPow2().ToUnit() : null; + } + + // === IEnumerable + yeild === + static IEnumerable Pow2Iterator(IEnumerable units) + where T : struct, IMensuraUnit, IEquatable + where R : struct, IMensuraUnit, IEquatable + { + foreach (var item in units) + yield return (item.ToDouble().QuickPow2()).ToUnit(); + } + static IEnumerable Pow2NullableIterator(IEnumerable units) + where T : struct, IMensuraUnit, IEquatable + where R : struct, IMensuraUnit, IEquatable + { + foreach (var item in units) + yield return item.HasValue + ? item.Value.ToDouble().QuickPow2().ToUnit() : null; + } + + // === IEnumerable === + internal static IEnumerable Pow2(this IEnumerable units) + where T : struct, IMensuraUnit, IEquatable + where R : struct, IMensuraUnit, IEquatable + { + if (units is null) return null!; + if (units is T[] array) return array.Pow2(); + if (units is List list) return list.Pow2(); + if (units is ICollection col) + { + var arr = col.ToArray(); + arr.Pow2Core2(arr.Length, arr); + return arr.ReCast(); + } + if (units is IReadOnlyCollection roc) + { + var arr = roc.ToArray(); + arr.Pow2(arr); + return arr.ReCast(); + } + else return Pow2Iterator(units); + } + internal static IEnumerable Pow2(this IEnumerable units) + where T : struct, IMensuraUnit, IEquatable + where R : struct, IMensuraUnit, IEquatable + { + if (units is null) return null!; + if (units is T?[] array) return array.Pow2(); + if (units is List list) return list.Pow2(); + if (units is ICollection col) + { + var arr = col.ToArray(); + arr.Pow2(arr); + return arr.ReCast(); + } + if (units is IReadOnlyCollection roc) + { + var arr = roc.ToArray(); + arr.Pow2(arr); + return arr.ReCast(); + } + else return Pow2NullableIterator(units); + } +} \ No newline at end of file diff --git a/QWERTYkez.Mensura/Extensions/CollectionsPow3Extensions.cs b/QWERTYkez.Mensura/Extensions/CollectionsPow3Extensions.cs new file mode 100644 index 0000000..6df3321 --- /dev/null +++ b/QWERTYkez.Mensura/Extensions/CollectionsPow3Extensions.cs @@ -0,0 +1,312 @@ +using System.Runtime.Intrinsics; +using System.Runtime.Intrinsics.X86; + +namespace QWERTYkez.Mensura.Extensions; + +internal static partial class CollectionsPow3Extensions +{ + [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static double QuickPow3(this double base_Value) => base_Value * base_Value * base_Value; + + + // === ТРЕТЬЯ СТЕПЕНЬ ========================================== + + // === PowCore3 === SIMD + internal static void PowCore3(this ReadOnlySpan units, int len, Span destination) + where T : struct, IMensuraUnit, IEquatable + where R : struct, IMensuraUnit, IEquatable + { + // Делаем реинтерпретацию памяти без выделений и копирований + ReadOnlySpan srcDouble = MemoryMarshal.Cast(units[..len]); + Span dstDouble = MemoryMarshal.Cast(destination[..len]); + + int i = 0; + + // Используем SIMD. Vector автоматически выберет AVX2 (4 элемента) или AVX-512 (8 элементов) + if (Vector.IsHardwareAccelerated && len >= Vector.Count) + { + ref double srcStart = ref MemoryMarshal.GetReference(srcDouble); + ref double dstStart = ref MemoryMarshal.GetReference(dstDouble); + int limit = len - Vector.Count; + + for (; i <= limit; i += Vector.Count) + { + ref double srcCurrent = ref Unsafe.Add(ref srcStart, i); + ref double dstCurrent = ref Unsafe.Add(ref dstStart, i); + + // Загружаем вектор, умножаем сам на себя (квадрат) и сохраняем + Vector v = Unsafe.As>(ref srcCurrent); + Unsafe.As>(ref dstCurrent) = v * v * v; + } + } + + // Добираем остаток элементов, если длина не кратна размеру вектора + for (; i < len; i++) + { + double val = srcDouble[i]; + dstDouble[i] = val * val * val; + } + } + internal static void PowCore3(this ReadOnlySpan units, int len, Span destination) + where T : struct, IMensuraUnit, IEquatable + where R : struct, IMensuraUnit, IEquatable + { + // 1. Получаем низкоуровневые ссылки на первый элемент каждого Span + ref T? srcRef = ref MemoryMarshal.GetReference(units); + ref R? dstRef = ref MemoryMarshal.GetReference(destination); + + // 2. Делаем каст самих ref-ссылок в наш мимик. Компилятор это пропускает. + ref NullableDoubleMimic srcStart = ref Unsafe.As(ref srcRef); + ref NullableDoubleMimic dstStart = ref Unsafe.As(ref dstRef); + + int i = 0; + int limit = len - 4; + + // Loop Unrolling (Развёртка) + for (; i <= limit; i += 4) + { + ref var s0 = ref Unsafe.Add(ref srcStart, i); + ref var s1 = ref Unsafe.Add(ref srcStart, i + 1); + ref var s2 = ref Unsafe.Add(ref srcStart, i + 2); + ref var s3 = ref Unsafe.Add(ref srcStart, i + 3); + + ref var d0 = ref Unsafe.Add(ref dstStart, i); + ref var d1 = ref Unsafe.Add(ref dstStart, i + 1); + ref var d2 = ref Unsafe.Add(ref dstStart, i + 2); + ref var d3 = ref Unsafe.Add(ref dstStart, i + 3); + + d0.Value = s0.Value * s0.Value * s0.Value; + d0.HasValue = s0.HasValue; + + d1.Value = s1.Value * s1.Value * s1.Value; + d1.HasValue = s1.HasValue; + + d2.Value = s2.Value * s2.Value * s2.Value; + d2.HasValue = s2.HasValue; + + d3.Value = s3.Value * s3.Value * s3.Value; + d3.HasValue = s3.HasValue; + } + + // Добираем остатки + for (; i < len; i++) + { + ref var s = ref Unsafe.Add(ref srcStart, i); + ref var d = ref Unsafe.Add(ref dstStart, i); + + d.Value = s.Value * s.Value * s.Value; + d.HasValue = s.HasValue; + } + } + + + // === ReadOnlySpan + internal static void Pow3(this ReadOnlySpan units, Span destination) + where T : struct, IMensuraUnit, IEquatable + where R : struct, IMensuraUnit, IEquatable + { + int len = units.Length; + if (len == 0) return; + if (len > destination.Length) + throw new ArgumentException("Целевой буфер destination меньше исходного source."); + + units.PowCore3(len, destination); + } + internal static void Pow3(this ReadOnlySpan units, Span destination) + where T : struct, IMensuraUnit, IEquatable + where R : struct, IMensuraUnit, IEquatable + { + int len = units.Length; + if (len == 0) return; + if (len > destination.Length) + throw new ArgumentException("Целевой буфер destination меньше исходного source."); + + units.PowCore3(len, destination); + } + + // === Array === + internal static R[] Pow3(this T[] units) + where T : struct, IMensuraUnit, IEquatable + where R : struct, IMensuraUnit, IEquatable + { + if (units is null) return null!; + int len = units.Length; + if (len == 0) return []; + + var result = new R[len]; + Pow3(units, result); + return result; + } + internal static R?[] Pow3(this T?[] units) + where T : struct, IMensuraUnit, IEquatable + where R : struct, IMensuraUnit, IEquatable + { + if (units is null) return null!; + int len = units.Length; + if (len == 0) return []; + + var result = new R?[len]; + Pow3(units, result); + return result; + } + + // === List === + internal static List Pow3(this List units) + where T : struct, IMensuraUnit, IEquatable + where R : struct, IMensuraUnit, IEquatable + { + if (units is null) return null!; + int count = units.Count; + if (count == 0) return []; + + var resultArray = new R[count]; + Pow3(CollectionsMarshal.AsSpan(units), resultArray); + return resultArray.WrapAsList(); + } + internal static List Pow3(this List units) + where T : struct, IMensuraUnit, IEquatable + where R : struct, IMensuraUnit, IEquatable + { + if (units is null) return null!; + int count = units.Count; + if (count == 0) return []; + + var resultArray = new R?[count]; + Pow3(CollectionsMarshal.AsSpan(units), resultArray); + return resultArray.WrapAsList(); + } + + // === ICollection === + internal static void Pow3(this ICollection units, Span destination) + where T : struct, IMensuraUnit, IEquatable + where R : struct, IMensuraUnit, IEquatable + { + 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.Pow3(destination); return; } + if (units is List list) { CollectionsMarshal.AsSpan(list).Pow3(destination); return; } + + int i = 0; + foreach (var item in units) + destination[i++] = item.ToDouble().QuickPow3().ToUnit(); + } + internal static void Pow3(this ICollection units, Span destination) + where T : struct, IMensuraUnit, IEquatable + where R : struct, IMensuraUnit, IEquatable + { + 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.Pow3(destination); return; } + if (units is List list) { CollectionsMarshal.AsSpan(list).Pow3(destination); return; } + + int i = 0; + foreach (var item in units) + destination[i++] = item.HasValue + ? item.Value.ToDouble().QuickPow3().ToUnit() : null; + } + + // === IReadOnlyCollection === + internal static void Pow3(this IReadOnlyCollection units, Span destination) + where T : struct, IMensuraUnit, IEquatable + where R : struct, IMensuraUnit, IEquatable + { + 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.Pow3(destination); return; } + if (units is List list) { CollectionsMarshal.AsSpan(list).Pow3(destination); return; } + + int i = 0; + foreach (var item in units) + destination[i++] = item.ToDouble().QuickPow3().ToUnit(); + } + internal static void Pow3(this IReadOnlyCollection units, Span destination) + where T : struct, IMensuraUnit, IEquatable + where R : struct, IMensuraUnit, IEquatable + { + 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.Pow3(destination); return; } + if (units is List list) { CollectionsMarshal.AsSpan(list).Pow3(destination); return; } + + int i = 0; + foreach (var item in units) + destination[i++] = item.HasValue + ? item.Value.ToDouble().QuickPow3().ToUnit() : null; + } + + // === IEnumerable + yeild === + static IEnumerable PowIterator3(IEnumerable units) + where T : struct, IMensuraUnit, IEquatable + where R : struct, IMensuraUnit, IEquatable + { + foreach (var item in units) + yield return (item.ToDouble().QuickPow3()).ToUnit(); + } + static IEnumerable PowNullableIterator3(IEnumerable units) + where T : struct, IMensuraUnit, IEquatable + where R : struct, IMensuraUnit, IEquatable + { + foreach (var item in units) + yield return item.HasValue + ? item.Value.ToDouble().QuickPow3().ToUnit() : null; + } + + // === IEnumerable === + internal static IEnumerable Pow3(this IEnumerable units) + where T : struct, IMensuraUnit, IEquatable + where R : struct, IMensuraUnit, IEquatable + { + if (units is null) return null!; + if (units is T[] array) return array.Pow3(); + if (units is List list) return list.Pow3(); + if (units is ICollection col) + { + var arr = col.ToArray(); + arr.PowCore3(arr.Length, arr); + return arr.ReCast(); + } + if (units is IReadOnlyCollection roc) + { + var arr = roc.ToArray(); + arr.Pow3(arr); + return arr.ReCast(); + } + else return PowIterator3(units); + } + internal static IEnumerable Pow3(this IEnumerable units) + where T : struct, IMensuraUnit, IEquatable + where R : struct, IMensuraUnit, IEquatable + { + if (units is null) return null!; + if (units is T?[] array) return array.Pow3(); + if (units is List list) return list.Pow3(); + if (units is ICollection col) + { + var arr = col.ToArray(); + arr.Pow3(arr); + return arr.ReCast(); + } + if (units is IReadOnlyCollection roc) + { + var arr = roc.ToArray(); + arr.Pow3(arr); + return arr.ReCast(); + } + else return PowNullableIterator3(units); + } +} \ No newline at end of file diff --git a/QWERTYkez.Mensura/Extensions/CollectionsPowExtensions.cs b/QWERTYkez.Mensura/Extensions/CollectionsPowNExtensions.cs similarity index 99% rename from QWERTYkez.Mensura/Extensions/CollectionsPowExtensions.cs rename to QWERTYkez.Mensura/Extensions/CollectionsPowNExtensions.cs index 08eaf84..04f43fb 100644 --- a/QWERTYkez.Mensura/Extensions/CollectionsPowExtensions.cs +++ b/QWERTYkez.Mensura/Extensions/CollectionsPowNExtensions.cs @@ -3,11 +3,9 @@ using System.Runtime.Intrinsics.X86; namespace QWERTYkez.Mensura.Extensions; -internal static partial class CollectionsPowExtensions +internal static partial class CollectionsPowNExtensions { - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static double QuickPow(this double base_Value, int exp) + [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static double QuickPow(this double base_Value, int exp) { switch (exp) { diff --git a/QWERTYkez.Mensura/Extensions/CollectionsSqrt3Extensions.cs b/QWERTYkez.Mensura/Extensions/CollectionsSqrt3Extensions.cs new file mode 100644 index 0000000..2f3f85b --- /dev/null +++ b/QWERTYkez.Mensura/Extensions/CollectionsSqrt3Extensions.cs @@ -0,0 +1,317 @@ +using System.Runtime.Intrinsics.X86; + +namespace QWERTYkez.Mensura.Extensions; + +internal static partial class CollectionsSqrt3Extensions +{ + // === SqrtCore3 === SIMD + internal static unsafe void SqrtCore3(this ReadOnlySpan units, int len, Span destination) + where T : struct, IMensuraUnit, IEquatable + where R : struct, IMensuraUnit, IEquatable + { + ReadOnlySpan srcDouble = MemoryMarshal.Cast(units); + Span dstDouble = MemoryMarshal.Cast(destination); + + int i = 0; + + ref double srcRef = ref MemoryMarshal.GetReference(srcDouble); + ref double dstRef = ref MemoryMarshal.GetReference(dstDouble); + + // 1. ПУТЬ AVX (x64 Процессоры Intel/AMD) — обрабатываем по 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); + + // Получаем указатели через Unsafe.AsPointer + double* pSrc = (double*)Unsafe.AsPointer(ref currentSrc); + double* pDst = (double*)Unsafe.AsPointer(ref currentDst); + + // Выровненная загрузка (требует, чтобы pSrc был кратен 32) + var v = Avx.LoadVector256(pSrc); + var sqrtV = Avx.Sqrt(v); + // Выровненное сохранение + Avx.Store(pDst, sqrtV); + } + } + // 2. ПУТЬ VECTOR (ARM64 / Apple Silicon / Старые CPU без AVX) + else if (Vector.IsHardwareAccelerated && len >= Vector.Count) + { + int vCount = Vector.Count; + int simdEnd = len & ~(vCount - 1); + + for (; i < simdEnd; i += vCount) + { + // Используем Span для кроссплатформенного создания вектора + var v = new Vector(srcDouble.Slice(i, vCount)); + + // Кроссплатформенный аппаратный корень (на ARM превратится в NEON инструкцию) + var sqrtV = Vector.SquareRoot(v); + + // Копируем напрямую в целевой Span + sqrtV.CopyTo(dstDouble.Slice(i, vCount)); + } + } + + // 3. Хвост массива (или обычный расчет, если SIMD на процессоре недоступен) + for (; i < len; i++) + { + Unsafe.Add(ref dstRef, i) = Math.Sqrt(Unsafe.Add(ref srcRef, i)); + } + } + internal static void SqrtCore3(this ReadOnlySpan units, int len, Span destination) + where T : struct, IMensuraUnit, IEquatable + where R : struct, IMensuraUnit, IEquatable + { + // Получаем прямые ref-ссылки на начало буферов за 0 тактов процессора + ref var srcRef = ref MemoryMarshal.GetReference(units); + ref var dstRef = ref MemoryMarshal.GetReference(destination); + + int i = 0; + int unrollEnd = len & ~3; // Граница развернутого цикла (кратная 4) + + // 1. ОСНОВНОЙ ЦИКЛ: Конвейерная обработка по 4 элемента за итерацию + for (; i < unrollEnd; i += 4) + { + T? u0 = Unsafe.Add(ref srcRef, i); + T? u1 = Unsafe.Add(ref srcRef, i + 1); + T? u2 = Unsafe.Add(ref srcRef, i + 2); + T? u3 = Unsafe.Add(ref srcRef, i + 3); + + // Получаем ref-ссылки на целевые ячейки типа R? (zero-cost адресация) + 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); + + // Считаем нативный корень прямо в регистрах и трансформируем тип из T в R по месту + d0 = u0.HasValue ? Math.Sqrt(u0.Value.ToDouble()).ToUnit() : null; + d1 = u1.HasValue ? Math.Sqrt(u1.Value.ToDouble()).ToUnit() : null; + d2 = u2.HasValue ? Math.Sqrt(u2.Value.ToDouble()).ToUnit() : null; + d3 = u3.HasValue ? Math.Sqrt(u3.Value.ToDouble()).ToUnit() : null; + } + + // 2. ХВОСТ ЦИКЛА: Довычисляем остаток элементов (от 1 до 3 штук) + for (; i < len; i++) + { + T? unit = Unsafe.Add(ref srcRef, i); + ref var dst = ref Unsafe.Add(ref dstRef, i); + dst = unit.HasValue ? Math.Sqrt(unit.Value.ToDouble()).ToUnit() : null; + } + } + + + + + // === ReadOnlySpan === + internal static void Sqrt3(this ReadOnlySpan units, Span destination) + where T : struct, IMensuraUnit, IEquatable + where R : struct, IMensuraUnit, IEquatable + { + if (units.IsEmpty) return; + int len = units.Length; + if (len > destination.Length) + throw new ArgumentException("Целевой буфер destination меньше исходного source."); + + units.SqrtCore3(len, destination); + } + internal static void Sqrt3(this ReadOnlySpan units, Span destination) + where T : struct, IMensuraUnit, IEquatable + where R : struct, IMensuraUnit, IEquatable + { + if (units.IsEmpty) return; + int len = units.Length; + if (len > destination.Length) + throw new ArgumentException("Целевой буфер destination меньше исходного source."); + + units.SqrtCore3(len, destination); + } + + // === Array === + internal static R[] Sqrt3(this T[] units) + where T : struct, IMensuraUnit, IEquatable + where R : struct, IMensuraUnit, IEquatable + { + if (units is null) return null!; + if (units.Length == 0) return []; + + var result = new R[units.Length]; + Sqrt3(units, result); + return result; + } + internal static R?[] Sqrt3(this T?[] units) + where T : struct, IMensuraUnit, IEquatable + where R : struct, IMensuraUnit, IEquatable + { + if (units is null) return null!; + if (units.Length == 0) return []; + + var result = new R?[units.Length]; + Sqrt3(units, result); + return result; + } + + // === List === + internal static List Sqrt3(this List units) + where T : struct, IMensuraUnit, IEquatable + where R : struct, IMensuraUnit, IEquatable + { + if (units is null) return null!; + int len = units.Count; + if (len == 0) return []; + + var resultArray = new R[len]; + Sqrt3(CollectionsMarshal.AsSpan(units), resultArray); + return resultArray.WrapAsList(); + } + internal static List Sqrt3(this List units) + where T : struct, IMensuraUnit, IEquatable + where R : struct, IMensuraUnit, IEquatable + { + if (units is null) return null!; + int len = units.Count; + if (len == 0) return []; + + var resultArray = new R?[len]; + Sqrt3(CollectionsMarshal.AsSpan(units), resultArray); + return resultArray.WrapAsList(); + } + + // === ICollection === + internal static void Sqrt3(this ICollection units, Span destination) + where T : struct, IMensuraUnit, IEquatable + where R : struct, IMensuraUnit, IEquatable + { + 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.Sqrt3(destination); return; } + if (units is List list) { CollectionsMarshal.AsSpan(list).Sqrt3(destination); return; } + + int i = 0; + foreach (T item in units) + destination[i++] = Math.Sqrt(item.ToDouble()).ToUnit(); + } + internal static void Sqrt3(this ICollection units, Span destination) + where T : struct, IMensuraUnit, IEquatable + where R : struct, IMensuraUnit, IEquatable + { + 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.Sqrt3(destination); return; } + if (units is List list) { CollectionsMarshal.AsSpan(list).Sqrt3(destination); return; } + + int i = 0; + foreach (T? item in units) + destination[i++] = item.HasValue + ? Math.Sqrt(item.Value.ToDouble()).ToUnit() : null; + } + + // === IReadOnlyCollection === + internal static void Sqrt3(this IReadOnlyCollection units, Span destination) + where T : struct, IMensuraUnit, IEquatable + where R : struct, IMensuraUnit, IEquatable + { + 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.Sqrt3(destination); return; } + if (units is List list) { CollectionsMarshal.AsSpan(list).Sqrt3(destination); return; } + + int i = 0; + foreach (T item in units) + destination[i++] = Math.Sqrt(item.ToDouble()).ToUnit(); + } + internal static void Sqrt3(this IReadOnlyCollection units, Span destination) + where T : struct, IMensuraUnit, IEquatable + where R : struct, IMensuraUnit, IEquatable + { + 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.Sqrt3(destination); return; } + if (units is List list) { CollectionsMarshal.AsSpan(list).Sqrt3(destination); return; } + + int i = 0; + foreach (T? item in units) + destination[i++] = item.HasValue + ? Math.Sqrt(item.Value.ToDouble()).ToUnit() : null; + } + + // === IEnumerable + yield === + internal static IEnumerable SqrtIterator3(this IEnumerable units) + where T : struct, IMensuraUnit, IEquatable + where R : struct, IMensuraUnit, IEquatable + { + foreach (var item in units) + yield return Math.Sqrt(item.ToDouble()).ToUnit(); + } + internal static IEnumerable SqrtNullableIterator3(this IEnumerable units) + where T : struct, IMensuraUnit, IEquatable + where R : struct, IMensuraUnit, IEquatable + { + foreach (var item in units) + yield return item.HasValue + ? Math.Sqrt(item.Value.ToDouble()).ToUnit() : null; + } + + // === IEnumerable === + internal static IEnumerable Sqrt3(this IEnumerable units) + where T : struct, IMensuraUnit, IEquatable + where R : struct, IMensuraUnit, IEquatable + { + if (units is null) return null!; + if (units is T[] array) return array.Sqrt3(); + if (units is List list) return list.Sqrt3(); + if (units is ICollection col) + { + var arr = col.ToArray(); + arr.Sqrt3(arr); + return arr.ReCast(); + } + if (units is IReadOnlyCollection roc) + { + var arr = roc.ToArray(); + arr.Sqrt3(arr); + return arr.ReCast(); + } + else return SqrtIterator3(units); + } + internal static IEnumerable Sqrt3(this IEnumerable units) + where T : struct, IMensuraUnit, IEquatable + where R : struct, IMensuraUnit, IEquatable + { + if (units is null) return null!; + if (units is T?[] array) return array.Sqrt3(); + if (units is List list) return list.Sqrt3(); + if (units is ICollection col) + { + var arr = col.ToArray(); + arr.Sqrt3(arr); + return arr.ReCast(); + } + if (units is IReadOnlyCollection roc) + { + var arr = roc.ToArray(); + arr.Sqrt3(arr); + return arr.ReCast(); + } + else return SqrtNullableIterator3(units); + } +} \ No newline at end of file diff --git a/QWERTYkez.Mensura/ListLayoutMimic.cs b/QWERTYkez.Mensura/ListLayoutMimic.cs deleted file mode 100644 index 0cc2d47..0000000 --- a/QWERTYkez.Mensura/ListLayoutMimic.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace QWERTYkez.Mensura; - -[StructLayout(LayoutKind.Sequential)] -internal class ListLayoutMimic -{ - public T[] Items = null!; - public int Size; - public int Version; -} \ No newline at end of file diff --git a/QWERTYkez.Mensura/Mimics.cs b/QWERTYkez.Mensura/Mimics.cs new file mode 100644 index 0000000..9c1dc18 --- /dev/null +++ b/QWERTYkez.Mensura/Mimics.cs @@ -0,0 +1,16 @@ +namespace QWERTYkez.Mensura; + +[StructLayout(LayoutKind.Sequential)] +internal class Mimics +{ + public T[] Items = null!; + public int Size; + public int Version; +} + +[StructLayout(LayoutKind.Sequential)] +internal struct NullableDoubleMimic +{ + public double Value; + public byte HasValue; +} \ No newline at end of file diff --git a/QWERTYkez.Mensura/Units/Area.cs b/QWERTYkez.Mensura/Units/Area.cs index deed4bd..9ee7c07 100644 --- a/QWERTYkez.Mensura/Units/Area.cs +++ b/QWERTYkez.Mensura/Units/Area.cs @@ -61,21 +61,21 @@ public static class AreaSqrtExtension { // === ReadOnlySpan [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Sqrt( - this ReadOnlySpan units, Span destination) => units.Sqrt(destination); + this ReadOnlySpan units, Span destination) => units.Sqrt3(destination); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Sqrt( - this ReadOnlySpan units, Span destination) => units.Sqrt(destination); + this ReadOnlySpan units, Span destination) => units.Sqrt3(destination); // === Array === - [MethodImpl(MethodImplOptions.AggressiveInlining)]public static Length[] Sqrt(this Area[] units) => units.Sqrt(); - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Length?[] Sqrt(this Area?[] units) => units.Sqrt(); + [MethodImpl(MethodImplOptions.AggressiveInlining)]public static Length[] Sqrt(this Area[] units) => units.Sqrt3(); + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Length?[] Sqrt(this Area?[] units) => units.Sqrt3(); // === List === - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static List Sqrt(this List units) => units.Sqrt(); - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static List Sqrt(this List units) => units.Sqrt(); + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static List Sqrt(this List units) => units.Sqrt3(); + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static List Sqrt(this List units) => units.Sqrt3(); // === IEnumerable === - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static IEnumerable Sqrt(this IEnumerable units) => units.Sqrt(); - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static IEnumerable Sqrt(this IEnumerable units) => units.Sqrt(); + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static IEnumerable Sqrt(this IEnumerable units) => units.Sqrt3(); + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static IEnumerable Sqrt(this IEnumerable units) => units.Sqrt3(); } internal readonly struct AreaConv diff --git a/QWERTYkez.Mensura/Units/Length.cs b/QWERTYkez.Mensura/Units/Length.cs index 2582d7f..5720015 100644 --- a/QWERTYkez.Mensura/Units/Length.cs +++ b/QWERTYkez.Mensura/Units/Length.cs @@ -86,34 +86,30 @@ public readonly partial record struct Length public static class LengthPowExtension { // === ReadOnlySpan - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Pow( - this ReadOnlySpan units, Span destination) => units.Pow(destination); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Pow( - this ReadOnlySpan units, Span destination) => units.Pow(destination); + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Pow( + this ReadOnlySpan units, Span destination) => units.Pow2(destination); + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Pow( + this ReadOnlySpan units, Span destination) => units.Pow2(destination); // === Array === - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Area[] Pow(this Length[] units) => units.Pow(); - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Area?[] Pow(this Length?[] units) => units.Pow(); + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Area[] Pow(this Length[] units) => units.Pow2(); + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Area?[] Pow(this Length?[] units) => units.Pow2(); // === List === - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static List Pow(this List units) => units.Pow(); - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static List Pow(this List units) => units.Pow(); + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static List Pow(this List units) => units.Pow2(); + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static List Pow(this List units) => units.Pow2(); // === IEnumerable === - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static IEnumerable Pow(this IEnumerable units) => units.Pow(); - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static IEnumerable Pow(this IEnumerable units) => units.Pow(); + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static IEnumerable Pow(this IEnumerable units) => units.Pow2(); + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static IEnumerable Pow(this IEnumerable units) => units.Pow2(); // === ReadOnlySpan - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Pow3( + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Pow3( this ReadOnlySpan units, Span destination) => units.Pow3(destination); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Pow3( + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Pow3( this ReadOnlySpan units, Span destination) => units.Pow3(destination); // === Array === diff --git a/QWERTYkez.Mensura/Units/Voltage.cs b/QWERTYkez.Mensura/Units/Voltage.cs index 8ab42ed..1662ff4 100644 --- a/QWERTYkez.Mensura/Units/Voltage.cs +++ b/QWERTYkez.Mensura/Units/Voltage.cs @@ -35,29 +35,6 @@ public readonly partial record struct Voltage public Voltage AddMegaVolts(double value) => new(_Value + VoltageConv.MegaVolts.To(value)); } -public static class VolumeSqrtExtension -{ - // === ReadOnlySpan - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Sqrt3( - this ReadOnlySpan units, Span destination) => units.Sqrt3(destination); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Sqrt3( - this ReadOnlySpan units, Span destination) => units.Sqrt3(destination); - - // === Array === - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Length[] Sqrt3(this Volume[] units) => units.Sqrt3(); - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Length?[] Sqrt3(this Volume?[] units) => units.Sqrt3(); - - // === List === - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static List Sqrt3(this List units) => units.Sqrt3(); - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static List Sqrt3(this List units) => units.Sqrt3(); - - // === IEnumerable === - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static IEnumerable Sqrt3(this IEnumerable units) => units.Sqrt3(); - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static IEnumerable Sqrt3(this IEnumerable units) => units.Sqrt3(); -} - internal readonly struct VoltageConv { private VoltageConv(double multiplicator) => this.Multiplicator = multiplicator; diff --git a/QWERTYkez.Mensura/Units/Volume.cs b/QWERTYkez.Mensura/Units/Volume.cs index c7fb1c7..6396224 100644 --- a/QWERTYkez.Mensura/Units/Volume.cs +++ b/QWERTYkez.Mensura/Units/Volume.cs @@ -35,6 +35,27 @@ public readonly partial record struct Volume public Volume AddMetersCubic(double value) => new(_Value + VolumeConv.MetersCubic.To(value)); } +public static class VolumeSqrtExtension +{ + // === ReadOnlySpan + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Sqrt3( + this ReadOnlySpan units, Span destination) => units.Sqrt3(destination); + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Sqrt3( + this ReadOnlySpan units, Span destination) => units.Sqrt3(destination); + + // === Array === + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Length[] Sqrt3(this Volume[] units) => units.Sqrt3(); + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Length?[] Sqrt3(this Volume?[] units) => units.Sqrt3(); + + // === List === + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static List Sqrt3(this List units) => units.Sqrt3(); + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static List Sqrt3(this List units) => units.Sqrt3(); + + // === IEnumerable === + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static IEnumerable Sqrt3(this IEnumerable units) => units.Sqrt3(); + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static IEnumerable Sqrt3(this IEnumerable units) => units.Sqrt3(); +} + internal readonly struct VolumeConv { private VolumeConv(double multiplicator) => this.Multiplicator = multiplicator;