C# - 变量范围和处置如何影响处理效率?

C# - how does variable scope and disposal impact processing efficiency?

前几天我和一位同事就这个假设情况进行了讨论。考虑这个伪代码:

public void Main()
{   
    MyDto dto = Repository.GetDto();

    foreach(var row in dto.Rows)
    {
        ProcessStrings(row);
    }
}

public void ProcessStrings(DataRow row)
{
    string string1 = GetStringFromDataRow(row, 1);
    string string2 = GetStringFromDataRow(row, 2);

    // do something with the strings
}

那么这个功能相同的替代方案:

public void Main()
{
    string1 = null;
    string2 = null,
    MyDto dto = Repository.GetDto();

    foreach(var row in dto.Rows)
    {
        ProcessStrings(row, string1, string2)
    }
}

public void ProcessStrings(DataRow row, string string1, string string2)
{
    string1 = GetStringFromDataRow(row, 1);
    string2 = GetStringFromDataRow(row, 2);

    // do something with the strings
}

运行 编译后的代码在处理上有何不同?我们是否认为第二个版本的效率稍微高一点是正确的,因为字符串变量将占用更少的内存并且只被处理一次,而在第一个版本中,它们在循环的每次传递中被处理?

如果第二个版本中的字符串由 ref 或作为 out 参数传递,会有什么不同吗?

在您的两个备选方案中,GetStringFromDataRow 每次都会创建新字符串。无论您是将此字符串的 reference 存储在局部变量中还是参数参数变量中(在您的情况下与局部变量本质上没有太大区别)都没有关系。想象一下,您甚至没有将 GetStringFromDataRow 的结果分配给任何变量——字符串的实例仍然被创建并存储在内存中的某个地方,直到垃圾被收集。如果您通过引用传递您的字符串 - 它不会有太大区别。您将能够重用内存位置来存储 reference 到创建的字符串(您可以将其视为字符串实例的内存地址),但不是字符串内容的内存位置。

当您处理 "marginally more efficient" 级优化时,您可能会面临看不到全貌并最终成为 "marginally less efficient" 的风险。

这里的答案也有同样的风险,但需要注意的是,让我们看看假设:

Storing a string into a variable creates a new instance of the string

不,一点也不。字符串是一个对象,您存储在变量中的是对该对象的引用。在 32 位系统上,此引用大小为 4 个字节,在 64 位系统上为 8 个字节。不多也不少。移动 4/8 字节是开销,您不会真正注意到它。

所以这两个示例都没有创建比另一个更多或更少的字符串,因此在这个例子中它们是等效的。

那么有什么不同呢?

好吧,在一个示例中,您将两个字符串引用存储到局部变量中。这很可能是 cpu 寄存器。可能是堆栈上的内存。很难说,取决于其余代码。有关系吗?极不可能。

在另一个示例中,您将两个参数作为 null 传递,然后在本地重复使用这些参数。这些参数可以作为 cpu 寄存器或堆栈内存传递。和另一个一样。这重要吗?完全没有。

所以很可能完全没有区别。

请注意一件事,您提到了 "disposal"。该术语保留用于实现 IDisposable 的对象的使用,然后通过对这些对象调用 IDisposable.Dispose 来处理这些对象的行为。字符串不是这样的对象,这与这个问题无关。

如果相反,处置是指 "garbage collection",那么由于我已经确定由于您询问的差异,这两个示例都不会创建比其他示例更多或更少的对象,所以这也是无关紧要的.

不过这并不重要。您或我或您的同事认为 会产生什么影响并不重要。知道是完全不同的,这使我...

关于优化我能给出的真实提示:

  1. 测量
  2. 测量
  3. 测量
  4. 了解
  5. 验证您是否理解正确
  6. 如果可能,改变

你测量,使用分析器找到你代码中真正的瓶颈和实时消耗者,然后理解为什么这些是瓶颈,然后确保你的理解是正确的,然后你可以看看你是否可以改变它。

在您的代码中,我会大胆猜测,如果您要分析您的程序,您会发现这两个示例对 运行 时间绝对没有任何影响。如果它们 do 有效,它将在纳秒级。最有可能的是,查看分析器结果这一行为会让您对您的程序有一个或多个 "huh, that's odd" 认识,并且您会发现瓶颈远比这里发挥作用的变量大得多。