跨度和二维数组
Span and two dimensional Arrays
是否可以将新的 System.Memory Span struct 与二维数据数组一起使用?
double[,] testMulti =
{
{ 1, 2, 3, 4 },
{ 5, 6, 7, 8 },
{ 9, 9.5f, 10, 11 },
{ 12, 13, 14.3f, 15 }
};
double[] testArray = { 1, 2, 3, 4 };
string testString = "Hellow world";
testMulti.AsSpan(); // Compile error
testArray.AsSpan();
testString.AsSpan();
虽然 testArray 和 testString 有 AsSpan 扩展,但 testMulti 没有这样的扩展。
Span 的设计是否仅限于处理一维数据数组?
我还没有找到使用 Span 处理 testMulti 数组的明显方法。
您可以使用非托管内存创建 Span
。这将允许您不加选择地切片和切块。
unsafe
{
Span<T> something = new Span<T>(pointerToarray, someLength);
}
完整演示
unsafe public static void Main(string[] args)
{
double[,] doubles = {
{ 1, 2, 3, 4 },
{ 5, 6, 7, 8 },
{ 9, 9.5f, 10, 11 },
{ 12, 13, 14.3f, 15 }
};
var length = doubles.GetLength(0) * doubles.GetLength(1);
fixed (double* p = doubles)
{
var span = new Span<double>(p, length);
var slice = span.Slice(6, 5);
foreach (var item in slice)
Console.WriteLine(item);
}
}
输出
7
8
9
9.5
10
其他选项是重新分配到一维数组,接受惩罚并且不要传递-Go
BlockCopy
- 或p/invoke直接
memcpy
并使用unsafe
和指针
Cast<T>
例如 multiDimensionalArrayData.Cast<byte>().ToArray()
前 2 个对于大型数组来说性能更高。
所有跨度都是一维的,因为内存是一维的。
您当然可以将各种结构映射到一维内存,但 Span class 不会为您做这件事。但是你可以很容易地自己写一些东西,例如:
public class Span2D<T> where T : struct
{
protected readonly Span<T> _span;
protected readonly int _width;
protected readonly int _height;
public Span2D(int height, int width)
{
T[] array = new T[_height * _width];
_span = array.AsSpan();
}
public T this[int row, int column]
{
get
{
return _span[row * _height + column];
}
set
{
_span[row * _height + column] = value;
}
}
}
棘手的部分是实现 Slice()
,因为二维结构的语义有点模糊。您可能只能按其中一个维度对这种结构进行切片,因为按另一个维度对其进行切片会导致内存不连续。
作为@saruman,我认为这是不可能的。
例如,您需要首先使用 Fast way to convert a two dimensional array to a List ( one dimensional ) or Convert 2 dimensional array 中所示的技术获取新的一维数组。
也许使用锯齿状数组而不是多维数组会更成功。
double[][] testMulti =
{
new double[] { 1, 2, 3, 4 },
new double[] { 5, 6, 7, 8 },
new double[] { 9, 9.5f, 10, 11 },
new double[] { 12, 13, 14.3f, 15 }
};
Span<double[]> span = testMulti.AsSpan(2, 1);
Span<double> slice = span[0].AsSpan(1, 2);
foreach (double d in slice)
Console.WriteLine(d);
slice[0] = 10.5f;
Console.Write(string.Join(", ", testMulti[2]));
Console.ReadLine();
输出
9.5
10
9, 10.5, 10, 11
正如 John Wu 已经提到的,跨度是一维的。
您当然可以自己实现 2D 跨度,但 Microsoft 已经为我们做到了。
查看文档 here。
您可以找到寻址 nuget 包 here.
该包还提供了一个Memory2D。
var arr = new int[,] { {1,2,3},{2,2,3},{3,2,3} };
var spn = arr.AsSapn2D();
// Now use it similar to a normal span
// The access is of course a bit different since we are using a 2D data structure.
Console.WriteLine(spn[0..2,..]);
是否可以将新的 System.Memory Span struct 与二维数据数组一起使用?
double[,] testMulti =
{
{ 1, 2, 3, 4 },
{ 5, 6, 7, 8 },
{ 9, 9.5f, 10, 11 },
{ 12, 13, 14.3f, 15 }
};
double[] testArray = { 1, 2, 3, 4 };
string testString = "Hellow world";
testMulti.AsSpan(); // Compile error
testArray.AsSpan();
testString.AsSpan();
虽然 testArray 和 testString 有 AsSpan 扩展,但 testMulti 没有这样的扩展。
Span 的设计是否仅限于处理一维数据数组?
我还没有找到使用 Span 处理 testMulti 数组的明显方法。
您可以使用非托管内存创建 Span
。这将允许您不加选择地切片和切块。
unsafe
{
Span<T> something = new Span<T>(pointerToarray, someLength);
}
完整演示
unsafe public static void Main(string[] args)
{
double[,] doubles = {
{ 1, 2, 3, 4 },
{ 5, 6, 7, 8 },
{ 9, 9.5f, 10, 11 },
{ 12, 13, 14.3f, 15 }
};
var length = doubles.GetLength(0) * doubles.GetLength(1);
fixed (double* p = doubles)
{
var span = new Span<double>(p, length);
var slice = span.Slice(6, 5);
foreach (var item in slice)
Console.WriteLine(item);
}
}
输出
7
8
9
9.5
10
其他选项是重新分配到一维数组,接受惩罚并且不要传递-Go
BlockCopy
- 或p/invoke直接
memcpy
并使用unsafe
和指针 Cast<T>
例如multiDimensionalArrayData.Cast<byte>().ToArray()
前 2 个对于大型数组来说性能更高。
所有跨度都是一维的,因为内存是一维的。
您当然可以将各种结构映射到一维内存,但 Span class 不会为您做这件事。但是你可以很容易地自己写一些东西,例如:
public class Span2D<T> where T : struct
{
protected readonly Span<T> _span;
protected readonly int _width;
protected readonly int _height;
public Span2D(int height, int width)
{
T[] array = new T[_height * _width];
_span = array.AsSpan();
}
public T this[int row, int column]
{
get
{
return _span[row * _height + column];
}
set
{
_span[row * _height + column] = value;
}
}
}
棘手的部分是实现 Slice()
,因为二维结构的语义有点模糊。您可能只能按其中一个维度对这种结构进行切片,因为按另一个维度对其进行切片会导致内存不连续。
作为@saruman,我认为这是不可能的。
例如,您需要首先使用 Fast way to convert a two dimensional array to a List ( one dimensional ) or Convert 2 dimensional array 中所示的技术获取新的一维数组。
也许使用锯齿状数组而不是多维数组会更成功。
double[][] testMulti =
{
new double[] { 1, 2, 3, 4 },
new double[] { 5, 6, 7, 8 },
new double[] { 9, 9.5f, 10, 11 },
new double[] { 12, 13, 14.3f, 15 }
};
Span<double[]> span = testMulti.AsSpan(2, 1);
Span<double> slice = span[0].AsSpan(1, 2);
foreach (double d in slice)
Console.WriteLine(d);
slice[0] = 10.5f;
Console.Write(string.Join(", ", testMulti[2]));
Console.ReadLine();
输出
9.5
10
9, 10.5, 10, 11
正如 John Wu 已经提到的,跨度是一维的。 您当然可以自己实现 2D 跨度,但 Microsoft 已经为我们做到了。
查看文档 here。
您可以找到寻址 nuget 包 here.
该包还提供了一个Memory2D。
var arr = new int[,] { {1,2,3},{2,2,3},{3,2,3} };
var spn = arr.AsSapn2D();
// Now use it similar to a normal span
// The access is of course a bit different since we are using a 2D data structure.
Console.WriteLine(spn[0..2,..]);