Skip to content

Add "Types should be sealed" analyzer#7878

Merged
andrewlock merged 8 commits intomasterfrom
andrew/sealed-analyzer
Dec 8, 2025
Merged

Add "Types should be sealed" analyzer#7878
andrewlock merged 8 commits intomasterfrom
andrew/sealed-analyzer

Conversation

@andrewlock
Copy link
Member

@andrewlock andrewlock commented Nov 28, 2025

Summary of changes

Adds an analyzer that checks that types are sealed (where appropriate)

Reason for change

There are numerous performance benefits to sealing types (as per this issue):

  1. Calls to overrides can be done directly rather than with virtual dispatch, which then also means they can be inlined.
Example

public class C {
    internal void Call(SealedType o) => o.M();
    internal void Call(NonSealedType o) => o.M();
}

internal class BaseType
{
    public virtual void M() {}
}
internal class NonSealedType : BaseType
{
    public override void M() {}
}
internal sealed class SealedType : BaseType
{
    public override void M() {}
}

results in

C.Call(SealedType)
    L0000: cmp [rdx], edx
    L0002: ret

C.Call(NonSealedType)
    L0000: mov rcx, rdx
    L0003: mov rax, [rdx]
    L0006: mov rax, [rax+0x40]
    L000a: mov rax, [rax+0x20]
    L000e: jmp rax

  1. is/as type checks for the type can be done more efficiently, as it only needs to compare the type itself rather than account for a potential hierarchy.
Example

public class C {
    public bool IsSealed(Object o) => o is SealedType;
    public bool IsNotSealed(Object o) => o is NonSealedType;
}

internal class NonSealedType { }
internal sealed class SealedType { }

results in:

C.IsSealed(System.Object)
    L0000: test rdx, rdx
    L0003: je short L0016
    L0005: mov rax, 0x7ff7ab9ad180
    L000f: cmp [rdx], rax
    L0012: je short L0016
    L0014: xor edx, edx
    L0016: test rdx, rdx
    L0019: setne al
    L001c: movzx eax, al
    L001f: ret

C.IsNotSealed(System.Object)
    L0000: sub rsp, 0x28
    L0004: mov rcx, 0x7ff7ab9ad048
    L000e: call System.Runtime.CompilerServices.CastHelpers.IsInstanceOfClass(Void*, System.Object)
    L0013: test rax, rax
    L0016: setne al
    L0019: movzx eax, al
    L001c: add rsp, 0x28
    L0020: ret

  1. Arrays of that type don’t need covariance checks every time an element is stored into it.
Example

public class C {
    internal void StoreSealed(SealedType[] arr, SealedType item) => arr[0] = item;
    internal void StoreNonSealed(NonSealedType[] arr, NonSealedType item) => arr[0] = item;
}

internal class NonSealedType { }
internal sealed class SealedType { }

results in

C.StoreSealed(SealedType[], SealedType)
    L0000: sub rsp, 0x28
    L0004: cmp dword ptr [rdx+8], 0
    L0008: jbe short L001c
    L000a: lea rcx, [rdx+0x10]
    L000e: mov rdx, r8
    L0011: call 0x00007ff801db9f80
    L0016: nop
    L0017: add rsp, 0x28
    L001b: ret
    L001c: call 0x00007ff801f0bc70
    L0021: int3

C.StoreNonSealed(NonSealedType[], NonSealedType)
    L0000: sub rsp, 0x28
    L0004: mov rcx, rdx
    L0007: xor edx, edx
    L0009: call System.Runtime.CompilerServices.CastHelpers.StelemRef(System.Array, Int32, System.Object)
    L000e: nop
    L000f: add rsp, 0x28
    L0013: ret

  1. Creating spans of that type don’t need to validate the actual type of the array matches the specified generic type.
Example

using System;
public class C {
    internal Span<SealedType> CreateSealed(SealedType[] arr) => arr;
    internal Span<NonSealedType> CreateNonSealedType(NonSealedType[] arr) => arr;
}

internal class NonSealedType { }
internal sealed class SealedType { }

results in

C.CreateSealed(SealedType[])
    L0000: test r8, r8
    L0003: jne short L000b
    L0005: xor eax, eax
    L0007: xor ecx, ecx
    L0009: jmp short L0013
    L000b: lea rax, [r8+0x10]
    L000f: mov ecx, [r8+8]
    L0013: mov [rdx], rax
    L0016: mov [rdx+8], ecx
    L0019: mov rax, rdx
    L001c: ret

C.CreateNonSealedType(NonSealedType[])
    L0000: sub rsp, 0x28
    L0004: test r8, r8
    L0007: jne short L000f
    L0009: xor eax, eax
    L000b: xor ecx, ecx
    L000d: jmp short L0026
    L000f: mov rax, 0x7ff7aba4d870
    L0019: cmp [r8], rax
    L001c: jne short L0034
    L001e: lea rax, [r8+0x10]
    L0022: mov ecx, [r8+8]
    L0026: mov [rdx], rax
    L0029: mov [rdx+8], ecx
    L002c: mov rax, rdx
    L002f: add rsp, 0x28
    L0033: ret
    L0034: call System.ThrowHelper.ThrowArrayTypeMismatchException()
    L0039: int3

And there's not really a good reason not to in most cases.

