【C#性能】C# 语言中的数组迭代

这篇具有很好参考价值的文章主要介绍了【C#性能】C# 语言中的数组迭代。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、说明

        可迭代性,是数组等操作的根本;在C++程序开发过程中,可迭代操作是非常普遍、非常广泛的,然而,对这种操作知道多少,又不知道多少,都将影响开发灵活性、开发的进度。因此,本文干脆系统地全部列举这种应用,以便在使用时查阅。

二、从简单示例入手

        实现数组中项的总和非常简单。我认为大多数开发人员会以这种方式实现它:

static int Sum(int[] array)
{
    var sum = 0;
    for (var index = 0; index < array.Length; index++)
        sum += array[index];
    return sum;
}

        C# 中实际上有一个更简单的替代方法:

static int Sum(int[] array)
{
    var sum = 0;
    foreach (var item in array)
        sum += item;
    return sum;
}

        另一种替代方法是使用 LINQ 提供的操作。它可以应用于任何可枚举项,包括数组。Sum()

        那么,这三者在性能方面如何公平呢?

【C#性能】C# 语言中的数组迭代,C#栏目,solr,linux,lucene

        该基准测试比较了 .NET 10、1 和 000 上大小为 6 和 7.8 的阵列的性能。int

        您可以看到,使用循环 比使用循环快 30% 左右 foreachfor

        在最新的 .NET 版本中,LINQ 实现有了很大的改进。它在 .NET 6 中要慢得多,但在 .NET 7 中要慢得多,对于 .NET 8 中的大型数组来说要快得多。

三、foreachfor

        怎么能比循环更快?foreachfor和循环都是循环的语法糖。当在数组上使用这些代码时,编译器实际上会生成非常相似的代码。forforeachwhile

        你可以在SharpLab中看到以下代码:

var array = new[] {0, 1, 2, 3, 4, 5 };

Console.WriteLine(Sum_For());
Console.WriteLine(Sum_ForEach());

int Sum_For()
{
    var sum = 0;
    for (var index = 0; index < array.Length; index++)
        sum += array[index];
    return sum;                     
}

int Sum_ForEach()
{
    var sum = 0;
    foreach (var item in array)
        sum += item;
    return sum;                     
}

        编译器生成以下内容:

[CompilerGenerated]
private static int <<Main>$>g__Sum_For|0_0(ref <>c__DisplayClass0_0 P_0)
{
    int num = 0;
    int num2 = 0;
    while (num2 < P_0.array.Length)
    {
        num += P_0.array[num2];
        num2++;
    }
    return num;
}

[CompilerGenerated]
private static int <<Main>$>g__Sum_ForEach|0_1(ref <>c__DisplayClass0_0 P_0)
{
    int num = 0;
    int[] array = P_0.array; // copy array reference
    int num2 = 0;
    while (num2 < array.Length)
    {
        int num3 = array[num2];
        num += num3;
        num2++;
    }
    return num;
}

        代码非常相似,但请注意,添加了对数组的引用作为局部变量。这允许 JIT 编译器删除边界检查,从而使迭代速度更快。检查生成的程序集的差异:foreach

Program.<<Main>$>g__Sum_For|0_0(<>c__DisplayClass0_0 ByRef)
    L0000: sub rsp, 0x28
    L0004: xor eax, eax
    L0006: xor edx, edx
    L0008: mov rcx, [rcx]
    L000b: cmp dword ptr [rcx+8], 0
    L000f: jle short L0038
    L0011: nop [rax]
    L0018: nop [rax+rax]
    L0020: mov r8, rcx
    L0023: cmp edx, [r8+8]
    L0027: jae short L003d
    L0029: mov r9d, edx
    L002c: add eax, [r8+r9*4+0x10]
    L0031: inc edx
    L0033: cmp [rcx+8], edx
    L0036: jg short L0020
    L0038: add rsp, 0x28
    L003c: ret
    L003d: call 0x000002e975d100fc
    L0042: int3

