在 C# 中通过引用将结构体值传递给方法是可接受的优化吗?

发布于 2024-09-03 13:37:19 字数 387 浏览 7 评论 0原文

假设我有一个结构:

struct MyStruct
{
    public int X
    public int Y
}

以及某个类中的一个方法,该方法在其他地方多次迭代:

public bool MyMethod( MyStruct myStruct )
{
    return ...
}

将 MyMethod 签名更改为以下是可接受的优化吗?

public bool MyMethod( ref MyStruct myStruct )

如果是这样,它真的有多大的优势?如果不是,那么使用这种方式,一个结构体需要多少个字段才能获得足够大的优势?

Say I have a struct:

struct MyStruct
{
    public int X
    public int Y
}

And a method in some class that is iterated over many times elsewhere:

public bool MyMethod( MyStruct myStruct )
{
    return ...
}

Is changing the MyMethod signature to the following an acceptable optimization?

public bool MyMethod( ref MyStruct myStruct )

If so, how much of an advantage would it really be? If not, about how many fields would a struct need for a big enough advantage using ref this way?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

请恋爱 2024-09-10 13:37:19

在 32 位系统上,您需要进行更改以推送 8 个字节的数据而不是 4 个字节。 64 位系统上推送的数据量没有变化。您还向编译器/JITter 添加了一个要求,即该结构必须存在于内存中,因为您将获取它的地址,这可能会否定其他优化。

我怀疑这是否会显示出您的程序的任何性能提升。首先进行分析,看看 MyMethod 是否是您程序中的瓶颈。

然后,假设 MyMethod 中没有其他优化机会,进行更改以通过 ref 传递,并再次进行分析以查看是否有任何改进。

You'd be making a change to push 8 bytes of data instead of 4, on a 32-bit system. No change in amount of data pushed on a 64-bit system. You are also adding a requirement to the compiler/JITter that the struct must exist in memory since you are going to be taking the address of it, which may negate other optimizations.

I doubt this will show any performance increase in your program. Profile first and see if MyMethod is even a bottleneck in your program.

Then, presuming there are no other optimization opportunities in MyMethod, make the change to pass by ref, and profile again to see if there is any improvement.

时常饿 2024-09-10 13:37:19

既然您明确询问这是否“可接受”……

我会回答。通过 ref 传递参数,你就对编译器和程序员撒谎了; .NET 中的 ref(仅)意味着您打算修改方法内的参数。

当然,您可以提供额外的注释,向程序员(但不是编译器……)解释“谎言”。但为什么首先要滥用语义呢?

如果您确实需要这种极端的微观优化(并查看其他答案 - 出于多种原因,任何性能优势都是值得怀疑的!).NET 可能是错误的环境。用C++实现相关部分。

Since you explicitly asked whether it was “acceptable” …

I’d answer no. By passing the argument by ref, you’re lying to the compiler and programmer; ref in .NET (exclusively) means that you intend to modify the argument inside the method.

Of course, you could provide an additional comment explaining the “lie” to the programmer (but not to the compiler … ). But why abuse the semantics in the first place?

If you really need such extreme micro-optimizations (and see the other answers – any performance advantage is questionable for any number of reasons!) .NET may just be the wrong environment. Implement the relevant part in C++.

戈亓 2024-09-10 13:37:19

鉴于你的例子,不太可能。在 64 位处理器上,该结构可以轻松地放入寄存器中,而不会通过堆栈传递。尽管不是在 32 位处理器和实例方法上。当您通过引用传递时,您需要为访问结构成员付费,因此需要额外的指针取消引用。这可能比避免复制的成本更高,ymmv。

当结构超过 16 字节时,通常会开始回报。当结构变得更大时,通常指导切换到类的原因之一。鉴于这完全取决于使用情况,您必须分析代码并读取程序集(当然是发布版本)或使用探查器。探查器不太可能显示出差异。通常很难测量纳秒,除非你人为地测量十亿次。

Not likely, given your example. On a 64-bit processor, the structure will fit comfortably in registers, it won't be passed through the stack. Albeit not on a 32-bit processor and an instance method. When you pass by reference, you're paying for accessing the structure members, an extra pointer dereference is required. This can get to be more expensive then avoiding the copy, ymmv.

The payoff typically starts when the structure is more than 16 bytes. One reason for the common guidance to switch to a class when the structure gets larger than that. Given that this completely depends on usage, you'll either have to analyze your code and read the assembly (release build of course) or use a profiler. The profiler isn't that likely to show a difference. It is usually pretty hard to measure a nanosecond unless you artificially do it a billion times.

野生奥特曼 2024-09-10 13:37:19

使用两个 int 字段,优势将非常小,不太可能产生影响。这可能根本没有任何区别。

一旦开始添加两个以上的 Int 字段,这种优势就会开始上升。不过,除非您经常调用该方法,否则这还不够重要。

With two int fields, the advantage will be extremely small, very unlikely to make a difference. It might not make any difference at all.

Once you start adding more than two Int fields, that advantage starts to go up. Still, not enough to matter unless you're calling that method a lot.

极致的悲 2024-09-10 13:37:19

一般来说,我会说不:坚持使用具有属性的类。然而,在某些情况下,可能需要这样的东西 - 特别是在紧凑框架(例如:XNA)或微框架上。在这两种情况下,GC非常不同,并且对象分配(和非确定性收集)可能会对应用程序产生重大影响。

在这种情况下,看到更多结构发挥作用并非闻所未闻,并且 ref 避免了常见的“丢失更新”问题(在大多数 .NET 中,结构应该是不可变;同样,XNA确实有一些场景很容易打破这个规则)。当然,另一种方法是接受并返回数据 - 那么类型可以是不可变的。

如果你指的是传递物体的表现;那么您必须分析您想要使用的确切设置。 x86 与 x64 的区别很明显,但还有更多。例如,使用 ref 要求值位于字段或局部变量中 - 但结构上的许多 JIT 性能调整都在堆栈的头部工作 - 这意味着您可能有更多“ldloc”/“stloc”比您严格需要的要多。您还引入了额外的取消引用。

In general I would say no: stick with a class with properties. However, there are circumstances that might call for something like this - in particular on compact-framework (think: XNA) or micro-framework. In both cases the GC is very different, and object allocations (and non-deterministic collection) may have a significant impact on the application.

In such cases it is not unheard of to see more structs come into play, and the ref avoids the common "lost update" problems (in most .NET, structs should be immutable; again, XNA does have some scenarios that make it tempting to break this rule). Another approach of course is to accept and return the data - then the type can be immutable.

If you mean the performance of passing the object; then you have to profile on the exact setup you intend using. x86 vs x64 is the obvious difference, but there are more. For example, using ref demands that a value is in either a field or a local variable - but many of the JIT performance tweaks on structs work on the head of the stack - meaning you might have a lot more "ldloc" / "stloc" than you strictly need. You also introduce an extra dereference.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文