首页 > 解决方案 > 条件与内存分配的 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声明。

更新:我的意思是,在成功的情况下。错误场景很少发生并且可以很慢。

标签: c#performancememory-management.net-coreconditional

解决方案


首先,您已经以两种方式编写了代码。如果您想知道哪种方式具有更好的性能,请运行这两个程序并衡量哪个具有更好的性能。这是准确回答性能问题的唯一方法,所以拿出你的秒表。

如果您无法衡量哪个性能更好,那么显然哪个性能更好并不重要,因为无法检测到差异。无法观察到的差异是无关紧要的。

现在让我们考虑您的具体问题。假设您确实决定在常见的成功案例中保存内存分配。 正确的解决方案是不要给 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。


推荐阅读