Burst против IL2CPP: ОбобщениÑ
(Russian translation from English by Maxim Voloshin)
ОбобщениÑ(Generic), будь то переменные, методы, или функции – Ñто оÑобенноÑти Ñзыка, преподноÑÑщие Ñюрпризы в IL2CPP. Сможет ли Burst ÑправитьÑÑ Ñ Ñтим лучше? Давайте выÑÑним!
Получение
Ðачнем Ñ ÑÐ¾Ð·Ð´Ð°Ð½Ð¸Ñ Ñтруктуры, Ñодержащей кучу обобщений:
struct GenericStruct<T> { public T Field; public GenericStruct(T value) { Field = value; } public T Get() { return Field; } public void Set(T value) { Field = value; } public T Property { get { return Field; } set { Field = value; } } }
Теперь Ñоздадим задачу, ÐºÐ¾Ñ‚Ð¾Ñ€Ð°Ñ Ð¿Ð¾Ð»ÑƒÑ‡Ð°ÐµÑ‚ значение Field
разными ÑпоÑобами:
[BurstCompile] struct GetGenericFieldJob : IJob { public GenericStruct<int> GenericStruct; public NativeArray<int> Out; public void Execute() { Out[0] = GenericStruct.Field; } } [BurstCompile] struct GetGenericPropertyJob : IJob { public GenericStruct<int> GenericStruct; public NativeArray<int> Out; public void Execute() { Out[0] = GenericStruct.Property; } } [BurstCompile] struct GetGenericGetterJob : IJob { public GenericStruct<int> GenericStruct; public NativeArray<int> Out; public void Execute() { Out[0] = GenericStruct.Get(); } }
Откроем Burst инÑпектор, где можно увидеть дизаÑÑемблер Ð´Ð»Ñ x86:
; GetGenericFieldJob mov rax, qword ptr [rdi + 8] mov ecx, dword ptr [rdi] mov dword ptr [rax], ecx ; GetGenericPropertyJob mov rax, qword ptr [rdi + 8] mov ecx, dword ptr [rdi] mov dword ptr [rax], ecx ; GetGenericGetterJob mov rax, qword ptr [rdi + 8] mov ecx, dword ptr [rdi] mov dword ptr [rax], ecx
Ðе нужно быть ÑкÑпертом аÑÑемблера, чтобы понÑть, что код вÑех трех задач одинаков. Получение ÑвойÑтва и вызовы функций были вÑтроены(inline) Burst компилÑтором. ВыполнÑетÑÑ Ð¼Ð¸Ð½Ð¸Ð¼ÑƒÐ¼ работы Ð´Ð»Ñ Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð¸Ñ Ð¿Ð¾Ð»Ñ Ð¸ приÑÐ²Ð°Ð¸Ð²Ð°Ð½Ð¸Ñ ÐµÐ³Ð¾ Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ Ð¿ÐµÑ€Ð²Ð¾Ð¼Ñƒ Ñлементу NativeArray
.
Чтобы Ñравнить Ñ IL2CPP, Ñоздадим клаÑÑ Ð±ÐµÐ· задач Ñ Ñ‚Ð°ÐºÐ¸Ð¼ же функционалом:
static class TestClass { public static void GetGenericFieldExecute( GenericStruct<int> GenericStruct, NativeArray<int> Out) { Out[0] = GenericStruct.Field; } public static void GetGenericPropertyExecute( GenericStruct<int> GenericStruct, NativeArray<int> Out) { Out[0] = GenericStruct.Property; } public static void GetGenericGetterExecute( GenericStruct<int> GenericStruct, NativeArray<int> Out) { Out[0] = GenericStruct.Get(); } }
Теперь мы можем Ñделать билд Ð´Ð»Ñ macOS и поÑмотреть в файл /path/to/project/PROJECTNAME_macOS_BackUpThisFolder_ButDontShipItWithYourGame/il2cppOutput/Assembly-CSharp.cpp
, чтобы увидеть Ñгенерированный C++ код:
// System.Void TestClass::GetGenericFieldExecute(GenericStruct`1<System.Int32>,Unity.Collections.NativeArray`1<System.Int32>) IL2CPP_EXTERN_C IL2CPP_METHOD_ATTR void TestClass_GetGenericFieldExecute_m6952D3E5CCB83892F969587C08AB72ACBADDFD6D (GenericStruct_1_t586960761D48CE541F98C0978DFF0AA576257CEF ___GenericStruct0, NativeArray_1_tC6374EC584BF0D6DD4AD6FA0FD00C2C82F82CCAF ___Out1, const RuntimeMethod* method) { { GenericStruct_1_t586960761D48CE541F98C0978DFF0AA576257CEF L_0 = ___GenericStruct0; int32_t L_1 = L_0.get_Field_0(); IL2CPP_NATIVEARRAY_SET_ITEM(int32_t, ((NativeArray_1_tC6374EC584BF0D6DD4AD6FA0FD00C2C82F82CCAF *)(&___Out1))->___m_Buffer_0, 0, L_1); return; } } // System.Void TestClass::GetGenericPropertyExecute(GenericStruct`1<System.Int32>,Unity.Collections.NativeArray`1<System.Int32>) IL2CPP_EXTERN_C IL2CPP_METHOD_ATTR void TestClass_GetGenericPropertyExecute_m2538CFF06C784D53DAEF39EE28131EB3D91FB332 (GenericStruct_1_t586960761D48CE541F98C0978DFF0AA576257CEF ___GenericStruct0, NativeArray_1_tC6374EC584BF0D6DD4AD6FA0FD00C2C82F82CCAF ___Out1, const RuntimeMethod* method) { static bool s_Il2CppMethodInitialized; if (!s_Il2CppMethodInitialized) { il2cpp_codegen_initialize_method (TestClass_GetGenericPropertyExecute_m2538CFF06C784D53DAEF39EE28131EB3D91FB332_MetadataUsageId); s_Il2CppMethodInitialized = true; } { int32_t L_0 = GenericStruct_1_get_Property_m025A61EA6663E602B36E1871CEE2E9D31B211363((GenericStruct_1_t586960761D48CE541F98C0978DFF0AA576257CEF *)(&___GenericStruct0), /*hidden argument*/GenericStruct_1_get_Property_m025A61EA6663E602B36E1871CEE2E9D31B211363_RuntimeMethod_var); IL2CPP_NATIVEARRAY_SET_ITEM(int32_t, ((NativeArray_1_tC6374EC584BF0D6DD4AD6FA0FD00C2C82F82CCAF *)(&___Out1))->___m_Buffer_0, 0, L_0); return; } } // System.Void TestClass::GetGenericGetterExecute(GenericStruct`1<System.Int32>,Unity.Collections.NativeArray`1<System.Int32>) IL2CPP_EXTERN_C IL2CPP_METHOD_ATTR void TestClass_GetGenericGetterExecute_m93709231DB117528EA77615832D05009A1DF482C (GenericStruct_1_t586960761D48CE541F98C0978DFF0AA576257CEF ___GenericStruct0, NativeArray_1_tC6374EC584BF0D6DD4AD6FA0FD00C2C82F82CCAF ___Out1, const RuntimeMethod* method) { static bool s_Il2CppMethodInitialized; if (!s_Il2CppMethodInitialized) { il2cpp_codegen_initialize_method (TestClass_GetGenericGetterExecute_m93709231DB117528EA77615832D05009A1DF482C_MetadataUsageId); s_Il2CppMethodInitialized = true; } { int32_t L_0 = GenericStruct_1_Get_mAD6B652D2C750B814ABCCDE24D2D58C3166153C8((GenericStruct_1_t586960761D48CE541F98C0978DFF0AA576257CEF *)(&___GenericStruct0), /*hidden argument*/GenericStruct_1_Get_mAD6B652D2C750B814ABCCDE24D2D58C3166153C8_RuntimeMethod_var); IL2CPP_NATIVEARRAY_SET_ITEM(int32_t, ((NativeArray_1_tC6374EC584BF0D6DD4AD6FA0FD00C2C82F82CCAF *)(&___Out1))->___m_Buffer_0, 0, L_0); return; } }
Получение Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ Ð¿Ð¾Ð»Ñ Ñ€ÐµÐ°Ð»Ð¸Ð·Ð¾Ð²Ð°Ð½Ð¾ хорошо, но иÑпользование ÑвойÑтва или метода Ð´Ð»Ñ Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð¸Ñ Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ Ð²Ñ‹Ð¿Ð¾Ð»Ð½Ñет дополнительные дейÑÑ‚Ð²Ð¸Ñ Ð² блоке if
.
УÑтановка
Теперь попробуем обратное – уÑтановить значение. Мы можем Ñделать Ñто Ñ‡ÐµÑ‚Ñ‹Ñ€ÑŒÐ¼Ñ Ð¿ÑƒÑ‚Ñми вмеÑто трех:
[BurstCompile] struct SetGenericFieldJob : IJob { public int Value; public GenericStruct<int> In; public NativeArray<GenericStruct<int>> Out; public void Execute() { In.Field = Value; Out[0] = In; } } [BurstCompile] struct SetGenericPropertyJob : IJob { public int Value; public GenericStruct<int> In; public NativeArray<GenericStruct<int>> Out; public void Execute() { In.Property = Value; Out[0] = In; } } [BurstCompile] struct SetGenericSetterJob : IJob { public int Value; public GenericStruct<int> In; public NativeArray<GenericStruct<int>> Out; public void Execute() { In.Set(Value); Out[0] = In; } } [BurstCompile] struct SetGenericConstructorJob : IJob { public int Value; public NativeArray<GenericStruct<int>> Out; public void Execute() { Out[0] = new GenericStruct<int>(Value); } }
Теперь поÑмотрим во что Burst Ñкомпилировал Ñти задачи:
; SetGenericFieldJob mov rax, qword ptr [rdi + 8] mov ecx, dword ptr [rdi] mov dword ptr [rdi + 4], ecx mov dword ptr [rax], ecx ; SetGenericPropertyJob mov rax, qword ptr [rdi + 8] mov ecx, dword ptr [rdi] mov dword ptr [rdi + 4], ecx mov dword ptr [rax], ecx ; SetGenericSetterJob mov rax, qword ptr [rdi + 8] mov ecx, dword ptr [rdi] mov dword ptr [rdi + 4], ecx mov dword ptr [rax], ecx ; SetGenericConstructorJob mov rax, qword ptr [rdi + 8] mov ecx, dword ptr [rdi] mov dword ptr [rdi + 4], ecx mov dword ptr [rax], ecx
И Ñнова, вÑе четыре задачи идентичны. Ð’Ñе они также выполнÑÑŽÑ‚ минимальное количеÑтво работы. Ðам оÑталоÑÑŒ Ñравнить полученное Ñ Ð°Ð½Ð°Ð»Ð¾Ð³Ð¸Ñ‡Ð½Ñ‹Ð¼Ð¸ функциÑми, Ñкомпилированными IL2CPP:
static class TestClass { public static void SetGenericFieldExecute( int Value, GenericStruct<int> In, NativeArray<GenericStruct<int>> Out) { In.Field = Value; Out[0] = In; } public static void SetGenericPropertyExecute( int Value, GenericStruct<int> In, NativeArray<GenericStruct<int>> Out) { In.Property = Value; Out[0] = In; } public static void SetGenericSetterExecute( int Value, GenericStruct<int> In, NativeArray<GenericStruct<int>> Out) { In.Set(Value); Out[0] = In; } public static void SetGenericConstructorExecute( int Value, NativeArray<GenericStruct<int>> Out) { Out[0] = new GenericStruct<int>(Value); } }
Собрав проект, мы увидим данный IL2CPP вывод:
// System.Void TestClass::SetGenericFieldExecute(System.Int32,GenericStruct`1<System.Int32>,Unity.Collections.NativeArray`1<GenericStruct`1<System.Int32>>) IL2CPP_EXTERN_C IL2CPP_METHOD_ATTR void TestClass_SetGenericFieldExecute_mD42100D588CE77AA17664B21AB781458327839A4 (int32_t ___Value0, GenericStruct_1_t586960761D48CE541F98C0978DFF0AA576257CEF ___In1, NativeArray_1_t179A5BEE7FE0D41A82BAC0386A76EBCEF8243C18 ___Out2, const RuntimeMethod* method) { { int32_t L_0 = ___Value0; (&___In1)->set_Field_0(L_0); GenericStruct_1_t586960761D48CE541F98C0978DFF0AA576257CEF L_1 = ___In1; IL2CPP_NATIVEARRAY_SET_ITEM(GenericStruct_1_t586960761D48CE541F98C0978DFF0AA576257CEF , ((NativeArray_1_t179A5BEE7FE0D41A82BAC0386A76EBCEF8243C18 *)(&___Out2))->___m_Buffer_0, 0, L_1); return; } } // System.Void TestClass::SetGenericPropertyExecute(System.Int32,GenericStruct`1<System.Int32>,Unity.Collections.NativeArray`1<GenericStruct`1<System.Int32>>) IL2CPP_EXTERN_C IL2CPP_METHOD_ATTR void TestClass_SetGenericPropertyExecute_m5F84D9266EF36EF50950B19157342D07FE39434E (int32_t ___Value0, GenericStruct_1_t586960761D48CE541F98C0978DFF0AA576257CEF ___In1, NativeArray_1_t179A5BEE7FE0D41A82BAC0386A76EBCEF8243C18 ___Out2, const RuntimeMethod* method) { static bool s_Il2CppMethodInitialized; if (!s_Il2CppMethodInitialized) { il2cpp_codegen_initialize_method (TestClass_SetGenericPropertyExecute_m5F84D9266EF36EF50950B19157342D07FE39434E_MetadataUsageId); s_Il2CppMethodInitialized = true; } { int32_t L_0 = ___Value0; GenericStruct_1_set_Property_m8FA3575D200FDB113066D2B4A437863B28B930BB((GenericStruct_1_t586960761D48CE541F98C0978DFF0AA576257CEF *)(&___In1), L_0, /*hidden argument*/GenericStruct_1_set_Property_m8FA3575D200FDB113066D2B4A437863B28B930BB_RuntimeMethod_var); GenericStruct_1_t586960761D48CE541F98C0978DFF0AA576257CEF L_1 = ___In1; IL2CPP_NATIVEARRAY_SET_ITEM(GenericStruct_1_t586960761D48CE541F98C0978DFF0AA576257CEF , ((NativeArray_1_t179A5BEE7FE0D41A82BAC0386A76EBCEF8243C18 *)(&___Out2))->___m_Buffer_0, 0, L_1); return; } } // System.Void TestClass::SetGenericSetterExecute(System.Int32,GenericStruct`1<System.Int32>,Unity.Collections.NativeArray`1<GenericStruct`1<System.Int32>>) IL2CPP_EXTERN_C IL2CPP_METHOD_ATTR void TestClass_SetGenericSetterExecute_mBD904F1C296446416DFAB3C1429A1D590D9513BF (int32_t ___Value0, GenericStruct_1_t586960761D48CE541F98C0978DFF0AA576257CEF ___In1, NativeArray_1_t179A5BEE7FE0D41A82BAC0386A76EBCEF8243C18 ___Out2, const RuntimeMethod* method) { static bool s_Il2CppMethodInitialized; if (!s_Il2CppMethodInitialized) { il2cpp_codegen_initialize_method (TestClass_SetGenericSetterExecute_mBD904F1C296446416DFAB3C1429A1D590D9513BF_MetadataUsageId); s_Il2CppMethodInitialized = true; } { int32_t L_0 = ___Value0; GenericStruct_1_Set_m46E487E73FE7B9851EEBF7F5E0747E863311C652((GenericStruct_1_t586960761D48CE541F98C0978DFF0AA576257CEF *)(&___In1), L_0, /*hidden argument*/GenericStruct_1_Set_m46E487E73FE7B9851EEBF7F5E0747E863311C652_RuntimeMethod_var); GenericStruct_1_t586960761D48CE541F98C0978DFF0AA576257CEF L_1 = ___In1; IL2CPP_NATIVEARRAY_SET_ITEM(GenericStruct_1_t586960761D48CE541F98C0978DFF0AA576257CEF , ((NativeArray_1_t179A5BEE7FE0D41A82BAC0386A76EBCEF8243C18 *)(&___Out2))->___m_Buffer_0, 0, L_1); return; } } // System.Void TestClass::SetGenericConstructorExecute(System.Int32,Unity.Collections.NativeArray`1<GenericStruct`1<System.Int32>>) IL2CPP_EXTERN_C IL2CPP_METHOD_ATTR void TestClass_SetGenericConstructorExecute_mB24CC718B7EFD961F29D7482165F2EAB67FA5005 (int32_t ___Value0, NativeArray_1_t179A5BEE7FE0D41A82BAC0386A76EBCEF8243C18 ___Out1, const RuntimeMethod* method) { static bool s_Il2CppMethodInitialized; if (!s_Il2CppMethodInitialized) { il2cpp_codegen_initialize_method (TestClass_SetGenericConstructorExecute_mB24CC718B7EFD961F29D7482165F2EAB67FA5005_MetadataUsageId); s_Il2CppMethodInitialized = true; } { int32_t L_0 = ___Value0; GenericStruct_1_t586960761D48CE541F98C0978DFF0AA576257CEF L_1; memset((&L_1), 0, sizeof(L_1)); GenericStruct_1__ctor_mF683E5E102C875280E885DC8E8C2808D8969FF6C((&L_1), L_0, /*hidden argument*/GenericStruct_1__ctor_mF683E5E102C875280E885DC8E8C2808D8969FF6C_RuntimeMethod_var); IL2CPP_NATIVEARRAY_SET_ITEM(GenericStruct_1_t586960761D48CE541F98C0978DFF0AA576257CEF , ((NativeArray_1_t179A5BEE7FE0D41A82BAC0386A76EBCEF8243C18 *)(&___Out1))->___m_Buffer_0, 0, L_1); return; } }
ОпÑть-таки Ñгенерированный код неплох Ð´Ð»Ñ Ð¿Ñ€Ñмого доÑтупа к значению полÑ, но приÑутÑтвует оверхÑд в функциÑÑ…. ВерÑÐ¸Ñ Ñ ÐºÐ¾Ð½Ñтруктором включает ненужный вызов memset
Ð´Ð»Ñ Ð¾Ñ‡Ð¸Ñтки Ñтруктуры, но он будет удален компилÑтором C++, так что не Ñтоит об Ñтом беÑпокоитьÑÑ.
Вызов
Ðаконец, попробуем вызвать функции, ÑвлÑющиеÑÑ Ð¾Ð±Ð¾Ð±Ñ‰ÐµÐ½Ð¸Ñми. Ð”Ð»Ñ Ñтого нам понадобитÑÑ Ð½Ð¾Ð²Ð°Ñ Ñтруктура:
struct StructWithGenericFunctions { public T Choose<T>(bool chooseA, T a, T b) { return chooseA ? a : b; } public static T StaticChoose<T>(bool chooseA, T a, T b) { return chooseA ? a : b; } }
Далее, Ñоздадим неÑколько задач, вызывающих функции:
[BurstCompile] struct CallGenericFunctionJob : IJob { public bool ChooseA; public StructWithGenericFunctions In; public NativeArray<int> Out; public void Execute() { Out[0] = In.Choose(ChooseA, 123, 456); } } [BurstCompile] struct CallGenericStaticFunctionJob : IJob { public bool ChooseA; public NativeArray<int> Out; public void Execute() { Out[0] = StructWithGenericFunctions.StaticChoose(ChooseA, 123, 456); } }
Вот что можно увидеть в Burst инÑпекторе:
; CallGenericFunctionJob mov rax, qword ptr [rdi + 8] cmp byte ptr [rdi], 0 mov ecx, 456 mov edx, 123 cmove edx, ecx mov dword ptr [rax], edx ; CallGenericStaticFunctionJob mov rax, qword ptr [rdi + 8] cmp byte ptr [rdi], 0 mov ecx, 456 mov edx, 123 cmove edx, ecx mov dword ptr [rax], edx
Ð’ очередной раз, мы видим, что результат одинаков. ÐÑÑемблерный код проÑÑ‚ и, Ð¼Ð¸Ð½Ð¸Ð¼Ð°Ð»ÑŒÐ½Ð°Ñ Ð»Ð¾Ð³Ð¸ÐºÐ° ÑоответÑÑ‚Ð²ÑƒÑŽÑ‰Ð°Ñ Ñ‚Ð¾Ð¼Ñƒ, что мы напиÑали в функциÑÑ…, была вÑтроена в Execute
.
Теперь Ñоздадим верÑию без задач:
static class TestClass { public static void CallGenericFunctionExecute( bool ChooseA, StructWithGenericFunctions In, NativeArray<int> Out) { Out[0] = In.Choose(ChooseA, 123, 456); } public static void CallGenericStaticFunctionExecute( bool ChooseA, NativeArray<int> Out) { Out[0] = StructWithGenericFunctions.StaticChoose(ChooseA, 123, 456); } }
И ÑоответÑтвующий вывод IL2CPP:
// System.Void TestClass::CallGenericFunctionExecute(System.Boolean,StructWithGenericFunctions,Unity.Collections.NativeArray`1<System.Int32>) IL2CPP_EXTERN_C IL2CPP_METHOD_ATTR void TestClass_CallGenericFunctionExecute_mE8ADF05FE4FF1D43420309F386E6B3730717DDB6 (bool ___ChooseA0, StructWithGenericFunctions_t2D57CAAA3EC03C9B25DCF8FF096FA46341B018C2 ___In1, NativeArray_1_tC6374EC584BF0D6DD4AD6FA0FD00C2C82F82CCAF ___Out2, const RuntimeMethod* method) { static bool s_Il2CppMethodInitialized; if (!s_Il2CppMethodInitialized) { il2cpp_codegen_initialize_method (TestClass_CallGenericFunctionExecute_mE8ADF05FE4FF1D43420309F386E6B3730717DDB6_MetadataUsageId); s_Il2CppMethodInitialized = true; } { bool L_0 = ___ChooseA0; int32_t L_1 = StructWithGenericFunctions_Choose_TisInt32_t585191389E07734F19F3156FF88FB3EF4800D102_mD3914329B4BEAEF619DD440AF05817106E0FB6AD((StructWithGenericFunctions_t2D57CAAA3EC03C9B25DCF8FF096FA46341B018C2 *)(&___In1), L_0, ((int32_t)123), ((int32_t)456), /*hidden argument*/StructWithGenericFunctions_Choose_TisInt32_t585191389E07734F19F3156FF88FB3EF4800D102_mD3914329B4BEAEF619DD440AF05817106E0FB6AD_RuntimeMethod_var); IL2CPP_NATIVEARRAY_SET_ITEM(int32_t, ((NativeArray_1_tC6374EC584BF0D6DD4AD6FA0FD00C2C82F82CCAF *)(&___Out2))->___m_Buffer_0, 0, L_1); return; } } // System.Void TestClass::CallGenericStaticFunctionExecute(System.Boolean,Unity.Collections.NativeArray`1<System.Int32>) IL2CPP_EXTERN_C IL2CPP_METHOD_ATTR void TestClass_CallGenericStaticFunctionExecute_mBBB9E4295CDE01461BD2FA226375D71E573AC6AF (bool ___ChooseA0, NativeArray_1_tC6374EC584BF0D6DD4AD6FA0FD00C2C82F82CCAF ___Out1, const RuntimeMethod* method) { static bool s_Il2CppMethodInitialized; if (!s_Il2CppMethodInitialized) { il2cpp_codegen_initialize_method (TestClass_CallGenericStaticFunctionExecute_mBBB9E4295CDE01461BD2FA226375D71E573AC6AF_MetadataUsageId); s_Il2CppMethodInitialized = true; } { bool L_0 = ___ChooseA0; int32_t L_1 = StructWithGenericFunctions_StaticChoose_TisInt32_t585191389E07734F19F3156FF88FB3EF4800D102_m47B49B4671A00FF9AD6B95B4648FFC2B0CEAD665(L_0, ((int32_t)123), ((int32_t)456), /*hidden argument*/StructWithGenericFunctions_StaticChoose_TisInt32_t585191389E07734F19F3156FF88FB3EF4800D102_m47B49B4671A00FF9AD6B95B4648FFC2B0CEAD665_RuntimeMethod_var); IL2CPP_NATIVEARRAY_SET_ITEM(int32_t, ((NativeArray_1_tC6374EC584BF0D6DD4AD6FA0FD00C2C82F82CCAF *)(&___Out1))->___m_Buffer_0, 0, L_1); return; } }
Оба вызова имеют некоторый оверхÑд, но, Ñ Ð´Ñ€ÑƒÐ³Ð¾Ð¹ Ñтороны, Ñто очень проÑтые функции, которые, к ÑчаÑтью, будут вÑтроены C++ компилÑтором.
Заключение
Ð¡ÐµÐ³Ð¾Ð´Ð½Ñ Ð¼Ñ‹ увидели, что Burst обрабатывает различные C# Ð¾Ð±Ð¾Ð±Ñ‰ÐµÐ½Ð¸Ñ Ñ‚Ð¾Ñ‡Ð½Ð¾ также или лучше чем IL2CPP. Каждый раз, он генерирует минимальный аÑÑемблерный код без лишней логики. IL2CPP, Ñ Ð´Ñ€ÑƒÐ³Ð¾Ð¹ Ñтороны, чаÑто, но не вÑегда, вÑтавлÑет дополнительные if
в функциÑÑ…, иÑпользующих обобщениÑ. Иногда, оба компилÑтора генерируют одинаковый машинный код Ð´Ð»Ñ Ð¦ÐŸÐ£, но в большинÑтве Ñлучаев Burst генерирует меньший по объему и более быÑтрый код.