【问题标题】:Performance of pass by value vs. pass by reference in C# .NETC# .NET 中按值传递与按引用传递的性能
【发布时间】:2011-03-24 16:16:00
【问题描述】:

我创建了一个轻量级的类,它的构造函数接受大约 10 个参数。该类不改变参数值;它只是通过构造函数在本地存储值。

有些参数是引用类型(字符串、类),有些是值类型(int、bool、枚举)。

我的问题是我是否应该通过引用传递所有参数(类除外),即使用关键字ref?

我在这里考虑的是性能。

【问题讨论】:

  • 我看不出它有什么帮助。即使ref 节省了复制结构的时间,您也不能将refs 保留为类成员,ref 仅在方法内有用。

标签: c#


【解决方案1】:

我不确定 c#,但是对于 c++/c,这取决于您传递的内容。如果您传递的是基本类型(int、float、double、char)......那么按值传递比按引用传递更快(因为函数调用为此进行了优化。 如果你传递更大的东西,一个大类,一个数组,长字符串......然后通过引用传递会快得多,因为如果你正在做一个 int[100000] 那么处理器将不得不分配一个 100000 x 32/ 64(取决于架构)块,然后将所有值复制过来,这需要很多时间。而通过引用只是传递一个指针

C# 抽象掉了大部分,所以我不知道它是做什么的,但我认为在效率方面适用于 c++/c 的东西通常可以适用于 c#。

【讨论】:

    【解决方案2】:

    我发现在大容量函数调用中,通过 ref 传递的值类型会稍微快一些。如果您有大量的函数调用并且需要速度,这可能是一个考虑因素。我愿意接受其他证据。

    public static void PassValue(decimal value)
    {
    
    }
    
    public static void PassRef(ref decimal value)
    {
    
    }            
    
    decimal passMe = 0.00010209230982047828903749827394729385792342352345m;
    
    for (int x = 0; x < 20; x++)
    {
        DateTime start = DateTime.UtcNow;
        TimeSpan taken = new TimeSpan();
    
        for (int i = 0; i < 50000000; i++)
        {
            PassValue(passMe);
        }
    
        taken = (DateTime.UtcNow - start);
        Console.WriteLine("Value : " + taken.TotalMilliseconds);
    
        start = DateTime.UtcNow;
    
        for (int i = 0; i < 50000000; i++)
        {
            PassRef(ref passMe);
        }
    
        taken = (DateTime.UtcNow - start);
        Console.WriteLine("Ref : " + taken.TotalMilliseconds);
    }
    

    结果:

    Value : 150
    Ref : 140
    Value : 150
    Ref : 143
    Value : 151
    Ref : 143
    Value : 152
    Ref : 144
    Value : 152
    Ref : 143
    Value : 154
    Ref : 144
    Value : 152
    Ref : 143
    Value : 154
    Ref : 143
    Value : 157
    Ref : 143
    Value : 153
    Ref : 144
    Value : 154
    Ref : 147
    Value : 153
    Ref : 144
    Value : 153
    Ref : 144
    Value : 153
    Ref : 146
    Value : 152
    Ref : 144
    Value : 153
    Ref : 143
    Value : 153
    Ref : 143
    Value : 153
    Ref : 144
    Value : 153
    Ref : 144
    Value : 152
    Ref : 143
    

    【讨论】:

      【解决方案3】:

      据我了解,您有一个只有字段的类和一个将参数分配给这些字段的构造函数,对吧?

      如果是这种情况,我会考虑在构造函数中使用 ref 是不好的做法。如果将参数分配给该类中的字段,则无论如何它都会按值存储。所以如果你不改变构造函数中的值,就不需要通过引用来使用它。

      【讨论】:

        【解决方案4】:

        没有。通过引用传递参数会增加开销,因此只会降低性能。

        【讨论】:

        • 我习惯了 c++,所以我可能完全错了,但你能详细说明一下吗?假设我有一些巨大的对象,不会:通过引用传递只传递数据的地址,而不必复制完整的对象?
        • @SvenvandenBoogaart 你没有应付它。您正在方法内创建一个新指针来保存对象值。但是更改指针值不会修改用于将此值传递给方法调用的外部指针。
        • 结构是按值传递的,因此使用 ref 会提高性能。
        • @OrestisP.:仅当结构的大小明显大于参考的大小时,但如果结构那么大,那么它很可能不应该在第一名。
        • @Guffa:结构体只需要超过 32/64 位(取决于架构)即可提高性能,这对于结构体来说很常见。在问题的上下文中,假设所有参数都可以在逻辑上组合在一起,将它们包装在一个结构中并将其作为 ref 传递将提高性能(或从 C# 7 开始使用“in”)。另一个经常出现的例子是 Vector3 结构(主要用于处理模拟),它由 3 个浮点数(12 个字节)组成。
        【解决方案5】:

        没有。对于引用类型,您已经在传递引用,除非您想更改引用指向的内容,否则无需按引用传递引用,例如为其分配一个新对象。 对于值类型,你可以通过引用传递,但除非你有性能问题,否则我不会这样做。特别是如果所讨论的类型很小(4 字节或更少),则几乎没有或没有性能提升,甚至可能会受到惩罚。

        【讨论】:

        • 我的理解是,在通过引用传递值类型时总会有性能损失,因为值类型必须被装箱。据我了解(来自奇妙的 C# In Depth,同样奇妙的 @JonSkeet)将值类型作为引用参数传递永远不会导致性能提高,相反它总是会导致性能下降。
        • @BenH:“通过引用传递值类型时总会有性能损失,因为值类型必须被装箱” -- 没有。您似乎将通过引用传递与传递 a 引用混淆了。她们不一样。将 ref 与值类型一起使用不会将值装箱。它将引用(指针)传递给保存该值的变量。
        • @PeterDuniho:在 .NET 内部,通过引用传递参数会传递“byref”。我没有看到该术语在讨论中被大量使用,但我认为如果更频繁地使用它会减少混乱,因为它听起来不像“对象引用”而不是“通过引用”这个短语。
        【解决方案6】:

        仅当方法需要更改参数时才使用ref,并且这些更改需要传递给调用代码。只有在通过分析器运行它并确定瓶颈确实是 CLR 将方法参数复制到堆栈上时,才应该优化它。

        请记住,CLR 已针对使用参数调用方法进行了高度优化,因此我认为这不是问题。

        【讨论】:

          【解决方案7】:

          如果类只保存参数,也许你应该使用结构?

          也许这很有趣?

          When to use struct?

          【讨论】:

            猜你喜欢
            • 2023-03-02
            • 2011-05-08
            • 1970-01-01
            • 2017-03-04
            • 2016-10-22
            • 2012-07-03
            相关资源
            最近更新 更多