Description
See the following benchmark, which compares the performance of instantiating and passing a ValueTuple<int, int>, ValueTuple<int, int, int>, and two structs which are functionally equivalent to ValueTuple.
publicclassTest{privateintsum=0;[Benchmark]publicintValueTuple2(){sum=0;for(inti=0;i<100;i++){M(newValueTuple<int,int>(i,i));}returnsum;}[Benchmark]publicintValueTuple3(){sum=0;for(inti=0;i<100;i++){M(newValueTuple<int,int,int>(i,i,i));}returnsum;}[Benchmark]publicintValueTupleIsh2(){sum=0;for(inti=0;i<100;i++){M(newValueTupleIsh<int,int>(i,i));}returnsum;}[Benchmark]publicintValueTupleIsh3(){sum=0;for(inti=0;i<100;i++){M(newValueTupleIsh<int,int,int>(i,i,i));}returnsum;}publicvoidM((int,int)s){sum+=s.Item1+s.Item2;}publicvoidM((int,int,int)s){sum+=s.Item1+s.Item2;}publicvoidM(ValueTupleIsh<int,int>s){sum+=s.Item1+s.Item2;}publicvoidM(ValueTupleIsh<int,int,int>s){sum+=s.Item1+s.Item2;}}publicstructValueTupleIsh<T1,T2>{publicT1Item1;publicT2Item2;publicValueTupleIsh(T1item1,T2item2){Item1=item1;Item2=item2;}}publicstructValueTupleIsh<T1,T2,T3>{publicT1Item1;publicT2Item2;publicT3Item3;publicValueTupleIsh(T1item1,T2item2,T3item3){Item1=item1;Item2=item2;Item3=item3;}}Data
This gives the following:
BenchmarkDotNet v0.13.6, Windows 11 (10.0.22621.1555/22H2/2022Update/SunValley2)
11th Gen Intel Core i7-11850H 2.50GHz, 1 CPU, 16 logical and 8 physical cores
.NET SDK 7.0.202
[Host] : .NET 6.0.15 (6.0.1523.11507), X64 RyuJIT AVX2
DefaultJob : .NET 6.0.15 (6.0.1523.11507), X64 RyuJIT AVX2
| Method | Mean | Error | StdDev |
|---|
| ValueTuple2 | 113.4 ns | 1.45 ns | 1.28 ns |
| ValueTuple3 | 661.5 ns | 7.37 ns | 6.90 ns |
| ValueTupleIsh2 | 112.0 ns | 1.22 ns | 1.08 ns |
| ValueTupleIsh3 | 111.5 ns | 1.03 ns | 0.86 ns |
As you can see, instantiating/passing a ValueTuple<int, int, int> is significantly slower than a ValueTuple<int, int>. This change is not seen with an equivalent struct.
Analysis
From SharpLab, we see:
Test.ValueTuple2() L0000: xoreax,eax L0002: mov[rcx+8],eax L0005: movedx,eax L0007: addedx,[rcx+8] L000a: addedx,eax L000c: mov[rcx+8],edx L000f: inceax L0011: cmpeax,0x64 L0014: jl short L0005 L0016: moveax,[rcx+8] L0019: retTest.ValueTuple3() L0000: subrsp,0x28 L0004: vzeroupper L0007: xoreax,eax L0009: mov[rcx+8],eax L000c: nop[rax] L0010: vxorps xmm0,xmm0,xmm0 L0014: vmovupd[rsp+0x18],xmm0 L001a: mov[rsp+0x18],eax L001e: mov[rsp+0x1c],eax L0022: mov[rsp+0x20],eax L0026: vmovupdxmm0,[rsp+0x18] L002c: vmovupd[rsp+8],xmm0 L0032: movedx,[rcx+8] L0035: addedx,[rsp+8] L0039: addedx,[rsp+0xc] L003d: mov[rcx+8],edx L0040: inceax L0042: cmpeax,0x64 L0045: jl short L0010 L0047: moveax,[rcx+8] L004a: addrsp,0x28 L004e: retTest.ValueTupleIsh2() L0000: xoreax,eax L0002: mov[rcx+8],eax L0005: movedx,eax L0007: addedx,[rcx+8] L000a: addedx,eax L000c: mov[rcx+8],edx L000f: inceax L0011: cmpeax,0x64 L0014: jl short L0005 L0016: moveax,[rcx+8] L0019: retTest.ValueTupleIsh3() L0000: xoreax,eax L0002: mov[rcx+8],eax L0005: movedx,eax L0007: addedx,[rcx+8] L000a: addedx,eax L000c: mov[rcx+8],edx L000f: inceax L0011: cmpeax,0x64 L0014: jl short L0005 L0016: moveax,[rcx+8] L0019: ret
I'm not sure why the runtime has significantly different codegen for the ValueTuple<int, int, int> case, but it doesn't seem to be helping!
Interestingly, the difference disappears if double is used instead of int as all generic type parameters.
Regression?
Unsure
Description
See the following benchmark, which compares the performance of instantiating and passing a
ValueTuple<int, int>,ValueTuple<int, int, int>, and two structs which are functionally equivalent toValueTuple.Data
This gives the following:
As you can see, instantiating/passing a
ValueTuple<int, int, int>is significantly slower than aValueTuple<int, int>. This change is not seen with an equivalent struct.Analysis
From SharpLab, we see:
I'm not sure why the runtime has significantly different codegen for the
ValueTuple<int, int, int>case, but it doesn't seem to be helping!Interestingly, the difference disappears if
doubleis used instead ofintas all generic type parameters.Regression?
Unsure