Обнаружил сильную зависимость скорости работы одной вычислительной функции от того, на каком адресе она начинается. Локализовал проблему с помощью вставки NOP-ов в разные её места, выяснилось что корень всех странностей находится в этом цикле (подозреваю, что различие в скорости работы тоже локализовано в нём, хотя как это точно проверить не знаю):
00000000000016b9 <.bL11>:
16b9: 48 8b 04 ce mov rax,QWORD PTR [rsi+rcx*8]
16bd: 48 19 04 cf sbb QWORD PTR [rdi+rcx*8],rax
16c1: 48 ff c1 inc rcx
16c4: 75 f3 jne 16b9 <.bL11>
16c6: 48 83 1f 00 sbb QWORD PTR [rdi],0x0
00000000000016ba <.bL11>:
16ba: 48 8b 04 ce mov rax,QWORD PTR [rsi+rcx*8]
16be: 48 19 04 cf sbb QWORD PTR [rdi+rcx*8],rax
16c2: 48 ff c1 inc rcx
16c5: 75 f3 jne 16ba <.bL11>
16c7: 48 83 1f 00 sbb QWORD PTR [rdi],0x0
00000000000016c5 <.bL11>:
16c5: 48 8b 04 ce mov rax,QWORD PTR [rsi+rcx*8]
16c9: 48 19 04 cf sbb QWORD PTR [rdi+rcx*8],rax
16cd: 48 ff c1 inc rcx
16d0: 75 f3 jne 16c5 <.bL11>
16d2: 48 83 1f 00 sbb QWORD PTR [rdi],0x0
Собственно, если отметить первый лагающий вариант (где .bL11=0x16BA) за точку отсчёта, то итоги такие:
-7..-1 - хорошая скорость
0..11 - замедление на 30-35%
12..15 - хорошая скорость
16..23 - замедление на 15-20%
24..31 - хорошая скорость
32..32+11 - замедление на 30-35%
32+12..32+31 - хорошая скорость
Ещё я проверил что будет если вставлять NOP-ы внутрь цикла. Так вот, добавление например 2 NOP-ов приводит к тому, что лаги начинаются на 2 байта раньше (т.е. при той же позиции JNE как и раньше начинались), а вот заканчиваются на том же месте как раньше, т.е. при той же позиции метки (т.е. лаги от -2 до 11). Вторая зона лагов стала какой-то размазанной с усилившимся пиком в самом начале, а так же она появилась таки во втором цикле на 32+16-2.
сдвиг время выполнения бенчмарка
-6 511.9 +- 14.8
-5 515.2 +- 12.3
-4 511.6 +- 8.9
-3 507.3 +- 7.2
-2 683.0 +- 9.2 <-- первый круг
-1 672.1 +- 12.3
0 670.5 +- 8.6
1 674.8 +- 17.1
2 669.4 +- 7.4
3 672.5 +- 11.0
4 675.0 +- 9.1
5 669.9 +- 8.9
6 675.5 +- 8.8
7 679.1 +- 7.6
8 696.9 +- 23.6
9 691.6 +- 14.8
10 680.5 +- 9.5
11 684.0 +- 9.0
12 516.5 +- 7.9
13 517.2 +- 11.6
14 792.3 +- 36.8 <--
15 605.4 +- 37.5
16 575.8 +- 24.2
17 566.0 +- 16.0
18 555.8 +- 21.6
19 562.0 +- 14.5
20 555.7 +- 8.7
21 537.2 +- 10.7
22 534.4 +- 11.2
23 549.9 +- 14.6
24 522.1 +- 10.6
25 517.8 +- 9.5
26 515.5 +- 8.4
27 517.0 +- 4.2
28 512.2 +- 7.4
29 516.8 +- 8.6
30 680.3 +- 9.2 <-- второй круг
31 675.0 +- 19.7
32 671.7 +- 8.9
33 669.2 +- 9.1
34 668.3 +- 10.8
35 667.0 +- 9.6
36 669.0 +- 8.9
37 671.0 +- 9.5
38 670.6 +- 10.8
39 676.5 +- 10.1
40 681.0 +- 10.5
41 682.1 +- 12.7
42 693.5 +- 16.7
43 689.8 +- 13.5
44 512.5 +- 4.9
45 516.9 +- 7.1
46 685.5 +- 12.3 <--
47 515.2 +- 7.6
48 519.0 +- 8.7
49 513.2 +- 7.9
50 512.4 +- 6.2
51 510.8 +- 4.7
52 510.3 +- 7.3
53 507.4 +- 7.0
54 509.2 +- 6.9
Вообще мне казалось что для более быстрой работы кода надо выравнивать начала циклов по 16-байтным границам, но тут ничего подобного незаметно, все адреса не пойми какие. Есть замеченная закономерность: когда JNE доползает до адреса 0x16c5 - лаги начинаются, когда до него доползает и начало цикла - заканчиваются. Но JNE двухбайтовый, т.е. когда он на 0x16c4 то второй его байт на 0x16c5 и лагов ещё нет.
Есть ли шанс объяснить почему так? А то мне совсем не нравится что код рандомно теряет треть скорости на ровном месте, тут я заметил, где-то не замечу, да и на разных процах это может быть по-разному, что делать?








