Я вже знав, що встановлення поля набагато повільніше, ніж налаштування локальної змінної, але також з'являється налаштування поля з локальна змінна значно повільніше, ніж встановлення локальної змінної з полем. Чому це? У будь-якому випадку використовується адреса поля.
public class Test
{
public int A = 0;
public int B = 4;
public void Method1() // Set local with field
{
int a = A;
for (int i = 0; i < 100; i++)
{
a += B;
}
A = a;
}
public void Method2() // Set field with local
{
int b = B;
for (int i = 0; i < 100; i++)
{
A += b;
}
}
}
Результати тесту з ітераціями 10e + 6:
Метод 1: 28.1321 мс Спосіб 2: 162.4528 мс
Відповіді:
15 для відповіді № 1Запускаючи це на моїй машині, я отримую аналогічні відмінності часу, однак, дивлячись на JITted код для 10-метрової ітерації, ясно, зрозуміло, чому це так:
Метод A:
mov r8,rcx
; "A" is loaded into eax
mov eax,dword ptr [r8+8]
xor edx,edx
; "B" is loaded into ecx
mov ecx,dword ptr [r8+0Ch]
nop dword ptr [rax]
loop_start:
; Partially unrolled loop, all additions done in registers
add eax,ecx
add eax,ecx
add eax,ecx
add eax,ecx
add edx,4
cmp edx,989680h
jl loop_start
; Store the sum in eax back to "A"
mov dword ptr [r8+8],eax
ret
І метод Б:
; "B" is loaded into edx
mov edx,dword ptr [rcx+0Ch]
xor r8d,r8d
nop word ptr [rax+rax]
loop_start:
; Partially unrolled loop, but each iteration requires reading "A" from memory
; adding "B" to it, and then writing the new "A" back to memory.
mov eax,dword ptr [rcx+8]
add eax,edx
mov dword ptr [rcx+8],eax
mov eax,dword ptr [rcx+8]
add eax,edx
mov dword ptr [rcx+8],eax
mov eax,dword ptr [rcx+8]
add eax,edx
mov dword ptr [rcx+8],eax
mov eax,dword ptr [rcx+8]
add eax,edx
mov dword ptr [rcx+8],eax
add r8d,4
cmp r8d,989680h
jl loop_start
rep ret
Як ви можете бачити з монтажу, метод A єбуде значно швидшим, оскільки значення A і B обидва ставляться до регістрів, і всі додатки відбуваються там без проміжних записів до пам'яті. Спосіб B, з іншого боку, набуває навантаження і зберігає до "A" в пам'яті для кожна окрема ітерація.
2 для відповіді № 2
У випадку 1 a
явно зберігається в реєстрі. Все інше було б жахливим результатом компіляції.
Можливо, .NET JIT не бажає / не може конвертувати магазини в A
зареєструвати магазини у випадку 2.
Я сумніваюсь, що це примушує модель пам'яті .NET, тому що інші теми ніколи не зможуть визначити різницю між вашими двома способами, якщо вони лише спостерігатимуть A
бути 0 або сумою. Вони не можуть спростувати теорію про те, що оптимізації ніколи не було. Це дозволяє це зробити під семантикою .NET абстрактної машини.
Не дивно, що .NET JIT виконує невелику оптимізацію. Це добре відомо послідовникам performance
тег на переповненні стека.
З досвіду я знаю, що JIT набагато частіше завантажує кеш-пам'ять в регістрах. Ось чому справа 1 (очевидно) не доступна B
з кожною ітерацією.
Регіональні обчислення дешевше, ніж доступ до пам'яті. Це навіть вірно, якщо згадана пам'ять знаходиться в кеші CPU L1 (як у випадку з цим).
Я думав, що тільки місцеві жителі мають право на кешування процесорів?
Це не може бути так, тому що процесор навіть не знає, що таке місцеві. Всі адреси виглядають однаково.
-2 для відповіді № 3
method2: поле читається ~ 100x і встановлюється ~ 100x занадто = 200x larg_0 (це) + 100x ldfld (поле навантаження) + 100x stfld (поле встановленого) + 100x ldloc (локальний)
method1: поле читається 100x, але не встановлено це еквівалентно методу 1 мінус 100x ldarg_0 (це)