The .NET SDK includes such an analyzer, but it's not quite right for our purposes.:

  • The .NET SDK version only applies to internal types, whereas we should apply it to public too (seeing as our "public" API isn't actually any different from an internal API)
  • In some cases we can't be sealed, e.g. duck types Subsequently realised that's not the case, we always create "wrapper" duck types around an instance, even with reverse duck typing, so whether the instance is sealed doesn't matter

Implementation details

  • Mostly copied the existing SealInternalTypes analyzer from the .NET SDK, and tweaked it
  • Originally excluded duck-types, but later removed as realised not necessary
  • Ignore "internals visible to" as it only impacts us, and we'd rather take the inconvenience of having to pragma ignore some cases
  • Left it as info for now, so that we can opt in certain projects as we see fit.
    • Note that for technical reasons, that means this does not show as default
  • In a subsequent PR, I'll bump the severity and fix the usages

Test coverage

Added unit tests in this PR, and I can confirm it works as expected locally

Other details

I'll make a follow up PR to enable this diagnostic in Datadog.Trace and select other projects

https://datadoghq.atlassian.net/browse/LANGPLAT-813

@andrewlock andrewlock requested a review from a team as a code owner November 28, 2025 13:57
Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@dd-trace-dotnet-ci-bot
Copy link

dd-trace-dotnet-ci-bot bot commented Nov 28, 2025

Execution-Time Benchmarks Report ⏱️

Execution-time results for samples comparing This PR (7878) and master.

✅ No regressions detected - check the details below

Full Metrics Comparison

FakeDbCommand

Metric Master (Mean ± 95% CI) Current (Mean ± 95% CI) Change Status
.NET Framework 4.8 - Baseline
duration75.51 ± (75.51 - 76.16) ms75.55 ± (75.45 - 76.29) ms+0.1%✅⬆️
.NET Framework 4.8 - Bailout
duration79.71 ± (79.41 - 80.04) ms81.80 ± (81.07 - 81.93) ms+2.6%✅⬆️
.NET Framework 4.8 - CallTarget+Inlining+NGEN
duration1059.05 ± (1059.60 - 1068.42) ms1066.67 ± (1065.95 - 1075.01) ms+0.7%✅⬆️
.NET Core 3.1 - Baseline
process.internal_duration_ms22.87 ± (22.80 - 22.95) ms22.86 ± (22.77 - 22.94) ms-0.1%
process.time_to_main_ms86.29 ± (85.99 - 86.59) ms87.70 ± (87.31 - 88.09) ms+1.6%✅⬆️
runtime.dotnet.exceptions.count0 ± (0 - 0)0 ± (0 - 0)+0.0%
runtime.dotnet.mem.committed10.90 ± (10.90 - 10.91) MB10.94 ± (10.93 - 10.94) MB+0.3%✅⬆️
runtime.dotnet.threads.count12 ± (12 - 12)12 ± (12 - 12)+0.0%
.NET Core 3.1 - Bailout
process.internal_duration_ms22.71 ± (22.65 - 22.77) ms22.72 ± (22.66 - 22.78) ms+0.0%✅⬆️
process.time_to_main_ms87.36 ± (87.01 - 87.71) ms89.05 ± (88.68 - 89.41) ms+1.9%✅⬆️
runtime.dotnet.exceptions.count0 ± (0 - 0)0 ± (0 - 0)+0.0%
runtime.dotnet.mem.committed10.95 ± (10.94 - 10.95) MB10.97 ± (10.97 - 10.97) MB+0.2%✅⬆️
runtime.dotnet.threads.count13 ± (13 - 13)13 ± (13 - 13)+0.0%
.NET Core 3.1 - CallTarget+Inlining+NGEN
process.internal_duration_ms220.19 ± (218.68 - 221.71) ms220.80 ± (219.47 - 222.13) ms+0.3%✅⬆️
process.time_to_main_ms499.39 ± (498.24 - 500.53) ms499.24 ± (498.05 - 500.43) ms-0.0%
runtime.dotnet.exceptions.count0 ± (0 - 0)0 ± (0 - 0)+0.0%
runtime.dotnet.mem.committed47.87 ± (47.85 - 47.89) MB47.88 ± (47.86 - 47.90) MB+0.0%✅⬆️
runtime.dotnet.threads.count28 ± (28 - 28)28 ± (28 - 28)+0.0%
.NET 6 - Baseline
process.internal_duration_ms21.80 ± (21.74 - 21.86) ms21.76 ± (21.69 - 21.83) ms-0.2%
process.time_to_main_ms76.20 ± (75.93 - 76.47) ms75.56 ± (75.24 - 75.88) ms-0.8%
runtime.dotnet.exceptions.count0 ± (0 - 0)0 ± (0 - 0)+0.0%
runtime.dotnet.mem.committed10.59 ± (10.59 - 10.59) MB10.63 ± (10.62 - 10.63) MB+0.3%✅⬆️
runtime.dotnet.threads.count10 ± (10 - 10)10 ± (10 - 10)+0.0%
.NET 6 - Bailout
process.internal_duration_ms21.78 ± (21.71 - 21.85) ms21.71 ± (21.65 - 21.78) ms-0.3%
process.time_to_main_ms77.80 ± (77.49 - 78.10) ms77.40 ± (77.03 - 77.76) ms-0.5%
runtime.dotnet.exceptions.count0 ± (0 - 0)0 ± (0 - 0)+0.0%
runtime.dotnet.mem.committed10.64 ± (10.64 - 10.64) MB10.66 ± (10.66 - 10.67) MB+0.2%✅⬆️
runtime.dotnet.threads.count11 ± (11 - 11)11 ± (11 - 11)+0.0%
.NET 6 - CallTarget+Inlining+NGEN
process.internal_duration_ms210.02 ± (208.88 - 211.16) ms212.97 ± (211.74 - 214.21) ms+1.4%✅⬆️
process.time_to_main_ms465.92 ± (464.77 - 467.07) ms469.16 ± (468.19 - 470.13) ms+0.7%✅⬆️
runtime.dotnet.exceptions.count0 ± (0 - 0)0 ± (0 - 0)+0.0%
runtime.dotnet.mem.committed48.14 ± (48.11 - 48.17) MB48.11 ± (48.08 - 48.13) MB-0.1%
runtime.dotnet.threads.count28 ± (28 - 28)28 ± (28 - 28)+0.0%✅⬆️
.NET 8 - Baseline
process.internal_duration_ms19.67 ± (19.61 - 19.73) ms19.79 ± (19.73 - 19.84) ms+0.6%✅⬆️
process.time_to_main_ms74.47 ± (74.15 - 74.80) ms75.26 ± (74.92 - 75.60) ms+1.1%✅⬆️
runtime.dotnet.exceptions.count0 ± (0 - 0)0 ± (0 - 0)+0.0%
runtime.dotnet.mem.committed7.64 ± (7.64 - 7.65) MB7.65 ± (7.64 - 7.65) MB+0.0%✅⬆️
runtime.dotnet.threads.count10 ± (10 - 10)10 ± (10 - 10)+0.0%
.NET 8 - Bailout
process.internal_duration_ms20.05 ± (19.97 - 20.12) ms19.75 ± (19.69 - 19.80) ms-1.5%
process.time_to_main_ms78.63 ± (78.21 - 79.06) ms76.17 ± (75.88 - 76.47) ms-3.1%
runtime.dotnet.exceptions.count0 ± (0 - 0)0 ± (0 - 0)+0.0%
runtime.dotnet.mem.committed7.69 ± (7.69 - 7.70) MB7.68 ± (7.68 - 7.69) MB-0.1%
runtime.dotnet.threads.count11 ± (11 - 11)11 ± (11 - 11)+0.0%
.NET 8 - CallTarget+Inlining+NGEN
process.internal_duration_ms194.08 ± (192.88 - 195.27) ms191.28 ± (190.39 - 192.17) ms-1.4%
process.time_to_main_ms456.43 ± (455.31 - 457.55) ms454.12 ± (453.13 - 455.12) ms-0.5%
runtime.dotnet.exceptions.count0 ± (0 - 0)0 ± (0 - 0)+0.0%
runtime.dotnet.mem.committed36.34 ± (36.30 - 36.38) MB36.37 ± (36.33 - 36.41) MB+0.1%✅⬆️
runtime.dotnet.threads.count27 ± (27 - 27)27 ± (27 - 27)-0.1%

HttpMessageHandler

Metric Master (Mean ± 95% CI) Current (Mean ± 95% CI) Change Status
.NET Framework 4.8 - Baseline
duration192.38 ± (192.44 - 193.25) ms192.91 ± (193.37 - 194.26) ms+0.3%✅⬆️
.NET Framework 4.8 - Bailout
duration196.53 ± (196.38 - 196.89) ms196.81 ± (196.82 - 197.31) ms+0.1%✅⬆️
.NET Framework 4.8 - CallTarget+Inlining+NGEN
duration1109.78 ± (1112.40 - 1119.95) ms1108.32 ± (1112.59 - 1121.05) ms-0.1%
.NET Core 3.1 - Baseline
process.internal_duration_ms187.76 ± (187.35 - 188.16) ms187.80 ± (187.46 - 188.14) ms+0.0%✅⬆️
process.time_to_main_ms80.58 ± (80.38 - 80.79) ms80.49 ± (80.30 - 80.69) ms-0.1%
runtime.dotnet.exceptions.count3 ± (3 - 3)3 ± (3 - 3)+0.0%
runtime.dotnet.mem.committed16.09 ± (16.06 - 16.11) MB16.18 ± (16.15 - 16.21) MB+0.6%✅⬆️
runtime.dotnet.threads.count20 ± (19 - 20)20 ± (19 - 20)-0.2%
.NET Core 3.1 - Bailout
process.internal_duration_ms187.18 ± (186.86 - 187.50) ms187.68 ± (187.33 - 188.04) ms+0.3%✅⬆️
process.time_to_main_ms81.88 ± (81.75 - 82.00) ms81.99 ± (81.85 - 82.14) ms+0.1%✅⬆️
runtime.dotnet.exceptions.count3 ± (3 - 3)3 ± (3 - 3)+0.0%
runtime.dotnet.mem.committed16.14 ± (16.12 - 16.17) MB16.20 ± (16.17 - 16.23) MB+0.4%✅⬆️
runtime.dotnet.threads.count21 ± (20 - 21)21 ± (21 - 21)+0.5%✅⬆️
.NET Core 3.1 - CallTarget+Inlining+NGEN
process.internal_duration_ms399.72 ± (396.96 - 402.48) ms393.74 ± (391.47 - 396.02) ms-1.5%
process.time_to_main_ms473.05 ± (472.44 - 473.65) ms470.35 ± (469.70 - 470.99) ms-0.6%
runtime.dotnet.exceptions.count3 ± (3 - 3)3 ± (3 - 3)+0.0%
runtime.dotnet.mem.committed58.68 ± (58.56 - 58.81) MB58.31 ± (58.15 - 58.46) MB-0.6%
runtime.dotnet.threads.count29 ± (29 - 29)29 ± (29 - 29)+0.0%✅⬆️
.NET 6 - Baseline
process.internal_duration_ms192.79 ± (192.47 - 193.12) ms192.11 ± (191.76 - 192.46) ms-0.4%
process.time_to_main_ms69.96 ± (69.80 - 70.13) ms69.91 ± (69.79 - 70.04) ms-0.1%
runtime.dotnet.exceptions.count4 ± (4 - 4)4 ± (4 - 4)+0.0%
runtime.dotnet.mem.committed16.22 ± (16.11 - 16.34) MB16.12 ± (15.99 - 16.26) MB-0.6%
runtime.dotnet.threads.count19 ± (19 - 19)18 ± (18 - 18)-3.2%
.NET 6 - Bailout
process.internal_duration_ms191.29 ± (191.04 - 191.54) ms191.92 ± (191.50 - 192.34) ms+0.3%✅⬆️
process.time_to_main_ms70.82 ± (70.73 - 70.91) ms71.05 ± (70.91 - 71.19) ms+0.3%✅⬆️
runtime.dotnet.exceptions.count4 ± (4 - 4)4 ± (4 - 4)+0.0%
runtime.dotnet.mem.committed16.10 ± (15.96 - 16.24) MB16.17 ± (16.05 - 16.28) MB+0.4%✅⬆️
runtime.dotnet.threads.count20 ± (19 - 20)20 ± (20 - 20)+0.4%✅⬆️
.NET 6 - CallTarget+Inlining+NGEN
process.internal_duration_ms409.55 ± (407.01 - 412.10) ms407.56 ± (405.31 - 409.81) ms-0.5%
process.time_to_main_ms441.10 ± (440.35 - 441.85) ms441.08 ± (440.50 - 441.66) ms-0.0%
runtime.dotnet.exceptions.count4 ± (4 - 4)4 ± (4 - 4)+0.0%
runtime.dotnet.mem.committed58.95 ± (58.78 - 59.13) MB58.72 ± (58.57 - 58.87) MB-0.4%
runtime.dotnet.threads.count30 ± (29 - 30)30 ± (30 - 30)+0.1%✅⬆️
.NET 8 - Baseline
process.internal_duration_ms190.32 ± (190.02 - 190.62) ms190.35 ± (189.98 - 190.72) ms+0.0%✅⬆️
process.time_to_main_ms69.33 ± (69.16 - 69.49) ms69.67 ± (69.46 - 69.88) ms+0.5%✅⬆️
runtime.dotnet.exceptions.count4 ± (4 - 4)4 ± (4 - 4)+0.0%
runtime.dotnet.mem.committed11.81 ± (11.78 - 11.83) MB11.73 ± (11.70 - 11.76) MB-0.7%
runtime.dotnet.threads.count18 ± (18 - 18)18 ± (18 - 18)-0.2%
.NET 8 - Bailout
process.internal_duration_ms190.13 ± (189.86 - 190.39) ms189.34 ± (189.08 - 189.59) ms-0.4%
process.time_to_main_ms70.41 ± (70.29 - 70.52) ms70.25 ± (70.16 - 70.34) ms-0.2%
runtime.dotnet.exceptions.count4 ± (4 - 4)4 ± (4 - 4)+0.0%
runtime.dotnet.mem.committed11.80 ± (11.77 - 11.84) MB11.86 ± (11.83 - 11.89) MB+0.4%✅⬆️
runtime.dotnet.threads.count19 ± (19 - 19)19 ± (19 - 19)-0.5%
.NET 8 - CallTarget+Inlining+NGEN
process.internal_duration_ms367.77 ± (366.27 - 369.27) ms366.06 ± (364.62 - 367.50) ms-0.5%
process.time_to_main_ms429.98 ± (429.24 - 430.72) ms429.67 ± (428.97 - 430.38) ms-0.1%
runtime.dotnet.exceptions.count4 ± (4 - 4)4 ± (4 - 4)+0.0%
runtime.dotnet.mem.committed47.88 ± (47.85 - 47.91) MB47.89 ± (47.86 - 47.92) MB+0.0%✅⬆️
runtime.dotnet.threads.count29 ± (29 - 29)29 ± (29 - 29)+0.0%✅⬆️
Comparison explanation

Execution-time benchmarks measure the whole time it takes to execute a program, and are intended to measure the one-off costs. Cases where the execution time results for the PR are worse than latest master results are highlighted in **red**. The following thresholds were used for comparing the execution times:

  • Welch test with statistical test for significance of 5%
  • Only results indicating a difference greater than 5% and 5 ms are considered.

Note that these results are based on a single point-in-time result for each branch. For full results, see the dashboard.

Graphs show the p99 interval based on the mean and StdDev of the test run, as well as the mean value of the run (shown as a diamond below the graph).

Duration charts
FakeDbCommand (.NET Framework 4.8)
gantt
    title Execution time (ms) FakeDbCommand (.NET Framework 4.8)
    dateFormat  x
    axisFormat %Q
    todayMarker off
    section Baseline
    This PR (7878) - mean (76ms)  : 69, 82
    master - mean (76ms)  : 71, 81

    section Bailout
    This PR (7878) - mean (81ms)  : 75, 88
    master - mean (80ms)  : 75, 85

    section CallTarget+Inlining+NGEN
    This PR (7878) - mean (1,070ms)  : 1005, 1136
    master - mean (1,064ms)  : 998, 1130

Loading
FakeDbCommand (.NET Core 3.1)
gantt
    title Execution time (ms) FakeDbCommand (.NET Core 3.1)
    dateFormat  x
    axisFormat %Q
    todayMarker off
    section Baseline
    This PR (7878) - mean (118ms)  : 109, 127
    master - mean (117ms)  : 111, 122

    section Bailout
    This PR (7878) - mean (119ms)  : 112, 126
    master - mean (117ms)  : 110, 125

    section CallTarget+Inlining+NGEN
    This PR (7878) - mean (754ms)  : 716, 792
    master - mean (758ms)  : 718, 798

Loading
FakeDbCommand (.NET 6)
gantt
    title Execution time (ms) FakeDbCommand (.NET 6)
    dateFormat  x
    axisFormat %Q
    todayMarker off
    section Baseline
    This PR (7878) - mean (104ms)  : 99, 110
    master - mean (105ms)  : 100, 110

    section Bailout
    This PR (7878) - mean (106ms)  : 101, 112
    master - mean (107ms)  : 102, 112

    section CallTarget+Inlining+NGEN
    This PR (7878) - mean (709ms)  : 676, 742
    master - mean (703ms)  : 672, 734

Loading
FakeDbCommand (.NET 8)
gantt
    title Execution time (ms) FakeDbCommand (.NET 8)
    dateFormat  x
    axisFormat %Q
    todayMarker off
    section Baseline
    This PR (7878) - mean (103ms)  : 97, 110
    master - mean (103ms)  : 95, 110

    section Bailout
    This PR (7878) - mean (105ms)  : 99, 111
    master - mean (107ms)  : 99, 115

    section CallTarget+Inlining+NGEN
    This PR (7878) - mean (679ms)  : 642, 716
    master - mean (684ms)  : 642, 725

Loading
HttpMessageHandler (.NET Framework 4.8)
gantt
    title Execution time (ms) HttpMessageHandler (.NET Framework 4.8)
    dateFormat  x
    axisFormat %Q
    todayMarker off
    section Baseline
    This PR (7878) - mean (194ms)  : 188, 199
    master - mean (193ms)  : 189, 197

    section Bailout
    This PR (7878) - mean (197ms)  : 195, 199
    master - mean (197ms)  : 194, 199

    section CallTarget+Inlining+NGEN
    This PR (7878) - mean (1,117ms)  : 1055, 1179
    master - mean (1,116ms)  : 1062, 1170

Loading
HttpMessageHandler (.NET Core 3.1)
gantt
    title Execution time (ms) HttpMessageHandler (.NET Core 3.1)
    dateFormat  x
    axisFormat %Q
    todayMarker off
    section Baseline
    This PR (7878) - mean (277ms)  : 272, 282
    master - mean (277ms)  : 271, 283

    section Bailout
    This PR (7878) - mean (278ms)  : 273, 282
    master - mean (277ms)  : 274, 281

    section CallTarget+Inlining+NGEN
    This PR (7878) - mean (902ms)  : 867, 937
    master - mean (911ms)  : 865, 957

Loading
HttpMessageHandler (.NET 6)
gantt
    title Execution time (ms) HttpMessageHandler (.NET 6)
    dateFormat  x
    axisFormat %Q
    todayMarker off
    section Baseline
    This PR (7878) - mean (270ms)  : 266, 274
    master - mean (271ms)  : 267, 275

    section Bailout
    This PR (7878) - mean (271ms)  : 265, 276
    master - mean (270ms)  : 267, 274

    section CallTarget+Inlining+NGEN
    This PR (7878) - mean (886ms)  : 846, 926
    master - mean (885ms)  : 845, 925

Loading
HttpMessageHandler (.NET 8)
gantt
    title Execution time (ms) HttpMessageHandler (.NET 8)
    dateFormat  x
    axisFormat %Q
    todayMarker off
    section Baseline
    This PR (7878) - mean (270ms)  : 265, 275
    master - mean (269ms)  : 265, 272

    section Bailout
    This PR (7878) - mean (269ms)  : 266, 272
    master - mean (270ms)  : 267, 273

    section CallTarget+Inlining+NGEN
    This PR (7878) - mean (829ms)  : 810, 848
    master - mean (827ms)  : 806, 848

Loading

@pr-commenter
Copy link

pr-commenter bot commented Nov 28, 2025

Benchmarks

Benchmarks Report for benchmark platform 🐌

Benchmarks for #7878 compared to master:

  • 1 benchmarks are faster, with geometric mean 1.801
  • 1 benchmarks have fewer allocations
  • 2 benchmarks have more allocations

The following thresholds were used for comparing the benchmark speeds:

  • Mann–Whitney U test with statistical test for significance of 5%
  • Only results indicating a difference greater than 10% and 0.3 ns are considered.

Allocation changes below 0.5% are ignored.

Benchmark details

Benchmarks.Trace.ActivityBenchmark - Same speed ✔️ Fewer allocations 🎉

Fewer allocations 🎉 in #7878

Benchmark Base Allocated Diff Allocated Change Change %
Benchmarks.Trace.ActivityBenchmark.StartStopWithChild‑net472 6 KB 5.93 KB -73 B -1.22%

Raw results

Branch Method Toolchain Mean StdError StdDev Gen 0 Gen 1 Gen 2 Allocated
master StartStopWithChild net6.0 10.5μs 56.6ns 330ns 0 0 0 5.5 KB
master StartStopWithChild netcoreapp3.1 13.5μs 68.5ns 306ns 0 0 0 5.72 KB
master StartStopWithChild net472 21.8μs 79.6ns 298ns 0.87 0.217 0 6 KB
#7878 StartStopWithChild net6.0 11μs 60.7ns 364ns 0 0 0 5.52 KB
#7878 StartStopWithChild netcoreapp3.1 13.7μs 68.9ns 292ns 0 0 0 5.71 KB
#7878 StartStopWithChild net472 22.2μs 98.9ns 370ns 1.03 0.309 0.103 5.93 KB
Benchmarks.Trace.AgentWriterBenchmark - Same speed ✔️ Same allocations ✔️

Raw results

Branch Method Toolchain Mean StdError StdDev Gen 0 Gen 1 Gen 2 Allocated
master WriteAndFlushEnrichedTraces net6.0 1.25ms 206ns 770ns 0 0 0 2.7 KB
master WriteAndFlushEnrichedTraces netcoreapp3.1 1.39ms 79.6ns 308ns 0 0 0 2.7 KB
master WriteAndFlushEnrichedTraces net472 1.76ms 1.26μs 4.86μs 0 0 0 3.26 KB
#7878 WriteAndFlushEnrichedTraces net6.0 1.26ms 175ns 632ns 0 0 0 2.7 KB
#7878 WriteAndFlushEnrichedTraces netcoreapp3.1 1.38ms 148ns 553ns 0 0 0 2.7 KB
#7878 WriteAndFlushEnrichedTraces net472 1.73ms 3.89μs 15.1μs 0 0 0 3.26 KB
Benchmarks.Trace.Asm.AppSecBodyBenchmark - Same speed ✔️ Same allocations ✔️

Raw results

Branch Method Toolchain Mean StdError StdDev Gen 0 Gen 1 Gen 2 Allocated
master AllCycleSimpleBody net6.0 1.09μs 6.08ns 39.9ns 0 0 0 1.22 KB
master AllCycleSimpleBody netcoreapp3.1 1.38μs 7.76ns 53.2ns 0 0 0 1.2 KB
master AllCycleSimpleBody net472 1μs 0.238ns 0.922ns 0.191 0 0 1.23 KB
master AllCycleMoreComplexBody net6.0 7.32μs 36.7ns 168ns 0 0 0 4.72 KB
master AllCycleMoreComplexBody netcoreapp3.1 8.88μs 46.5ns 237ns 0 0 0 4.62 KB
master AllCycleMoreComplexBody net472 7.7μs 4.23ns 16.4ns 0.731 0 0 4.74 KB
master ObjectExtractorSimpleBody net6.0 317ns 0.0977ns 0.366ns 0 0 0 280 B
master ObjectExtractorSimpleBody netcoreapp3.1 395ns 2.17ns 13.4ns 0 0 0 272 B
master ObjectExtractorSimpleBody net472 300ns 0.0108ns 0.0402ns 0.0439 0 0 281 B
master ObjectExtractorMoreComplexBody net6.0 6.34μs 29.4ns 118ns 0 0 0 3.78 KB
master ObjectExtractorMoreComplexBody netcoreapp3.1 7.82μs 36.6ns 142ns 0 0 0 3.69 KB
master ObjectExtractorMoreComplexBody net472 6.68μs 2.21ns 8.56ns 0.602 0 0 3.8 KB
#7878 AllCycleSimpleBody net6.0 1.06μs 6.17ns 52.3ns 0 0 0 1.22 KB
#7878 AllCycleSimpleBody netcoreapp3.1 1.43μs 7.61ns 39.5ns 0 0 0 1.2 KB
#7878 AllCycleSimpleBody net472 1.01μs 0.259ns 1.01ns 0.192 0 0 1.23 KB
#7878 AllCycleMoreComplexBody net6.0 7.12μs 37.9ns 204ns 0 0 0 4.72 KB
#7878 AllCycleMoreComplexBody netcoreapp3.1 9.17μs 45.6ns 199ns 0 0 0 4.62 KB
#7878 AllCycleMoreComplexBody net472 7.7μs 3.51ns 13.6ns 0.733 0 0 4.74 KB
#7878 ObjectExtractorSimpleBody net6.0 308ns 1.78ns 13.8ns 0 0 0 280 B
#7878 ObjectExtractorSimpleBody netcoreapp3.1 416ns 2.04ns 8.4ns 0 0 0 272 B
#7878 ObjectExtractorSimpleBody net472 309ns 0.0817ns 0.316ns 0.0435 0 0 281 B
#7878 ObjectExtractorMoreComplexBody net6.0 6.3μs 29.4ns 114ns 0 0 0 3.78 KB
#7878 ObjectExtractorMoreComplexBody netcoreapp3.1 7.83μs 31ns 120ns 0 0 0 3.69 KB
#7878 ObjectExtractorMoreComplexBody net472 6.8μs 5.41ns 20.9ns 0.579 0 0 3.8 KB
Benchmarks.Trace.Asm.AppSecEncoderBenchmark - Same speed ✔️ Same allocations ✔️

Raw results

Branch Method Toolchain Mean StdError StdDev Gen 0 Gen 1 Gen 2 Allocated
master EncodeArgs net6.0 76.3μs 299ns 1.12μs 0 0 0 32.4 KB
master EncodeArgs netcoreapp3.1 97.9μs 379ns 1.47μs 0 0 0 32.4 KB
master EncodeArgs net472 108μs 14ns 54.2ns 4.87 0 0 32.5 KB
master EncodeLegacyArgs net6.0 142μs 34.7ns 134ns 0 0 0 2.14 KB
master EncodeLegacyArgs netcoreapp3.1 198μs 26.2ns 98.1ns 0 0 0 2.14 KB
master EncodeLegacyArgs net472 263μs 42.9ns 166ns 0 0 0 2.14 KB
#7878 EncodeArgs net6.0 75.7μs 354ns 1.41μs 0 0 0 32.4 KB
#7878 EncodeArgs netcoreapp3.1 96.7μs 269ns 1.04μs 0 0 0 32.4 KB
#7878 EncodeArgs net472 108μs 34.4ns 133ns 4.88 0 0 32.5 KB
#7878 EncodeLegacyArgs net6.0 145μs 15ns 56.2ns 0 0 0 2.14 KB
#7878 EncodeLegacyArgs netcoreapp3.1 200μs 203ns 787ns 0 0 0 2.14 KB
#7878 EncodeLegacyArgs net472 264μs 30.6ns 114ns 0 0 0 2.14 KB
Benchmarks.Trace.Asm.AppSecWafBenchmark - Same speed ✔️ Same allocations ✔️

Raw results

Branch Method Toolchain Mean StdError StdDev Gen 0 Gen 1 Gen 2 Allocated
master RunWafRealisticBenchmark net6.0 427μs 2.14μs 9.31μs 0 0 0 4.55 KB
master RunWafRealisticBenchmark netcoreapp3.1 464μs 1.58μs 5.9μs 0 0 0 4.48 KB
master RunWafRealisticBenchmark net472 496μs 231ns 834ns 0 0 0 0 b
master RunWafRealisticBenchmarkWithAttack net6.0 319μs 1.47μs 5.49μs 0 0 0 2.24 KB
master RunWafRealisticBenchmarkWithAttack netcoreapp3.1 347μs 1.93μs 13.6μs 0 0 0 2.22 KB
master RunWafRealisticBenchmarkWithAttack net472 368μs 493ns 1.78μs 0 0 0 0 b
#7878 RunWafRealisticBenchmark net6.0 430μs 1.69μs 6.09μs 0 0 0 4.55 KB
#7878 RunWafRealisticBenchmark netcoreapp3.1 474μs 2.7μs 25μs 0 0 0 4.48 KB
#7878 RunWafRealisticBenchmark net472 500μs 401ns 1.5μs 0 0 0 0 b
#7878 RunWafRealisticBenchmarkWithAttack net6.0 314μs 1.41μs 5.07μs 0 0 0 2.24 KB
#7878 RunWafRealisticBenchmarkWithAttack netcoreapp3.1 355μs 2.97μs 27.9μs 0 0 0 2.22 KB
#7878 RunWafRealisticBenchmarkWithAttack net472 368μs 472ns 1.7μs 0 0 0 0 b
Benchmarks.Trace.AspNetCoreBenchmark - Unknown 🤷 Same allocations ✔️

Raw results

Branch Method Toolchain Mean StdError StdDev Gen 0 Gen 1 Gen 2 Allocated
master SendRequest net6.0 84μs 75.5ns 292ns 0 0 0 19.03 KB
master SendRequest netcoreapp3.1 97μs 364ns 2.03μs 0 0 0 21.25 KB
master SendRequest net472 0ns 0ns 0ns 0 0 0 0 b
#7878 SendRequest net6.0 84.9μs 85.4ns 308ns 0 0 0 19.04 KB
#7878 SendRequest netcoreapp3.1 98.6μs 390ns 2.02μs 0 0 0 21.25 KB
#7878 SendRequest net472 0ns 0ns 0ns 0 0 0 0 b
Benchmarks.Trace.CharSliceBenchmark - Faster 🎉 Same allocations ✔️

Faster 🎉 in #7878

Benchmark base/diff Base Median (ns) Diff Median (ns) Modality
Benchmarks.Trace.CharSliceBenchmark.OriginalCharSlice‑netcoreapp3.1 1.801 3,928,850.00 2,181,300.00

Raw results

Branch Method Toolchain Mean StdError StdDev Gen 0 Gen 1 Gen 2 Allocated
master OriginalCharSlice net6.0 1.91ms 1.86μs 7.21μs 0 0 0 640 KB
master OriginalCharSlice netcoreapp3.1 3.93ms 877ns 3.39μs 0 0 0 640.05 KB
master OriginalCharSlice net472 2.58ms 442ns 1.65μs 0 0 0 638.98 KB
master OptimizedCharSlice net6.0 1.45ms 3.25μs 12.6μs 0 0 0 0 b
master OptimizedCharSlice netcoreapp3.1 2.73ms 996ns 3.45μs 0 0 0 0 b
master OptimizedCharSlice net472 1.95ms 957ns 3.58μs 0 0 0 0 b
master OptimizedCharSliceWithPool net6.0 1.05ms 691ns 2.68μs 0 0 0 0 b
master OptimizedCharSliceWithPool netcoreapp3.1 1.85ms 1.99μs 7.7μs 0 0 0 0 b
master OptimizedCharSliceWithPool net472 1.16ms 710ns 2.75μs 0 0 0 0 b
#7878 OriginalCharSlice net6.0 1.94ms 1.96μs 7.08μs 0 0 0 640 KB
#7878 OriginalCharSlice netcoreapp3.1 2.18ms 6.59μs 41.7μs 0 0 0 640 KB
#7878 OriginalCharSlice net472 2.71ms 756ns 2.93μs 0 0 0 638.98 KB
#7878 OptimizedCharSlice net6.0 1.48ms 327ns 1.18μs 0 0 0 0 b
#7878 OptimizedCharSlice netcoreapp3.1 2.78ms 558ns 1.93μs 0 0 0 0 b
#7878 OptimizedCharSlice net472 1.89ms 927ns 3.34μs 0 0 0 0 b
#7878 OptimizedCharSliceWithPool net6.0 1.02ms 1.1μs 3.8μs 0 0 0 0 b
#7878 OptimizedCharSliceWithPool netcoreapp3.1 1.86ms 1.87μs 7.24μs 0 0 0 0 b
#7878 OptimizedCharSliceWithPool net472 1.15ms 374ns 1.35μs 0 0 0 0 b
Benchmarks.Trace.CIVisibilityProtocolWriterBenchmark - Same speed ✔️ More allocations ⚠️

More allocations ⚠️ in #7878

Benchmark Base Allocated Diff Allocated Change Change %
Benchmarks.Trace.CIVisibilityProtocolWriterBenchmark.WriteAndFlushEnrichedTraces‑net472 55.79 KB 56.87 KB 1.08 KB 1.93%

Raw results

Branch Method Toolchain Mean StdError StdDev Gen 0 Gen 1 Gen 2 Allocated
master WriteAndFlushEnrichedTraces net6.0 761μs 1.44μs 5.57μs 0 0 0 41.68 KB
master WriteAndFlushEnrichedTraces netcoreapp3.1 700μs 3.64μs 17.8μs 0 0 0 41.8 KB
master WriteAndFlushEnrichedTraces net472 892μs 3.52μs 13.6μs 4.46 0 0 55.79 KB
#7878 WriteAndFlushEnrichedTraces net6.0 751μs 4.38μs 35.1μs 0 0 0 41.73 KB
#7878 WriteAndFlushEnrichedTraces netcoreapp3.1 729μs 4.26μs 40μs 0 0 0 41.78 KB
#7878 WriteAndFlushEnrichedTraces net472 954μs 5.05μs 27.2μs 8.93 4.46 0 56.87 KB
Benchmarks.Trace.DbCommandBenchmark - Same speed ✔️ Same allocations ✔️

Raw results

Branch Method Toolchain Mean StdError StdDev Gen 0 Gen 1 Gen 2 Allocated
master ExecuteNonQuery net6.0 1.86μs 7.27ns 28.2ns 0 0 0 1.02 KB
master ExecuteNonQuery netcoreapp3.1 2.57μs 6.77ns 26.2ns 0 0 0 1.02 KB
master ExecuteNonQuery net472 2.79μs 3.48ns 13.5ns 0.15 0 0 987 B
#7878 ExecuteNonQuery net6.0 1.98μs 2.4ns 9.3ns 0 0 0 1.02 KB
#7878 ExecuteNonQuery netcoreapp3.1 2.54μs 6.09ns 23.6ns 0 0 0 1.02 KB
#7878 ExecuteNonQuery net472 2.69μs 1.1ns 3.81ns 0.149 0 0 987 B
Benchmarks.Trace.ElasticsearchBenchmark - Same speed ✔️ Same allocations ✔️

Raw results

Branch Method Toolchain Mean StdError StdDev Gen 0 Gen 1 Gen 2 Allocated
master CallElasticsearch net6.0 1.67μs 8.33ns 36.3ns 0 0 0 1.01 KB
master CallElasticsearch netcoreapp3.1 2.27μs 9.56ns 37ns 0 0 0 1.02 KB
master CallElasticsearch net472 3.4μs 3.1ns 11.6ns 0.154 0 0 1.01 KB
master CallElasticsearchAsync net6.0 1.76μs 8.62ns 35.5ns 0 0 0 984 B
master CallElasticsearchAsync netcoreapp3.1 2.46μs 10.8ns 41.9ns 0 0 0 1.07 KB
master CallElasticsearchAsync net472 3.55μs 1.58ns 5.9ns 0.159 0 0 1.07 KB
#7878 CallElasticsearch net6.0 1.72μs 2.5ns 9.69ns 0 0 0 1.01 KB
#7878 CallElasticsearch netcoreapp3.1 2.23μs 10.9ns 46.4ns 0 0 0 1.02 KB
#7878 CallElasticsearch net472 3.53μs 2.15ns 8.32ns 0.158 0 0 1.01 KB
#7878 CallElasticsearchAsync net6.0 1.75μs 6.77ns 26.2ns 0 0 0 984 B
#7878 CallElasticsearchAsync netcoreapp3.1 2.72μs 9.45ns 36.6ns 0 0 0 1.07 KB
#7878 CallElasticsearchAsync net472 3.6μs 3.85ns 14.9ns 0.163 0 0 1.07 KB
Benchmarks.Trace.GraphQLBenchmark - Same speed ✔️ Same allocations ✔️

Raw results

Branch Method Toolchain Mean StdError StdDev Gen 0 Gen 1 Gen 2 Allocated
master ExecuteAsync net6.0 1.91μs 9.42ns 38.8ns 0 0 0 953 B
master ExecuteAsync netcoreapp3.1 2.5μs 7.36ns 28.5ns 0 0 0 952 B
master ExecuteAsync net472 2.63μs 2.29ns 8.86ns 0.144 0 0 915 B
#7878 ExecuteAsync net6.0 1.88μs 8.98ns 37ns 0 0 0 952 B
#7878 ExecuteAsync netcoreapp3.1 2.38μs 8.47ns 32.8ns 0 0 0 952 B
#7878 ExecuteAsync net472 2.68μs 1.48ns 5.72ns 0.134 0 0 915 B
Benchmarks.Trace.HttpClientBenchmark - Same speed ✔️ Same allocations ✔️

Raw results

Branch Method Toolchain Mean StdError StdDev Gen 0 Gen 1 Gen 2 Allocated
master SendAsync net6.0 6.89μs 23.4ns 90.8ns 0 0 0 2.34 KB
master SendAsync netcoreapp3.1 8.46μs 24.9ns 89.7ns 0 0 0 2.89 KB
master SendAsync net472 12.2μs 9.27ns 34.7ns 0.489 0 0 3.14 KB
#7878 SendAsync net6.0 6.97μs 16.4ns 63.7ns 0 0 0 2.34 KB
#7878 SendAsync netcoreapp3.1 8.16μs 27.4ns 106ns 0 0 0 2.89 KB
#7878 SendAsync net472 12.3μs 10.1ns 39.3ns 0.493 0 0 3.14 KB
Benchmarks.Trace.Iast.StringAspectsBenchmark - Same speed ✔️ More allocations ⚠️

More allocations ⚠️ in #7878

Benchmark Base Allocated Diff Allocated Change Change %
Benchmarks.Trace.Iast.StringAspectsBenchmark.StringConcatAspectBenchmark‑netcoreapp3.1 257.52 KB 272.67 KB 15.15 KB 5.88%

Raw results

Branch Method Toolchain Mean StdError StdDev Gen 0 Gen 1 Gen 2 Allocated
master StringConcatBenchmark net6.0 44.9μs 266ns 2.39μs 0 0 0 42.51 KB
master StringConcatBenchmark netcoreapp3.1 50.7μs 327ns 3.09μs 0 0 0 42.54 KB
master StringConcatBenchmark net472 56.4μs 290ns 1.3μs 0 0 0 49.15 KB
master StringConcatAspectBenchmark net6.0 485μs 2.34μs 9.37μs 0 0 0 273.92 KB
master StringConcatAspectBenchmark netcoreapp3.1 434μs 5.13μs 50.3μs 0 0 0 257.52 KB
master StringConcatAspectBenchmark net472 411μs 2.37μs 18.3μs 0 0 0 270.34 KB
#7878 StringConcatBenchmark net6.0 44.4μs 255ns 1.79μs 0 0 0 42.51 KB
#7878 StringConcatBenchmark netcoreapp3.1 48.3μs 262ns 1.33μs 0 0 0 42.54 KB
#7878 StringConcatBenchmark net472 58.8μs 291ns 1.3μs 0 0 0 49.15 KB
#7878 StringConcatAspectBenchmark net6.0 474μs 2.09μs 10.2μs 0 0 0 273.98 KB
#7878 StringConcatAspectBenchmark netcoreapp3.1 475μs 6.94μs 68.7μs 0 0 0 272.67 KB
#7878 StringConcatAspectBenchmark net472 417μs 2.3μs 13.4μs 0 0 0 270.34 KB
Benchmarks.Trace.ILoggerBenchmark - Same speed ✔️ Same allocations ✔️

Raw results

Branch Method Toolchain Mean StdError StdDev Gen 0 Gen 1 Gen 2 Allocated
master EnrichedLog net6.0 2.71μs 13.2ns 57.7ns 0 0 0 1.69 KB
master EnrichedLog netcoreapp3.1 3.53μs 4.87ns 18.9ns 0 0 0 1.7 KB
master EnrichedLog net472 3.85μs 2.27ns 8.81ns 0.251 0 0 1.6 KB
#7878 EnrichedLog net6.0 2.66μs 2.13ns 8.27ns 0 0 0 1.69 KB
#7878 EnrichedLog netcoreapp3.1 3.54μs 16.8ns 67.3ns 0 0 0 1.7 KB
#7878 EnrichedLog net472 3.82μs 2.22ns 8.31ns 0.248 0 0 1.6 KB
Benchmarks.Trace.Log4netBenchmark - Same speed ✔️ Same allocations ✔️

Raw results

Branch Method Toolchain Mean StdError StdDev Gen 0 Gen 1 Gen 2 Allocated
master EnrichedLog net6.0 124μs 53.6ns 186ns 0 0 0 4.31 KB
master EnrichedLog netcoreapp3.1 129μs 199ns 718ns 0 0 0 4.31 KB
master EnrichedLog net472 168μs 150ns 580ns 0 0 0 4.51 KB
#7878 EnrichedLog net6.0 124μs 385ns 1.44μs 0 0 0 4.31 KB
#7878 EnrichedLog netcoreapp3.1 129μs 84.4ns 316ns 0 0 0 4.31 KB
#7878 EnrichedLog net472 167μs 86ns 333ns 0 0 0 4.51 KB
Benchmarks.Trace.NLogBenchmark - Same speed ✔️ Same allocations ✔️

Raw results

Branch Method Toolchain Mean StdError StdDev Gen 0 Gen 1 Gen 2 Allocated
master EnrichedLog net6.0 5.15μs 20.2ns 78.2ns 0 0 0 2.24 KB
master EnrichedLog netcoreapp3.1 6.95μs 20.3ns 78.6ns 0 0 0 2.26 KB
master EnrichedLog net472 7.55μs 8.84ns 34.2ns 0.3 0 0 2.05 KB
#7878 EnrichedLog net6.0 5.1μs 21.9ns 84.9ns 0 0 0 2.24 KB
#7878 EnrichedLog netcoreapp3.1 6.83μs 22.2ns 86.1ns 0 0 0 2.26 KB
#7878 EnrichedLog net472 7.69μs 8.3ns 32.2ns 0.309 0 0 2.05 KB
Benchmarks.Trace.RedisBenchmark - Same speed ✔️ Same allocations ✔️

Raw results

Branch Method Toolchain Mean StdError StdDev Gen 0 Gen 1 Gen 2 Allocated
master SendReceive net6.0 2μs 0.614ns 2.38ns 0 0 0 1.18 KB
master SendReceive netcoreapp3.1 2.65μs 12.8ns 54.2ns 0 0 0 1.19 KB
master SendReceive net472 2.95μs 2.31ns 8.94ns 0.177 0 0 1.17 KB
#7878 SendReceive net6.0 2μs 7.96ns 30.8ns 0 0 0 1.18 KB
#7878 SendReceive netcoreapp3.1 2.78μs 5.4ns 20.9ns 0 0 0 1.19 KB
#7878 SendReceive net472 3.05μs 7.14ns 27.6ns 0.179 0 0 1.17 KB
Benchmarks.Trace.SerilogBenchmark - Same speed ✔️ Same allocations ✔️

Raw results

Branch Method Toolchain Mean StdError StdDev Gen 0 Gen 1 Gen 2 Allocated
master EnrichedLog net6.0 4.34μs 4.31ns 16.7ns 0 0 0 1.58 KB
master EnrichedLog netcoreapp3.1 5.44μs 16.8ns 65.1ns 0 0 0 1.63 KB
master EnrichedLog net472 6.59μs 11.4ns 44ns 0.294 0 0 2.03 KB
#7878 EnrichedLog net6.0 4.59μs 9.11ns 34.1ns 0 0 0 1.58 KB
#7878 EnrichedLog netcoreapp3.1 5.75μs 16.8ns 65.1ns 0 0 0 1.63 KB
#7878 EnrichedLog net472 6.6μs 9.89ns 37ns 0.294 0 0 2.03 KB
Benchmarks.Trace.SpanBenchmark - Same speed ✔️ Same allocations ✔️

Raw results

Branch Method Toolchain Mean StdError StdDev Gen 0 Gen 1 Gen 2 Allocated
master StartFinishSpan net6.0 798ns 4.05ns 17.7ns 0 0 0 576 B
master StartFinishSpan netcoreapp3.1 975ns 5.14ns 21.8ns 0 0 0 576 B
master StartFinishSpan net472 902ns 0.0976ns 0.365ns 0.0907 0 0 578 B
master StartFinishScope net6.0 946ns 3.67ns 13.7ns 0 0 0 696 B
master StartFinishScope netcoreapp3.1 1.16μs 5.79ns 24.6ns 0 0 0 696 B
master StartFinishScope net472 1.12μs 0.553ns 2.14ns 0.101 0 0 658 B
master StartFinishTwoScopes net6.0 1.78μs 2.92ns 11.3ns 0 0 0 1.19 KB
master StartFinishTwoScopes netcoreapp3.1 2.19μs 11.3ns 55.4ns 0 0 0 1.19 KB
master StartFinishTwoScopes net472 2.17μs 2.55ns 9.87ns 0.162 0 0 1.08 KB
#7878 StartFinishSpan net6.0 785ns 3.85ns 15.4ns 0 0 0 576 B
#7878 StartFinishSpan netcoreapp3.1 964ns 4.77ns 20.8ns 0 0 0 576 B
#7878 StartFinishSpan net472 908ns 2.38ns 9.22ns 0.0899 0 0 578 B
#7878 StartFinishScope net6.0 950ns 4.83ns 23.1ns 0 0 0 696 B
#7878 StartFinishScope netcoreapp3.1 1.14μs 5.94ns 29.1ns 0 0 0 697 B
#7878 StartFinishScope net472 1.13μs 2.9ns 11.2ns 0.102 0 0 658 B
#7878 StartFinishTwoScopes net6.0 1.74μs 8.81ns 40.4ns 0 0 0 1.19 KB
#7878 StartFinishTwoScopes netcoreapp3.1 2.19μs 10.9ns 46.2ns 0 0 0 1.19 KB
#7878 StartFinishTwoScopes net472 2.14μs 1.79ns 6.92ns 0.16 0 0 1.08 KB
Benchmarks.Trace.TraceAnnotationsBenchmark - Same speed ✔️ Same allocations ✔️

Raw results

Branch Method Toolchain Mean StdError StdDev Gen 0 Gen 1 Gen 2 Allocated
master RunOnMethodBegin net6.0 1.12μs 5.98ns 31.6ns 0 0 0 696 B
master RunOnMethodBegin netcoreapp3.1 1.42μs 7.24ns 33.2ns 0 0 0 696 B
master RunOnMethodBegin net472 1.49μs 3.84ns 14.9ns 0.098 0 0 658 B
#7878 RunOnMethodBegin net6.0 1.07μs 4.88ns 18.9ns 0 0 0 696 B
#7878 RunOnMethodBegin netcoreapp3.1 1.44μs 1.73ns 6.48ns 0 0 0 696 B
#7878 RunOnMethodBegin net472 1.49μs 5.09ns 19.7ns 0.0979 0 0 658 B

Copy link
Member

@lucaspimentel lucaspimentel left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice

@andrewlock andrewlock force-pushed the andrew/sealed-analyzer branch from 222d5ac to 6f7e4ff Compare December 2, 2025 07:57
Copy link
Collaborator

@bouwkast bouwkast left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

@andrewlock andrewlock merged commit bd65991 into master Dec 8, 2025
151 checks passed
@andrewlock andrewlock deleted the andrew/sealed-analyzer branch December 8, 2025 17:49
@github-actions github-actions bot added this to the vNext-v3 milestone Dec 8, 2025
andrewlock added a commit that referenced this pull request Dec 9, 2025
## Summary of changes

- Upgrades the `SealedAnalyzer` added in #7878 from `info` to `warning`
for specific projects
- Fix all the errors it flagged

## Reason for change

As described in `#7878`, making types `sealed` can have performance
benefits. It's basically a free small perf bump, and there's not
_really_ any downsides.

## Implementation details

After writing the analyzer, I got claude code to fix all the violations.
Annoyingly, `dotnet format` doesn't seem to work with this analyzer
because it's a "compilation end" analyzer.

Then I went through all the changes and confirmed they're valid. I also
changed a bunch to `static` where they were stateless and effectively
_should_ have been static.

> I initially made all the integrations and aspects `static` instead of
`sealed`, but then realised the integration are used as generic
arguments, and I suspect that's not allowed at runtime

I have written some comments on the PR for places where we had to
explicitly ignore, but GitHub can't handle it, so here's some others 😅

- `Tracer` - had to not marked this sealed, because we derive from it in
tests 🙁 This one is very annoying, I may try to fix in a subsequent PR.
- `DirectSubmissionLogSink` - we were deriving from this in tests so
that we could call a protected method, so just added a way to call that
instead.
- `PerformanceCountersListener` - we derive from this in tests.

## Test coverage

As long as everything passes, we should be good 🤞 

## Other details

Stacked on
- #7878

https://datadoghq.atlassian.net/browse/LANGPLAT-813
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants