Немного более понятно, что происходит, если вы посмотрите на сборку JITted из ваших методов:
Program.Activated()
L0000: sub rsp, 0x18
L0004: xor eax, eax // Initialize Structure to {0}
L0006: mov [rsp+0x10], rax // Store to stack
L000b: mov eax, 0x64 // Load literal 100
L0010: mov edx, 0x1 // Load literal 1
L0015: xor ecx, ecx // Initialize SomeValue to {0}
L0017: mov [rsp+0x8], rcx // Store to stack
L001c: lea rcx, [rsp+0x8] // Load pointer to SomeValue from stack
L0021: mov [rcx], dl // Set SomeValue.HasValue to 1
L0023: mov [rcx+0x4], eax // Set SomeValue.Value to 100
L0026: mov rax, [rsp+0x8] // Load SomeValue's value from stack
L002b: mov [rsp+0x10], rax // Store it to a different location on stack
L0030: mov rax, [rsp+0x10] // Return it from that location
L0035: add rsp, 0x18
L0039: ret
Program.ActivatedAssignment()
L0000: push rax
L0001: xor eax, eax // Initialize SomeValue to {0}
L0003: mov [rsp], rax // Store to stack
L0007: mov eax, 0x64 // Load literal 100
L000c: mov edx, 0x1 // Load literal 1
L0011: lea rcx, [rsp] // Load pointer to SomeValue from stack
L0015: mov [rcx], dl // Set SomeValue.HasValue to 1
L0017: mov [rcx+0x4], eax // Set SomeValue.Value to 100
L001a: mov rax, [rsp] // Return SomeValue
L001e: add rsp, 0x8
L0022: ret
Очевидно, Activated()
выполняет больше работы, и поэтому медленнее.То, к чему это сводится, - много перетасовки стека (все ссылки на rsp
).Я прокомментировал их как мог, но метод Activated()
немного запутан из-за избыточных mov
s.ActivatedAssigment()
гораздо проще.
В конечном счете, вы фактически не экономите место в стеке, опуская локальную переменную.Переменная должна существовать в какой-то момент независимо от того, даете вы ей имя или нет.Вставленный вами IL-код показывает локальную переменную (они называют ее V_0
), которая представляет собой временную переменную, созданную компилятором C #, поскольку вы не создали ее явно.
Если эти два отличия заключаются в том, что версия спеременная temp резервирует только один слот стека (.maxstack 1
) и использует его как для Nullable<T>
, так и для Structure
, следовательно, для перемешивания.В версии с указанной переменной она резервирует 2 слота (.maxstack 2
).
По иронии судьбы, в версии с предварительно зарезервированной локальной переменной для selection
JIT может устранить внешнюю структуру.и иметь дело только со встроенным Nullable<T>
, что делает код чище / быстрее.
Я не уверен, что вы можете извлечь какие-либо рекомендации из этого примера, но я думаю, что достаточно легко увидеть, что компилятор C #является источником разницы перф.JIT достаточно умен, чтобы делать правильные вещи с вашей структурой, но только если он выглядит определенным образом.