.NET遍历二维数组-先行/先列哪个更快?

2025-12-13 0 555

上周在.NET性能优化群里面有一个很有意思的讨论,讨论的问题如下所示:

.NET遍历二维数组-先行/先列哪个更快?

请教大佬:2D数组,用C#先遍历行再遍历列,或者先遍历列再遍历行,两种方式在性能上有区别吗?
据我所知,Julia或者python的 pandas,一般建议先遍历列,再遍历行

在群里面引发了很多大佬的讨论,总的来说观点分为以下三种:

  • 应该不会有什么差别
  • 先遍历列会比先遍历行更快
  • 先遍历行会比先遍历列更快

看了群里面激烈的讨论,刚好今天有时间,我们就来看看真实情况是怎么样的?实践出真知,我们编写一个Benchmark一测便知。

测试#

在下面的代码中,我们创建了一个ArrayBenchmark类,它包含了两个方法:RowFirst和ColumnFirst。这两个方法分别代表了先行后列和先列后行两种遍历方式。每次测试时,数组的大小将使用参数(Size)设置。在Main方法中,我们调用BenchmarkRunner.Run方法来运行测试。

using System;
using System.Diagnostics;
using BenchmarkDotNet.Attributes;

namespace TwoDimensionalArrayBenchmark
{
  public class ArrayBenchmark
  {
    private int[,] _array;

    [Params(1000, 2000, 4000, 8000, 16000)]
    public int Size { get; set; }

    [GlobalSetup]
    public void Setup()
    {
      _array = new int[Size, Size];
      var rnd = new Random();
      for (int i = 0; i < Size; i++)
      {
        for (int j = 0; j < Size; j++)
        {
          _array[i, j] = rnd.Next();
        }
      }
    }

    [Benchmark]
    public int RowFirst()
    {
      // 先遍历一整行
      int sum = 0;
      for (int i = 0; i < Size; i++)
      {
        for (int j = 0; j < Size; j++)
        {
          sum += _array[i, j];
        }
      }
      return sum;
    }

    [Benchmark]
    public int ColumnFirst()
    {
      // 先遍历一整列
      int sum = 0;
      for (int j = 0; j < Size; j++)
      {
        for (int i = 0; i < Size; i++)
        {
          sum += _array[i, j];
        }
      }
      return sum;
    }
  }

  class Program
  {
    static void Main(string[] args)
    {
      var summary = BenchmarkDotNet.Running.BenchmarkRunner.Run<ArrayBenchmark>();
      Console.ReadKey();
    }
  }
}

得出的结果如下所示,从结果中我们可以看到,在.NET7.0中先遍历行远远快于先遍历列,随着数据量的增大有着近10倍的差距:
.NET遍历二维数组-先行/先列哪个更快?

关于为什么先行后列的性能比先列后行高,猜测主要有以下两个原因:

  1. CPU 缓存层次结构:当遍历二维数组时,先行后列方式更适合利用 CPU 的缓存层次结构。每次访问二维数组中的一行数据时,这一整行的数据都可以从 L1/L2/L3 缓存中读取,这样就可以大大提高数据读取的效率。

  2. 内存布局:二维数组的内存布局可能是按行存储的,也就是说一整行的数据在内存中是连续的。因此,先行后列的方式更容易利用内存的连续性,使数据读取更加顺畅。

我们可以通过简单的代码来验证一下.NET中二维数组的存储格式,使用Unsafe.AsPointer可以获取引用对象的指针,然后将其强转为long类型即可获得它的地址。

下面使用的是先行后列的遍历方式:
.NET遍历二维数组-先行/先列哪个更快?

由于一个int类型占用4字节的空间,所以我们可以发现在使用先行后列的方式时刚好就是顺序顺序递增的。

也就是说C#在逻辑上虽然是二维数组,实际上存储是按每一行连续存储的,如下图所示:
.NET遍历二维数组-先行/先列哪个更快?

CPU的缓存也是按照这个顺序进行缓存的,所以当我们先行后列遍历的时候整行数据都可能在CPU缓存中,可以最大化的利用好CPU缓存。

.NET遍历二维数组-先行/先列哪个更快?

