Ответ 1
Существует разница в сгенерированном собственном коде для методов safeAbsSlow
и safeAbsFast
.
safeAbsSlow
(C2, уровень 4):
0x0000023d12ec4b14: add eax,ecx
0x0000023d12ec4b16: inc ebx
0x0000023d12ec4b18: cmp ebx,989680h
0x0000023d12ec4b1e: jnl 23d12ec4b4eh ; jump if 'ebx' was not less than '10_000_000'
0x0000023d12ec4b20: mov ecx,dword ptr [r9+rbx*4+10h]
0x0000023d12ec4b25: test ecx,ecx
0x0000023d12ec4b27: jnl 23d12ec4b14h ; jump if 'ecx' was not less-than '0'
0x0000023d12ec4b29: neg ecx
0x0000023d12ec4b2b: test ecx,ecx
0x0000023d12ec4b2d: jnl 23d12ec4b14h ; jump if 'ecx' was not less-than '0'
safeAbsFast
(C2, уровень 4):
0x000001d89e8a4b20: mov ecx,dword ptr [r9+rdi*4+10h]
0x000001d89e8a4b25: cmp ecx,80000000h
0x000001d89e8a4b2b: je 1d89e8a4b66h ; jump if 'ecx' was equal to '2147483648'
0x000001d89e8a4b2d: mov r11d,ecx
0x000001d89e8a4b30: neg r11d
0x000001d89e8a4b33: test ecx,ecx
0x000001d89e8a4b35: cmovl ecx,r11d
0x000001d89e8a4b39: add eax,ecx
0x000001d89e8a4b3b: inc edi
0x000001d89e8a4b3d: cmp edi,989680h
0x000001d89e8a4b43: jl 1d89e8a4b20h ; jump if 'edi' was less than '10_000_000'
Как видно из вышесказанного, safeAbsSlow
имеет больше условных переходов, чем safeAbsFast
.
Это связано, в частности, с тем, что реализация Math.abs
, встроенная в safeAbsFast
, не имеет условных переходов:
0x000001d89e8a4b2d: mov r11d,ecx
0x000001d89e8a4b30: neg r11d
0x000001d89e8a4b33: test ecx,ecx
0x000001d89e8a4b35: cmovl ecx,r11d
В результате в версии slow
происходит гораздо больше промахов по сравнению с версией normal
, когда набор данных имеет как положительные, так и отрицательные значения, которые разбросаны по массиву. Ниже приведена соответствующая статистика, собранная с помощью профилировщика Linux perf
:
Benchmark Mode Cnt Score Error Units
safeAbsFast avgt 10 9611659.726 ± 1429082.431 ns/op
safeAbsFast:branch-misses avgt 2869.853 #/op
safeAbsFast:branches avgt 12492918.020 #/op
safeAbsFast:cycles avgt 28212203.936 #/op
safeAbsFast:instructions avgt 92352048.153 #/op
safeAbsSlow avgt 10 44524180.366 ± 6324887.086 ns/op
safeAbsSlow:branch-misses avgt 5006493.144 #/op
safeAbsSlow:branches avgt 17496069.911 #/op
safeAbsSlow:cycles avgt 126413171.674 #/op
safeAbsSlow:instructions avgt 67549877.558 #/op
Напротив, вот результат для отсортированного набора данных:
Benchmark Mode Cnt Score Error Units
safeAbsFast avgt 10 9026800.584 ± 528992.157 ns/op
safeAbsFast:branch-misses avgt 2785.463 #/op
safeAbsFast:branches avgt 12474751.905 #/op
safeAbsFast:cycles avgt 27379727.603 #/op
safeAbsFast:instructions avgt 92418075.715 #/op
safeAbsSlow avgt 10 6981828.374 ± 2375480.834 ns/op
safeAbsSlow:branch-misses avgt 2801.022 #/op
safeAbsSlow:branches avgt 17496585.992 #/op
safeAbsSlow:cycles avgt 19478382.113 #/op
safeAbsSlow:instructions avgt 67589946.278 #/op
Предыдущая версия slow
становится еще быстрее, когда сортируется набор данных (в этом случае дорогостоящие потери в ветвях сводятся к минимуму).
Окружающая среда:
openjdk version "12-internal" 2019-03-19
OpenJDK Runtime Environment (slowdebug build 12-internal+0-adhoc.jdk12)
OpenJDK 64-Bit Server VM (slowdebug build 12-internal+0-adhoc.jdk12, mixed mode)