You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 19:02:11