337 lines
15 KiB
C#
337 lines
15 KiB
C#
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);
|
||
} |