我想通过Equals
比较一个C#中的大型Struct,它又是通过IEquatable<T>
接口实现的。
我的问题是它的性能非常差,因为我的结构体相当大。想象一下结构体的简化版本,如下所示:
public struct Data
{
public byte b0;
public byte b1;
public byte b2;
public byte b3;
public byte b4;
public byte b5;
public byte b6;
public byte b7;
}
字符串
我现在写一个简单的Equals:
public bool Equals(Data other)
{
return b0 == other.b0 &&
b1 == other.b1 &&
...
}
型
有没有一种方法可以使Equals方法更有效?
更新
5条答案
按热度按时间7jmck4yq1#
假设你的字节大小的结构体是unmanaged types,并且你正在使用.NET Core 2.1或更高版本,你可以使用
MemoryMarshal.Cast()
沿着MemoryMarshal.CreateReadOnlySpan()
将你的每个结构体转换为ReadOnlySpan<byte>
。这样做之后,你可以逐字节比较它们的值相等性:字符串
如果你的结构体有很多很多字段,这比手动逐字段比较要简单得多。进一步注意,根据参考源,
MemoryExtensions.SequenceEqual<T>(this ReadOnlySpan<T> span, ReadOnlySpan<T> other)
是 Unrolled and vectorized for half-constant input,所以它可能会被合理优化。演示fiddle here与
EqualityComparer<T>.Default.Equals()
相比有80-85%的加速。话虽如此,如果你的结构真的是 * 在100字节 *,那么你很可能花了大量的时间来复制它们。在其设计指南Choosing Between Class and Struct中,Microsoft指出:
如果类型的示例是小的,并且通常是短暂的,或者通常嵌入在其他对象中,那么请考虑定义结构而不是类。
避免定义结构,除非该类型具有以下所有特征:
*示例大小不超过16字节。
一个结构体的大小在100字节左右绝对不小。微软建议在这种情况下使用类。如果你必须使用这样一个大的结构体(例如,因为你正在与一些非托管代码交互),请确保尽可能通过
ref
,ref readonly
或in
传递它。hec6srdp2#
事实上,有一种方法可以提高复杂结构的性能。
以你为例:
字符串
然后你可以写
Equals
方法如下:型
DataHelper
看起来像这样:型
为什么会这样?
这里重要的是,两个结构体在内存中的大小相同。然后我们可以使用
Unsafe.As
将Data
的内存重新解释为DataHelper
,这允许我们比较,在我们的例子中,一个long
和一个long
,而不是八个字节和八个字节。这是可扩展的。例如,如果你有一个结构体,它的大小是33字节,那么你可以创建
DataHelper
作为四个long和一个byte
,并比较它们。唯一必须保证的是:
1.你必须使用一个结构体。这对类不起作用,因为类只包含它们数据的位置,而结构体包含它们的实际数据。
1.两个结构的大小必须相同。
和往常一样,衡量一下您是否真的看到了性能提升。
h9a6wy2h3#
采取了一些建议张贴在这里和我的个人采取通过
Vector64
:字符串
又组成了following benchmark:
型
它给出了“在我的机器上”:
| 方法|是说|误差|StdDev|
| --|--|--|--|
| 自动相等|144.292美制|1.1918美制|1.1149美制|
| 通过自定义等于|145.347美制|0.3022 μ s的范围|0.2523 μ s的范围|
| 通过向量|54.058美制|0.1327 μ s的范围|0.1176 μ s的电流|
| 通过数据帮助程序|5.285美制|0.0134 μ s的范围|0.0119 μ s的电流|
备注:
ValueType.Equals
应已针对以下情况进行了大量优化:型
但请注意,根据CLR实现-it can be a brittle heuristic
DataHelper
方法似乎是最快的一种(在我的硬件上),但对不同数量的 prop 的可扩展性较低。gdx19jrr4#
在这个答案中,我并不是要说明如何优化
Equals
方法,而是要说明如何避免编写它。字符串
这样,C#编译器将为您实现
Equals
方法:型
SharpLab演示版。
wvt8vs2t5#
请尝试以下操作:
字符串