c# - 条件与内存分配的 C# 性能
问题描述
考虑以下 C# 代码:
IntPtr native = GetNativeError(/* parameters here */);
return new ManagedError(native);
然后客户端代码以下列方式检查错误:
ManagedError err = /* get it with the code above */;
if(err.IsOk()) {
/* Success */
}
if
这里的内存分配可以以语句为代价来节省:
IntPtr native = GetNativeError(/* parameters here */);
if(native == IntPtr.Zero) {
return null;
} else {
return new ManagedError(native);
}
然后客户端代码将检查错误,如下所示:
ManagedError err = /* get it with the code above */;
if(err == null) {
/* Success */
}
我的问题是:哪一种方法更快?第一个有额外的内存分配。第二个有附加if
声明。
更新:我的意思是,在成功的情况下。错误场景很少发生并且可以很慢。
解决方案
首先,您已经以两种方式编写了代码。如果您想知道哪种方式具有更好的性能,请运行这两个程序并衡量哪个具有更好的性能。这是准确回答性能问题的唯一方法,所以拿出你的秒表。
如果您无法衡量哪个性能更好,那么显然哪个性能更好并不重要,因为无法检测到差异。无法观察到的差异是无关紧要的。
现在让我们考虑您的具体问题。假设您确实决定在常见的成功案例中保存内存分配。 正确的解决方案是不要给 null 赋予特殊含义。正确的解决方案是使用空对象模式。那就是创建一个对象的特殊实例,该实例总是在您使用 null 的地方使用。
class ManagedError
{
public static readonly Success = new ManagedError(IntPtr.Zero);
private ManagedError(IntPtr hr) { ... }
public ManagedError FromNative(IntPtr hr)
{
if (hr == IntPtr.Zero) return Success;
return new ManagedError(hr);
}
}
...
IntPtr native = GetNativeError(/* parameters here */);
return ManagedError.FromNative(native);
完毕。你总是得到一个有效的对象,在常见的情况下你不做任何分配。
另外,正如另一个答案中提到的:为什么这不是结构?你为什么要进行任何引用类型的堆内存分配?如果这个东西只是一个 intptr 的包装器,那么它应该是一个结构;它和 intptr 一样便宜!
如果你把它变成一个结构,那么这会变得更容易,因为你只需使用结构的默认实例作为你的空对象!
struct ManagedError
{
public static readonly Success = default(ManagedError);
private readonly IntPtr hr;
public ManagedError(IntPtr hr) { this.hr = hr }
}
你完成了;根本没有堆分配。您只需包装 intptr。
推荐阅读
- visual-studio-code - 如何在 vscode 中禁用文件索引
- javascript - 从 javascript 对象中删除较少的序列计数
- ruby-on-rails - 如何允许单个用户使用“acts_as_votable”gem 在单个帖子上多次投票
- c# - 如何使用 C# 在 Unity 中读取 SVG 文件样式属性
- c# - Unity Transform 特定角度之间的随机旋转
- python - Django Dockerized 与 Celery 和 Postgres
- xslt-1.0 - 撒克逊人抱怨未知功能虽然功能可用
- python - 如何控制 python 脚本的哪些部分以 root 身份运行
- vhdl - 测试台波形显示未定义的变量
- python-3.x - 使用类概念打开和关闭 GUI