C#中快速获取一维封装二维数组行与列的最优方法
优化Wrapper类中行/列的获取性能(unsafe代码实现)
我们先明确场景:现有一个Wrapper类将二维float[][]封装为一维float[]存储,当前通过普通for循环实现行/列的提取,但性能还有优化空间,尤其是可以借助unsafe代码减少索引计算开销或利用连续内存的批量拷贝。
基础前提
要使用unsafe代码,需先在项目属性中开启允许不安全代码(右键项目→属性→生成→勾选"允许不安全代码")。
优化后的实现
1. 行提取的最优实现(连续内存可直接批量拷贝)
一维数组中每行的元素是连续存储的,无需unsafe就能达到极致性能——直接用Buffer.BlockCopy(CLR底层优化的内存拷贝,比手动循环快得多)。如果一定要用unsafe,也可以通过指针实现批量拷贝,性能差异不大。
2. 列提取的unsafe优化(减少索引计算开销)
列元素在一维数组中是间隔CountX个元素存储的,无法批量拷贝,但用unsafe指针可以省去每次循环中的索引算术运算,直接通过指针偏移访问元素,提升性能。
完整代码如下:
class Wrapper { public int CountX { get; } public int CountY { get; } public float[] Values { get; } public Wrapper(float[] values, int countX, int countY) { Values = values ?? throw new ArgumentNullException(nameof(values)); if (values.Length != countX * countY) throw new ArgumentException("数组长度与行列数不匹配"); CountX = countX; CountY = countY; } // 最优行提取:用Buffer.BlockCopy,无需unsafe public float[] GetRow(int row) { ValidateRowIndex(row); var result = new float[CountX]; // 每个float占4字节,计算字节偏移量 Buffer.BlockCopy(Values, row * CountX * sizeof(float), result, 0, CountX * sizeof(float)); return result; } // unsafe版行提取(性能与BlockCopy接近,可按需选择) public unsafe float[] GetRowUnsafe(int row) { ValidateRowIndex(row); var result = new float[CountX]; fixed (float* pSrc = Values, pDest = result) { // 直接拷贝连续内存块 Buffer.MemoryCopy(pSrc + row * CountX, pDest, result.Length * sizeof(float), CountX * sizeof(float)); } return result; } // unsafe版列提取:减少索引计算开销 public unsafe float[] GetColumn(int column) { ValidateColumnIndex(column); var result = new float[CountY]; fixed (float* pSrc = Values, pDest = result) { float* srcPtr = pSrc + column; float* destPtr = pDest; for (int i = 0; i < CountY; i++) { *destPtr++ = *srcPtr; srcPtr += CountX; // 直接偏移一行的元素数 } } return result; } private void ValidateRowIndex(int row) { if (row < 0 || row >= CountY) throw new ArgumentOutOfRangeException(nameof(row), "行索引超出范围"); } private void ValidateColumnIndex(int column) { if (column < 0 || column >= CountX) throw new ArgumentOutOfRangeException(nameof(column), "列索引超出范围"); } }
使用示例
var wr = new Wrapper(new float[] {1,2,3,4,5,6,7,8}, 4, 2); // 获取第1行(索引从0开始):[5,6,7,8] var row = wr.GetRow(1); // 获取第3列(索引从0开始):[4,8] var column = wr.GetColumn(3);
性能对比说明
- GetRow:
Buffer.BlockCopy相比原for循环,性能提升非常明显,尤其是当CountX较大时——因为它直接操作内存块,避免了循环中的逐个元素赋值和重复边界检查(CLR内部优化)。 - GetColumn:unsafe指针版本相比原for循环,省去了每次循环中
column + CountX*i的算术运算,直接通过指针偏移访问,在大CountY场景下能看到明显的性能提升。
内容的提问来源于stack exchange,提问作者sausage_mouse
相关产品推荐
相关产品推荐

