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",那么由于我已经确定由于您询问的差异,这两个示例都不会创建比其他示例更多或更少的对象,所以这也是无关紧要的.
不过这并不重要。您或我或您的同事认为 会产生什么影响并不重要。知道是完全不同的,这使我...
关于优化我能给出的真实提示:
- 测量
- 测量
- 测量
- 了解
- 验证您是否理解正确
- 如果可能,改变
你测量,使用分析器找到你代码中真正的瓶颈和实时消耗者,然后理解为什么这些是瓶颈,然后确保你的理解是正确的,然后你可以看看你是否可以改变它。
在您的代码中,我会大胆猜测,如果您要分析您的程序,您会发现这两个示例对 运行 时间绝对没有任何影响。如果它们 do 有效,它将在纳秒级。最有可能的是,查看分析器结果这一行为会让您对您的程序有一个或多个 "huh, that's odd" 认识,并且您会发现瓶颈远比这里发挥作用的变量大得多。
前几天我和一位同事就这个假设情况进行了讨论。考虑这个伪代码:
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",那么由于我已经确定由于您询问的差异,这两个示例都不会创建比其他示例更多或更少的对象,所以这也是无关紧要的.
不过这并不重要。您或我或您的同事认为 会产生什么影响并不重要。知道是完全不同的,这使我...
关于优化我能给出的真实提示:
- 测量
- 测量
- 测量
- 了解
- 验证您是否理解正确
- 如果可能,改变
你测量,使用分析器找到你代码中真正的瓶颈和实时消耗者,然后理解为什么这些是瓶颈,然后确保你的理解是正确的,然后你可以看看你是否可以改变它。
在您的代码中,我会大胆猜测,如果您要分析您的程序,您会发现这两个示例对 运行 时间绝对没有任何影响。如果它们 do 有效,它将在纳秒级。最有可能的是,查看分析器结果这一行为会让您对您的程序有一个或多个 "huh, that's odd" 认识,并且您会发现瓶颈远比这里发挥作用的变量大得多。