Program.<<Main>$>g__Sum_ForEach|0_1(<>c__DisplayClass0_0 ByRef)
    L0000: xor eax, eax
    L0002: mov rdx, [rcx]
    L0005: xor ecx, ecx
    L0007: mov r8d, [rdx+8]
    L000b: test r8d, r8d
    L000e: jle short L001f
    L0010: mov r9d, ecx
    L0013: add eax, [rdx+r9*4+0x10]
    L0018: inc ecx
    L001a: cmp r8d, ecx
    L001d: jg short L0010
    L001f: ret

        这导致基准测试中的性能得到改善。

        请注意,在 SharpLab 中,数组已经是一个局部变量,不会生成副本。在这种情况下,性能是等效的。

四、对数组进行切片

        有时我们可能只想迭代数组的一部分。再一次,我认为大多数开发人员会实现以下内容:

static int Sum(int[] source, int start, int length)
{
    var sum = 0;
    for (var index = start; index < start + length; index++)
        sum += source[index];
    return sum;        
}

        这可以通过使用 Span.Slice() 方法轻松转换为 foreach

static int Sum(int[] source, int start, int length)  
    => Sum(source.AsSpan().Slice(start, length));

static int Sum(ReadOnlySpan<int> source)
{
    var sum = 0;
    foreach (var item in source)
        sum += item;
    return sum;
}

        那么,这些展会在表现方面如何呢?

【C#性能】C# 语言中的数组迭代,C#栏目,solr,linux,lucene

        在数组的一部分上使用也比使用循环好 20% 左右。foreachfor

五、LINQ

        检查 in 的源代码,对于 .NET 8 之前的 .NET 版本,您会发现它使用循环 。那么,如果使用 a 比 a 快,为什么在这种情况下它这么慢?Sum()System.Linqforeachforeachfor

        此实现是 类型的扩展方法。与 和 操作不同,当源在数组中时没有特殊情况。编译器将此实现转换为如下所示的内容:Sum()IEnumerable<int>Count()Where()Sum()

static int Sum(this IEnumerable<int> source)
{
    var sum = 0;
    IEnumerator<int> enumerator = source.GetEnumerator();
    try
    {
        while(enumerator.MoveNext())
            sum += enumerator.Current;
    }
    finally
    {
        enumerator?.Dispose()
    }
    return sum;
}

        此代码存在多个性能问题:

  • GetEnumerator()返回。这意味着枚举器是引用类型,这意味着它必须在堆上分配,从而增加垃圾回收器的压力。IEnumerator<T>
  • IEnumerator<T>来自。然后,它需要 来释放枚举器,因此无法内联此方法。IDisposabletry/finally
  • 对 的迭代需要调用方法和属性。由于枚举器是引用类型,因此这些调用是虚拟的。IEnumerable<T>MoveNext()Current

        所有这些都使数组的枚举速度慢得多。

注意:请查看我的另一篇文章“值类型枚举器的性能与引用类型枚举器的性能”,以了解这两种类型的枚举器之间的性能差异。

.NET 8 的性能要好得多,因为它会在 源是数组或 .如果是 or 的数组或列表,它会通过使用 SIMD 进行更多优化,从而允许同时对多个项目求和。Sum()List<T>intlong

注意:请查看我的另一篇文章“.NET 中的单指令多数据 (SIMD)”,了解 SIMD 的工作原理以及如何在代码中使用。

六、结论

        数组的迭代是编译器可以执行代码优化的特例。的使用保证了这些优化的最佳条件。foreach

        将数组转换为 会使其迭代速度慢得多。IEnumerable<T>

        并非所有 LINQ 方法都针对数组的情况进行了优化。在 .NET 8 之前,最好使用 Sum() 方法的自定义实现。文章来源地址https://www.toymoban.com/news/detail-606428.html