如果按照先列后行的遍历,那么对缓存就很不友好,需要多次从内存中读取数据。
.NET遍历二维数组-先行/先列哪个更快?

总结#

这就是本文的全部了,目前看来在C# .NET中遍历二维数组是先行快于先列,不过这也不是绝对的事情,因为在编译器和即时编译器中,是可以自动的去做一些优化,让程序更快的访问数据。比如在群里大佬们比较了在VC中的差异,结果是发现DEBUG模式确实行快于列,但是Release两者差别几乎可以忽略不计,当然这不在本文的讨论范围中。

.NET性能优化交流群#

相信大家在开发中经常会遇到一些性能问题,苦于没有有效的工具去发现性能瓶颈,或者是发现瓶颈以后不知道该如何优化。之前一直有读者朋友询问有没有技术交流群,但是由于各种原因一直都没创建,现在很高兴的在这里宣布,我创建了一个专门交流.NET性能优化经验的群组,主题包括但不限于:

  • 如何找到.NET性能瓶颈,如使用APM、dotnet tools等工具
  • .NET框架底层原理的实现,如垃圾回收器、JIT等等
  • 如何编写高性能的.NET代码,哪些地方存在性能陷阱

希望能有更多志同道合朋友加入,分享一些工作中遇到的.NET性能问题和宝贵的性能分析优化经验。目前一群已满,现在开放二群。
如果提示已经达到200人,可以加我微信,我拉你进群:ls1075
另外也创建了QQ群,群号: 687779078,欢迎大家加入。

作者:InCerry

作者Github:https://github.com/incerrygit

出处:https://www.cnblogs.com/InCerry/p/dotNET-Traversing-Two-Dimensional-Array-Row-First-or-Column-First-Faster.html

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。

声明:本博客版权归「InCerry」所有。

收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

申明:本文由第三方发布,内容仅代表作者观点,与本网站无关。对本文以及其中全部或者部分内容的真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。本网发布或转载文章出于传递更多信息之目的,并不意味着赞同其观点或证实其描述,也不代表本网对其真实性负责。

左子网 编程相关 .NET遍历二维数组-先行/先列哪个更快? https://www.zuozi.net/36429.html

常见问题
  • 1、自动:拍下后,点击(下载)链接即可下载;2、手动:拍下后,联系卖家发放即可或者联系官方找开发者发货。
查看详情
  • 1、源码默认交易周期:手动发货商品为1-3天,并且用户付款金额将会进入平台担保直到交易完成或者3-7天即可发放,如遇纠纷无限期延长收款金额直至纠纷解决或者退款!;
查看详情
  • 1、描述:源码描述(含标题)与实际源码不一致的(例:货不对板); 2、演示:有演示站时,与实际源码小于95%一致的(但描述中有”不保证完全一样、有变化的可能性”类似显著声明的除外); 3、发货:不发货可无理由退款; 4、安装:免费提供安装服务的源码但卖家不履行的; 5、收费:价格虚标,额外收取其他费用的(但描述中有显著声明或双方交易前有商定的除外); 6、其他:如质量方面的硬性常规问题BUG等。 注:经核实符合上述任一,均支持退款,但卖家予以积极解决问题则除外。
查看详情
  • 1、左子会对双方交易的过程及交易商品的快照进行永久存档,以确保交易的真实、有效、安全! 2、左子无法对如“永久包更新”、“永久技术支持”等类似交易之后的商家承诺做担保,请买家自行鉴别; 3、在源码同时有网站演示与图片演示,且站演与图演不一致时,默认按图演作为纠纷评判依据(特别声明或有商定除外); 4、在没有”无任何正当退款依据”的前提下,商品写有”一旦售出,概不支持退款”等类似的声明,视为无效声明; 5、在未拍下前,双方在QQ上所商定的交易内容,亦可成为纠纷评判依据(商定与描述冲突时,商定为准); 6、因聊天记录可作为纠纷评判依据,故双方联系时,只与对方在左子上所留的QQ、手机号沟通,以防对方不承认自我承诺。 7、虽然交易产生纠纷的几率很小,但一定要保留如聊天记录、手机短信等这样的重要信息,以防产生纠纷时便于左子介入快速处理。
查看详情

相关文章

猜你喜欢
发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务