如何根据数组索引从数组复制到 Vector256,反之亦然?

Posted

技术标签:

【中文标题】如何根据数组索引从数组复制到 Vector256,反之亦然?【英文标题】:How to copy from an array to a Vector256 and vice versa based on the array index? 【发布时间】:2021-03-12 19:14:08 【问题描述】:

假设我有一个int[] 数组或Vector256<int>s。如何使用数组索引将值从一个复制到另一个?

目前我必须遍历数组索引并一一复制值:

int[] input = ...; // length divisible by Vector256<int>.Count
int[] output = new int[intput.Length];

for (int i = 0; i < input.Length; i += Vector256<int>.Count)

    Vector256<int> v = Vector256.Create(
                array[i], array[i + 1], array[i + 2], array[i + 3],
                array[i + 4], array[i + 5], array[i + 6], array[i + 7]);

    Vector256<int> v2 = DoSomeWork(v);

    for (int j = 0; j < Vector256<int>.Count; ++j)
    
        output[i + j] = v2.GetElement(i + j);
    

在 Java SDK 16 中,有些函数可以完全满足我的需求。 C#中是否有类似的功能?

int[] input = ...;
int[] output = new int[values.length];

for (int i = 0; i < input.length; i += IntVector.SPECIES_256.length()) 
    IntVector v = IntVector.fromArray(IntVector.SPECIES_256, input, i);
    IntVector v2 = DoSomeWork(v);
    v2.intoArray(output, i);

【问题讨论】:

【参考方案1】:

您可以使用System.Numerics 中的Vector。像这样的:

var vector = new Vector<int>(new Span<int>(ints, i, 8));
Vector256<int> v = vector.AsVector256();
......
v2.AsVector().CopyTo(output, i);

您也可以尝试直接使用System.Numerics.Vector 进行计算。

还可以将unsafeAvx.LoadVector256Avx.StoreSystem.Runtime.Intrinsics.X86 一起使用。像这样的:

fixed (int* ptr = input) 
fixed (int* ptrRes = output) 

    var vectorCount = Vector256<int>.Count;
    for (int i = 0; i <= input.Length - vectorCount; i += vectorCount) 
    
        var v = Avx.LoadVector256(ptr + i);
        ....
        Avx.Store(ptrRes + i, v2);
    

【讨论】:

我正在调用Avx2内在函数,因此我认为我不能直接使用Vector,我需要先转换为Vector256 我可以知道您创建变量vectorCount 来保存总元素而不是直接使用Vector256&lt;int&gt;.Count 的原因吗? @b1105029 我有一个小小的迷信,认为它可以带来非常非常小的速度提升。有时也更容易重构它。但你应该检查自己。 @b1105029 Vector256&lt;int&gt;.Count 有一个常数值8。没有理由将其存储到变量中。至于我const vectorCount = 8 看起来更好,只是为了代码可读性。 Roslyn 将在构建应用程序时将其替换为文字。【参考方案2】:

另一种解决方案。

int[] input = ...;
int[] output = new int[input.Length];
Span<Vector256<int>> inputVectors = MemoryMarshal.Cast<int, Vector256<int>>(input);
Span<Vector256<int>> outputVectors = MemoryMarshal.Cast<int, Vector256<int>>(output);
for (int i = 0; i < inputVectors.Length; i++)
    outputVectors[i] = DoSomeWork(inputVectors[i]); 

结果将自动保存在output 数组中。

不安全的版本

int[] input = ...;
int[] output = new int[input.Length];
fixed (int* inPtr = input, outPtr = output)

    Vector256<int>* src = (Vector256<int>*)inPtr;
    Vector256<int>* dst = (Vector256<int>*)outPtr;
    Vector256<int>* srcEnd = src + (input.Length >> 3);
    while (src < srcEnd)
    
        *dst = DoSomeWork(*src);
        src++;
        dst++;
    

所有 3 种解决方案:从公认的答案来看是不安全的,并且上述两种解决方案的性能几乎相同。

【讨论】:

以上是关于如何根据数组索引从数组复制到 Vector256,反之亦然?的主要内容,如果未能解决你的问题,请参考以下文章

如何在不循环的情况下将数组的内容复制到 C++ 中的 std::vector?

在C中将奇数索引元素从一个数组复制到另一个数组

如何创建数组的副本? [复制]

如何从javascript中的数组中删除最后一个索引元素? [复制]

如何从 UITableView 获取行索引,其中数组列表由追加到类

如何在 C# 中将数组的一部分复制到另一个数组?