使用 “readonly record struct” 类型作为方法的输出参数,会导致装箱吗?

huangapple go评论68阅读模式
英文:

Use "readonly record struct" type as the out parameter of method, will it cause boxing?

问题

考虑以下代码,我在一些性能关键的地方使用它来解析数据流。在这种情况下,`message` 会被装箱吗?

```cs
readonly record struct DataMessage(ulong Number, string Str, 
                                   ReadOnlySequence<byte> Payload);

bool TryParseMessage(ref ReadOnlySequence<byte> buffer, 
                     out DataMessage message) {
    message = default;
    ...
    message = new DataMessage(...);
    return true;
}

void PerformanceCriticalFunction() {
    ...
    while (TryParseMessage(..., out var message)) {
        // 消耗消息
        ...
    }
    ...
}

<details>
<summary>英文:</summary>

Consider the following code, I use this to parse data streams in some performance-critical places. In this case, will the `message` be boxed?

```cs
readonly record struct DataMessage(ulong Number, string Str, 
                                   ReadOnlySequence&lt;byte&gt; Payload);

bool TryParseMessage(ref ReadOnlySequence&lt;byte&gt; buffer, 
                     out DataMessage message) {
    message = default;
    ...
    message = new DataMessage(...);
    return true;
}

void PerformanceCriticalFunction() {
    ...
    while (TryParseMessage(..., out var message)) {
        // consume the message
        ...
    }
    ...
}

答案1

得分: 3

没有。这里没有与拳击相关的内容。拳击是由以下任一情况引起的:

  • 直接将值类型转换为object(或dynamic,它只是一个带有口音的object,或ValueType - 这听起来有点讽刺)
  • 将值类型转换为它实现的接口(在“受限调用”之外,这很复杂,但主要涉及到<T> where T : ISomeInterface
  • 调用未被重写的值类型上的virtual方法(这也与“受限调用”规则相关,如果类型位于不同的程序集中)
  • 使用结构作为委托的目标
  • (可能还有一些其他我忘记的小众情况)

这些情况都不适用,所以...没有拳击。

如果有疑虑:测试它 - 没有发出box指令。如果我们故意添加一个GC.KeepAlive(message)(在我的情况下是在TryParseMessage中)以查找预期的拳击,我们会看到类似于:

    IL_0023: ldarg.2
    IL_0024: ldobj DataMessage
    IL_0029: box DataMessage
    IL_002e: call void [System.Runtime]System.GC::KeepAlive(object)

这告诉您您要寻找的内容。

英文:

No. There is nothing here that relates to boxing. Boxing is caused by any of the following:

  • converting a value-type to object directly (or dynamic, which is just object with an accent, or ValueType - which sounds ironic)
  • converting a value-type to an interface that it implements (outside of "constrained call", which is ... complex to explain but mostly comes down to &lt;T&gt; where T : ISomeInterface)
  • calling a virtual method that is not overridden on a value-type (this also relates to "constrained call" rules if the type is in a different assembly)
  • using the struct as the target of a delegate
  • (probably some other niche things I'm forgetting)

None of these things apply, so... no boxing

If in doubt: test it - there are no box instructions emitted. If we deliberately add a GC.KeepAlive(message) (in my case in TryParseMessage) to look for expected boxing, we see something similar to:

    IL_0023: ldarg.2
    IL_0024: ldobj DataMessage
    IL_0029: box DataMessage
    IL_002e: call void [System.Runtime]System.GC::KeepAlive(object)

which tells you what you're looking for

huangapple
  • 本文由 发表于 2023年2月8日 17:01:56
  • 转载请务必保留本文链接:https://go.coder-hub.com/75383368.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定