Files
QWERTYkez.Mensura/QWERTYkez.Mensura/Extensions/CollectionsMultiplyExtensions.cs
melekhin c27c2e0085 26.06.03
2026-06-03 12:07:27 +07:00

337 lines
15 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.
namespace QWERTYkez.Mensura.Extensions;
public static partial class CollectionsMultiplyExtensions
{
// === MultiplyCore === SIMD
internal static void MultiplyCore<T, R>(this ReadOnlySpan<T> units, double multiplicator, 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);
var vectorizedMultiplicator = new Vector<double>(multiplicator);
int vectorSize = Vector<double>.Count;
int i = 0;
ref double srcRef = ref MemoryMarshal.GetReference(srcDouble);
ref double dstRef = ref MemoryMarshal.GetReference(dstDouble);
int simdEnd = len & ~(vectorSize - 1);
for (; i < simdEnd; i += vectorSize)
{
ref double currentSrc = ref Unsafe.Add(ref srcRef, i);
ref double currentDst = ref Unsafe.Add(ref dstRef, i);
var vector = Unsafe.As<double, Vector<double>>(ref currentSrc);
var multiplied = vector * vectorizedMultiplicator;
Unsafe.As<double, Vector<double>>(ref currentDst) = multiplied;
}
for (; i < len; i++)
{
Unsafe.Add(ref dstRef, i) = Unsafe.Add(ref srcRef, i) * multiplicator;
}
}
internal static void MultiplyCore<T, R>(this ReadOnlySpan<T?> units, double multiplicator, int len, Span<R?> destination)
where T : struct, IMensuraUnit, IEquatable<T>
where R : struct, IMensuraUnit, IEquatable<R>
{
// Получаем прямые неуправляемые 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-ссылки на ячейки назначения (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);
// Пишем строго по месту. Если HasValue == false, в ячейку dX запишется null (сбросятся байты флага)
d0 = u0.HasValue ? (u0.Value.ToDouble() * multiplicator).ToUnit<R>() : null;
d1 = u1.HasValue ? (u1.Value.ToDouble() * multiplicator).ToUnit<R>() : null;
d2 = u2.HasValue ? (u2.Value.ToDouble() * multiplicator).ToUnit<R>() : null;
d3 = u3.HasValue ? (u3.Value.ToDouble() * multiplicator).ToUnit<R>() : 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 ? (unit.Value.ToDouble() * multiplicator).ToUnit<R>() : null;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static void MultiplyCore<T, R>(this double multiplicator, ReadOnlySpan<T> units, int len, Span<R> destination)
where T : struct, IMensuraUnit, IEquatable<T>
where R : struct, IMensuraUnit, IEquatable<R> => units.MultiplyCore(multiplicator, len, destination);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static void MultiplyCore<T, R>(this double multiplicator, ReadOnlySpan<T?> units, int len, Span<R?> destination)
where T : struct, IMensuraUnit, IEquatable<T>
where R : struct, IMensuraUnit, IEquatable<R> => units.MultiplyCore(multiplicator, len, destination);
// === ReadOnlySpan
public static void Multiply<T>(this ReadOnlySpan<T> units, double multiplicator, Span<T> destination)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (units.IsEmpty) return;
int len = units.Length;
if (len > destination.Length)
throw new ArgumentException("Целевой буфер destination меньше исходного source.");
units.MultiplyCore(multiplicator, len, destination);
}
public static void Multiply<T>(this ReadOnlySpan<T?> units, double multiplicator, Span<T?> destination)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (units.IsEmpty) return;
int len = units.Length;
if (len > destination.Length)
throw new ArgumentException("Целевой буфер destination меньше исходного source.");
units.MultiplyCore(multiplicator, len, destination);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Multiply<T>(this double multiplicator, ReadOnlySpan<T> units, Span<T> destination)
where T : struct, IMensuraUnit, IEquatable<T> => units.Multiply(multiplicator, destination);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Multiply<T>(this double multiplicator, ReadOnlySpan<T?> units, Span<T?> destination)
where T : struct, IMensuraUnit, IEquatable<T> => units.Multiply(multiplicator, destination);
// === Array ===
public static T[] Multiply<T>(this T[] units, double multiplicator)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (units is null) return null!;
int len = units.Length;
if (len == 0) return [];
var result = new T[len];
Multiply(units, multiplicator, result);
return result;
}
public static T?[] Multiply<T>(this T?[] units, double multiplicator)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (units is null) return null!;
int len = units.Length;
if (len == 0) return [];
var result = new T?[len];
Multiply(units, multiplicator, result);
return result;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T[] Multiply<T>(this double multiplicator, T[] units)
where T : struct, IMensuraUnit, IEquatable<T> => units.Multiply(multiplicator);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T?[] Multiply<T>(this double multiplicator, T?[] units)
where T : struct, IMensuraUnit, IEquatable<T> => units.Multiply(multiplicator);
// === List<T> ===
public static List<T> Multiply<T>(this List<T> units, double multiplicator)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (units is null) return null!;
int len = units.Count;
if (len == 0) return [];
var resultArray = new T[len];
Multiply(CollectionsMarshal.AsSpan(units), multiplicator, resultArray);
return resultArray.WrapAsList();
}
public static List<T?> Multiply<T>(this List<T?> units, double multiplicator)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (units is null) return null!;
int count = units.Count;
if (count == 0) return [];
var resultArray = new T?[count];
Multiply(CollectionsMarshal.AsSpan(units), multiplicator, resultArray);
return resultArray.WrapAsList();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static List<T> Multiply<T>(this double multiplicator, List<T> units)
where T : struct, IMensuraUnit, IEquatable<T> => units.Multiply(multiplicator);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static List<T?> Multiply<T>(this double multiplicator, List<T?> units)
where T : struct, IMensuraUnit, IEquatable<T> => units.Multiply(multiplicator);
// === ICollection<T> ===
public static void Multiply<T>(this ICollection<T> units, double multiplicator, Span<T> destination)
where T : struct, IMensuraUnit, IEquatable<T>
{
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.Multiply(multiplicator, destination); return; }
if (units is List<T> list) { CollectionsMarshal.AsSpan(list).Multiply(multiplicator, destination); return; }
int i = 0;
foreach (var item in units)
destination[i++] = (item.ToDouble() * multiplicator).ToUnit<T>();
}
public static void Multiply<T>(this ICollection<T?> units, double multiplicator, Span<T?> destination)
where T : struct, IMensuraUnit, IEquatable<T>
{
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.Multiply(multiplicator, destination); return; }
if (units is List<T?> list) { CollectionsMarshal.AsSpan(list).Multiply(multiplicator, destination); return; }
int i = 0;
foreach (var item in units)
destination[i++] = item.HasValue
? (item.Value.ToDouble() * multiplicator).ToUnit<T>() : null;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Multiply<T>(this double multiplicator, ICollection<T> units, Span<T> destination)
where T : struct, IMensuraUnit, IEquatable<T> => units.Multiply(multiplicator, destination);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Multiply<T>(this double multiplicator, ICollection<T?> units, Span<T?> destination)
where T : struct, IMensuraUnit, IEquatable<T> => units.Multiply(multiplicator, destination);
// === IReadOnlyCollection<T> ===
public static void Multiply<T>(this IReadOnlyCollection<T> units, double multiplicator, Span<T> destination)
where T : struct, IMensuraUnit, IEquatable<T>
{
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.Multiply(multiplicator, destination); return; }
if (units is List<T> list) { CollectionsMarshal.AsSpan(list).Multiply(multiplicator, destination); return; }
int i = 0;
foreach (var item in units)
destination[i++] = (item.ToDouble() * multiplicator).ToUnit<T>();
}
public static void Multiply<T>(this IReadOnlyCollection<T?> units, double multiplicator, Span<T?> destination)
where T : struct, IMensuraUnit, IEquatable<T>
{
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.Multiply(multiplicator, destination); return; }
if (units is List<T?> list) { CollectionsMarshal.AsSpan(list).Multiply(multiplicator, destination); return; }
int i = 0;
foreach (var item in units)
destination[i++] = item.HasValue
? (item.Value.ToDouble() * multiplicator).ToUnit<T>() : null;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Multiply<T>(this double multiplicator, IReadOnlyCollection<T> units, Span<T> destination)
where T : struct, IMensuraUnit, IEquatable<T> => units.Multiply(multiplicator, destination);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Multiply<T>(this double multiplicator, IReadOnlyCollection<T?> units, Span<T?> destination)
where T : struct, IMensuraUnit, IEquatable<T> => units.Multiply(multiplicator, destination);
// === IEnumerable<T> + yeild ===
static IEnumerable<T> MultiplyIterator<T>(IEnumerable<T> units, double multiplicator)
where T : struct, IMensuraUnit, IEquatable<T>
{
foreach (var item in units)
yield return (item.ToDouble() * multiplicator).ToUnit<T>();
}
static IEnumerable<T?> MultiplyNullableIterator<T>(IEnumerable<T?> units, double multiplicator)
where T : struct, IMensuraUnit, IEquatable<T>
{
foreach (T? item in units)
yield return item.HasValue
? (item.Value.ToDouble() * multiplicator).ToUnit<T>() : null;
}
// === IEnumerable<T> ===
public static IEnumerable<T> Multiply<T>(this IEnumerable<T> units, double multiplicator)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (units is null) return null!;
if (units is T[] array) return array.Multiply(multiplicator);
if (units is List<T> list) return list.Multiply(multiplicator);
if (units is ICollection<T> col)
{
var arr = new T[col.Count];
col.Multiply(multiplicator, arr);
return arr;
}
if (units is IReadOnlyCollection<T> roc)
{
var arr = new T[roc.Count];
roc.Multiply(multiplicator, arr);
return arr;
}
return MultiplyIterator(units, multiplicator);
}
public static IEnumerable<T?> Multiply<T>(this IEnumerable<T?> units, double multiplicator)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (units is null) return null!;
if (units is T?[] array) return array.Multiply(multiplicator);
if (units is List<T?> list) return list.Multiply(multiplicator);
if (units is ICollection<T?> col)
{
var arr = new T?[col.Count];
col.Multiply(multiplicator, arr);
return arr;
}
if (units is IReadOnlyCollection<T?> roc)
{
var arr = new T?[roc.Count];
roc.Multiply(multiplicator, arr);
return arr;
}
return MultiplyNullableIterator(units, multiplicator);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static IEnumerable<T> Multiply<T>(this double multiplicator, IEnumerable<T> units)
where T : struct, IMensuraUnit, IEquatable<T> => Multiply(units, multiplicator);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static IEnumerable<T?> Multiply<T>(this double multiplicator, IEnumerable<T?> units)
where T : struct, IMensuraUnit, IEquatable<T> => Multiply(units, multiplicator);
}