When I was conducting the following benchmark test, the benchmark information indicated that there was no additional memory allocation. However, doesn't converting a struct to an interface involve boxing?
[MemoryDiagnoser]
public class Test
{
[Benchmark]
public Color Test123()
{
Color color = new Color();
IFormattable formattable = color;
return (Color)formattable;
}
[Benchmark]
public int Test234()
{
int number = 114514;
IConvertible convertible = number;
return (int)convertible;
}
}
[StructLayout(LayoutKind.Sequential)]
public struct Color : IFormattable
{
public double R { get; set; }
public double G { get; set; }
public double B { get; set; }
public double A { get; set; }
public static Color GetRandomColor()
{
Random rand = new Random();
double r = rand.Next(0, 256) * rand.NextDouble();
double g = rand.Next(0, 256) * rand.NextDouble();
double b = rand.Next(0, 256) * rand.NextDouble();
double a = rand.Next(0, 256) * rand.NextDouble();
return new Color(r, g, b, a);
}
public string ToString(string? format, IFormatProvider? formatProvider)
{
return $"{R} {G} {B} {A}";
}
public Color(double r, double g, double b, double a)
{
R = r;
G = g;
B = b;
A = a;
}
public static bool operator ==(Color left, Color right)
{
return left.R == right.R && left.G == right.G && left.B == right.B && left.A == right.A;
}
public static bool operator !=(Color left, Color right)
{
return !(left == right);
}
}
.NET 8
The Color struct consists of only four double members: RGBA.
"I tried to open Rider to analyze the IL code and found that there are indeed boxing and unboxing instructions. I would like to understand why there is no additional memory allocation."
.method public hidebysig static valuetype Program.Color
Test123() cil managed
{
.maxstack 1
.locals init (
[0] valuetype Program.Color color,
[1] class [System.Runtime]System.IFormattable formattable,
[2] valuetype Program.Color V_2
)
// [28 9 - 28 10]
IL_0000: nop
// [29 13 - 29 39]
IL_0001: ldloca.s color
IL_0003: initobj Program.Color
// [30 13 - 30 46]
IL_0009: ldloc.0 // color
IL_000a: box Program.Color
IL_000f: stloc.1 // formattable
// [31 13 - 31 39]
IL_0010: ldloc.1 // formattable
IL_0011: unbox.any Program.Color
IL_0016: stloc.2 // V_2
IL_0017: br.s IL_0019
// [32 9 - 32 10]
IL_0019: ldloc.2 // V_2
IL_001a: ret
} // end of method Program::Test123
The JIT compiler is smart enough to remove the formattable
gymnastics and essentially return just the color
If you change the method to actually return IFormattable
, you will have allocations:
public IFormattable Test123() {
Color color = new Color();
IFormattable formattable = color;
return formattable;
}