#c# #performance #.net-core #types #valuetuple
#c# #Производительность #.net-core #типы #valuetuple
Вопрос:
Если мы сделаем
var (hello, world) = GetHelloAndWorldStrings();
if (hello == "hello" amp;amp; world == "world")
Environment.Exit(0);
Влечет ли это за собой какие-либо дополнительные затраты, кроме простого выполнения следующего:
var helloAndWorld = GetHelloAndWorldStrings();
if (helloAndWorld.Hello == "hello" amp;amp; helloAndWorld.World == "world")
Environment.Exit(0);
Или это все синтаксический сахар — код, который в конечном итоге генерируется, всегда использует Item1 и Item2.
Комментарии:
2. Они всегда используют
Item1
иItem2
. Имена приведены только для удобства чтения и доступны только во время компиляции. IL показывает, что они используютItem1
иItem2
независимо от того, как вы их назвали (ValueTuple — это структура с полями, подобными любым другим структурам) — напримерvaluetype [System.Runtime]System.ValueTuple``2<int32, int32>::Item1
3. Снижения производительности нет, сгенерированный код идентичен. Вы также можете использовать
if (helloAndWorld == ("hello","world"))
Ответ №1:
По сути, это один и тот же сгенерированный IL, однако с незначительными выдаваемыми различиями… скорее всего, они будут jitted и оптимизированы для точно тех же инструкций.
Учитывая
private (String hello,string world) GetHelloAndWorldStrings()
{
return ("asdsd","sadfsdf");
}
...
public int Test1()
{
var asd = GetHelloAndWorldStrings();
if (asd.hello == "hello" amp;amp; asd.world == "world")
return 1;
return 0;
}
public int Test2()
{
var (hello, world) = GetHelloAndWorldStrings();
if (hello == "hello" amp;amp; world == "world")
return 1;
return 0;
}
В основном было бы выдано как
public int Test1()
{
ValueTuple<string, string> helloAndWorldStrings = GetHelloAndWorldStrings();
if (helloAndWorldStrings.Item1 == "hello" amp;amp; helloAndWorldStrings.Item2 == "world")
{
return 1;
}
return 0;
}
public int Test2()
{
ValueTuple<string, string> helloAndWorldStrings = GetHelloAndWorldStrings();
string item = helloAndWorldStrings.Item1;
string item2 = helloAndWorldStrings.Item2;
if (item == "hello" amp;amp; item2 == "world")
{
return 1;
}
return 0;
}
Вот пример JIT ASM в выпуске
C.Test1()
L0000: push ebp
L0001: mov ebp, esp
L0003: push esi
L0004: mov ecx, [0x11198648]
L000a: mov esi, [0x1119864c]
L0010: mov edx, [0x11198650]
L0016: call System.String.Equals(System.String, System.String)
L001b: test eax, eax
L001d: je short L0038
L001f: mov edx, [0x11198654]
L0025: mov ecx, esi
L0027: call System.String.Equals(System.String, System.String)
L002c: test eax, eax
L002e: je short L0038
L0030: mov eax, 1
L0035: pop esi
L0036: pop ebp
L0037: ret
L0038: xor eax, eax
L003a: pop esi
L003b: pop ebp
L003c: ret
против
C.Test2()
L0000: push ebp
L0001: mov ebp, esp
L0003: push esi
L0004: mov ecx, [0x11198648]
L000a: mov esi, [0x1119864c]
L0010: mov edx, [0x11198650]
L0016: call System.String.Equals(System.String, System.String)
L001b: test eax, eax
L001d: je short L0038
L001f: mov edx, [0x11198654]
L0025: mov ecx, esi
L0027: call System.String.Equals(System.String, System.String)
L002c: test eax, eax
L002e: je short L0038
L0030: mov eax, 1
L0035: pop esi
L0036: pop ebp
L0037: ret
L0038: xor eax, eax
L003a: pop esi
L003b: pop ebp
L003c: ret
Короче говоря, чистая выгода от беспокойства об этом … минус 5 минут вашей жизни, которые вы никогда не вернете
Комментарии:
1. Если вы добавите 2
int
свойства в набор кортежей (one
иtwo
), я думаю, разница станет более понятной, а JIT (JIT ASM) в release, наконец, покажет некоторые различия в сгенерированном коде (Test1
содержит меньше инструкций).2. @SpiritBob, вероятно, из-за копирования типа значения.
3.Да, по сравнению с простой передачей по ссылке. У меня глупый вопрос — как вы расшифровали то,
Test2
что действительно выдает копию структуры кортежа значений? (string item = helloAndWorldStrings.Item1;
string item2 = helloAndWorldStrings.Item1;
)
Ответ №2:
Есть третий вариант, эквивалентный первому:
public int Test3()
{
var asd = GetHelloAndWorldStrings();
if (asd == ("hello", "world"))
return 1;
return 0;
}
Это в равной степени переводится в (sharplab.io):
public int Test3()
{
ValueTuple<string, string> helloAndWorldStrings = GetHelloAndWorldStrings();
if (helloAndWorldStrings.Item1 == "hello" amp;amp; helloAndWorldStrings.Item2 == "world")
{
return 1;
}
return 0;
}
.method public hidebysig
instance int32 Test3 () cil managed
{
// Method begins at RVA 0x2064
// Code size 47 (0x2f)
.maxstack 2
.locals init (
[0] valuetype [System.Private.CoreLib]System.ValueTuple`2<string, string>
)
IL_0000: ldarg.0
IL_0001: call instance valuetype [System.Private.CoreLib]System.ValueTuple`2<string, string> C::GetHelloAndWorldStrings()
IL_0006: stloc.0
IL_0007: ldloc.0
IL_0008: ldfld !0 valuetype [System.Private.CoreLib]System.ValueTuple`2<string, string>::Item1
IL_000d: ldstr "hello"
IL_0012: call bool [System.Private.CoreLib]System.String::op_Equality(string, string)
IL_0017: brfalse.s IL_002d
IL_0019: ldloc.0
IL_001a: ldfld !1 valuetype [System.Private.CoreLib]System.ValueTuple`2<string, string>::Item2
IL_001f: ldstr "world"
IL_0024: call bool [System.Private.CoreLib]System.String::op_Equality(string, string)
IL_0029: brfalse.s IL_002d
IL_002b: ldc.i4.1
IL_002c: ret
IL_002d: ldc.i4.0
IL_002e: ret
} // end of method C::Test3