到了这里,关于【C#性能】C# 语言中的数组迭代的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Python中的迭代器与生成器提高性能的秘密武器【第143篇—迭代器与生成器】

    前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。【点击进入巨牛的人工智能学习网站】。 在Python编程中,迭代器和生成器是提高性能和减少内存消耗的重要工具。它们不仅简化了代码结构,而且在处理大型数据集时具有明显的优势

    2024年03月24日
    浏览(42)
  • 【搜索引擎Solr】Solr:提高批量索引的性能

    几个月前,我致力于提高“完整”索引器的性能。我觉得这种改进足以分享这个故事。完整索引器是 Box 从头开始创建搜索索引的过程,从 hbase 表中读取我们所有的文档并将文档插入到 Solr 索引中。 我们根据 id 对索引文档进行分片,同样的文档 id 也被用作 hbase 表中的 key。

    2024年02月15日
    浏览(35)
  • 【搜索引擎Solr】配置 Solr 以获得最佳性能

    Apache Solr 是广泛使用的搜索引擎。有几个著名的平台使用 Solr;Netflix 和 Instagram 是其中的一些名称。我们在 tajawal 的应用程序中一直使用 Solr 和 ElasticSearch。在这篇文章中,我将为您提供一些关于如何编写优化的 Schema 文件的技巧。我们不会讨论 Solr 的基础知识,我希望您了解

    2024年02月16日
    浏览(29)
  • C# 遍历数组中的对象委托方法

    1,声明一个委托,理解这个可以装没有返回值和参数方法。 2,声明一个类 Cs2 3,声明一个类 Cs,里面声明的是静态,不用new,方便其他地方调用方法。 4,给Cs类中ListResponseFilterWords22的添加数据,里面委托的方法,可以根据自己自由自定义代码。 5,遍历集合中的对象,调

    2024年02月14日
    浏览(33)
  • 【搜索引擎】提高Apache Solr 性能

    这是一个关于我们如何设法克服搜索和相关性堆栈的稳定性和性能问题的简短故事。 在过去的 10 个月里,我很高兴与个性化和相关性团队合作。我们负责根据排名和机器学习向用户提供“个性化和相关的内容”。我们通过一组提供三个公共端点的微服务来做到这一点,即

    2024年02月17日
    浏览(31)
  • 高性能的全文检索库Apache Lucene 介绍

            Apache Lucene 是一个高性能的全文检索库,由 Apache Software Foundation 维护。Lucene 提供了丰富的 API,用于实现快速、准确的全文搜索。本文将详细介绍 Apache Lucene 的技术特点、应用场景和优势。         Apache Lucene 的历史和发展         Apache Lucene 起源于 1999 年

    2024年03月27日
    浏览(38)
  • 【Rust】Rust学习 第十三章Rust 中的函数式语言功能:迭代器与闭包

    Rust 的设计灵感来源于很多现存的语言和技术。其中一个显著的影响就是  函数式编程 ( functional programming )。函数式编程风格通常包含将函数作为参数值或其他函数的返回值、将函数赋值给变量以供之后执行等等。 更具体的,我们将要涉及: 闭包 ( Closures ),一个可以储

    2024年02月12日
    浏览(39)
  • 选择结构还是类?C#中的最佳实践与性能优化指南

      概述: 在C#中,选择使用结构(struct)而非类(class)取决于数据大小、不可变性和性能需求。结构适用于小型、不可变的数据对象,具有轻量级和高性能的优势。然而,对于复杂对象和需要继承的情况,应选择类。以下是一个简单的结构示例,演示了结构在栈上分配内存

    2024年04月09日
    浏览(55)
  • C#,全文检索的原理与实现代码,No-Lucene

     文档管理系列技术文章 文档管理系统的核心技术与难点https://blog.csdn.net/beijinghorn/article/details/122426112PB级全文检索(分布式)解决方案——HyperSearchhttps://blog.csdn.net/beijinghorn/article/details/122377760  全文检索是文档管理系统的核心功能。  实现全文检索的途径其实很多,包括但

    2024年02月03日
    浏览(34)
  • C语言中的数组(详解)

    🎈个人主页:库库的里昂 🎐CSDN新晋作者 🎉欢迎 👍点赞✍评论⭐收藏 ✨系列专栏C语言初阶、代码小游戏 🤝希望作者的文章能对你有所帮助,有不足的地方请在评论区留言指正,大家一起学习交流!🤗 【前言】 数组 可以说是目前为止讲到的第一个 真正意义上存储数据

    2024年02月15日
    浏览(26)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包