Анализ причины снижения производительности с другой версией ядра

Я столкнулся со странным снижением производительности ядра Linux с 3.11 по 3.12 в системах x86_64. Выполнение теста Radiance для Mark Stock на Fedora 20, 3.12 заметно медленнее. Больше ничего не меняется - идентичный двоичный файл, идентичный glibc - я просто загружаю другую версию ядра, и производительность меняется. Программа на время, rpict, представляет собой код пользовательского уровня, связанный с процессором, на 100%.

Прежде чем сообщить об этом как об ошибке, я бы хотел найти причину такого поведения. Я не знаю много о ядре Linux, и журнал изменений с 3.11 на 3.12 не дает мне никакой подсказки.

Я наблюдал это на двух системах: Intel Haswell (i7-4771) и AMD Richland (A8-6600K). На системе Haswell время пользователя изменилось с 895 сек с 3.11 до 962 с 3.12. На Ричленде с 1764 по 1844 год. Это время повторяется с точностью до нескольких секунд.

Я провел некоторое профилирование с помощью perf и обнаружил, что IPC снизился в той же пропорции, что и замедление. В системе Haswell это, по-видимому, вызвано большим количеством пропущенных ветвей, но почему скорость прогнозирования должна снижаться? Radiance использует генератор случайных чисел - может ли "лучшая" случайность вызывать пропущенные ветви? Но кроме поддержки OMAP4, RNG не должен казаться измененным в 3.12.

В системе AMD производительность просто указывает на увеличение числа циклов простоя бэкэнда, но причина не ясна.

Система Haswell:

3.11.10  895s user, 3.74% branch-misses, 1.65 insns per cycle
3.12.6   962s user, 4.22% branch-misses, 1.52 insns per cycle

Система Ричленда:

3.11.10  1764s user, 8.23% branch-misses, 0.75 insns per cycle
3.12.6   1844s user, 8.26% branch-misses, 0.72 insns per cycle

Я также посмотрел на diff из вывода dmesg обоих ядер, но не увидел ничего, что могло бы вызвать такое замедление работы программы, связанной с процессором.

Я попытался переключить регулятор cpufreq со стандартного ondemand на peformance, но это не имело никакого эффекта.

Исполняемый файл был скомпилирован с использованием gcc 4.7.3, но без инструкций AVX. libm все еще использует AVX (например, __ieee754_pow_fma4) но эти функции составляют всего 0,3% от общего времени выполнения.

Дополнительная информация:

Любые идеи (кроме деления ядра на изменения)?

2 ответа

Решение

Давайте проверим ваш perf stat выходы: http://www.chr-breitkopf.de/tmp/perf-stat.A8.txt

Ядро 3.11.10

    1805057.522096 task-clock                #    0.999 CPUs utilized          
           183,822 context-switches          #    0.102 K/sec                  
               109 cpu-migrations            #    0.000 K/sec                  
            40,451 page-faults               #    0.022 K/sec                  
 7,523,630,814,458 cycles                    #    4.168 GHz                     [83.31%]
   628,027,409,355 stalled-cycles-frontend   #    8.35% frontend cycles idle    [83.34%]
 2,688,621,128,444 stalled-cycles-backend    #   35.74% backend  cycles idle    [33.35%]
 5,607,337,995,118 instructions              #    0.75  insns per cycle        
                                             #    0.48  stalled cycles per insn [50.01%]
   825,679,208,404 branches                  #  457.425 M/sec                   [66.67%]
    67,984,693,354 branch-misses             #    8.23% of all branches         [83.33%]

    1806.804220050 seconds time elapsed

Ядро 3.12.6

    1875709.455321 task-clock                #    0.999 CPUs utilized          
           192,425 context-switches          #    0.103 K/sec                  
               133 cpu-migrations            #    0.000 K/sec                  
            40,356 page-faults               #    0.022 K/sec                  
 7,822,017,368,073 cycles                    #    4.170 GHz                     [83.31%]
   634,535,174,769 stalled-cycles-frontend   #    8.11% frontend cycles idle    [83.34%]
 2,949,638,742,734 stalled-cycles-backend    #   37.71% backend  cycles idle    [33.35%]
 5,607,926,276,713 instructions              #    0.72  insns per cycle        
                                             #    0.53  stalled cycles per insn [50.01%]
   825,760,510,232 branches                  #  440.239 M/sec                   [66.67%]
    68,205,868,246 branch-misses             #    8.26% of all branches         [83.33%]

    1877.263511002 seconds time elapsed

Для поля 3.12.6 в поле "циклы" добавлено почти 300 циклов; и только 6,5 Gcycles были киосками фронтэнда и 261 Gcycles были остановлены в бэкэнде. У вас есть только 0,2 G дополнительных пропусков веток (каждый стоит около 20 циклов - на страницу Optim.manual 597; так что 4Gcycles), поэтому я думаю, что ваши проблемы с производительностью связаны с проблемами подсистемы памяти (более реалистичное внутреннее событие, которое может быть под влиянием ядра). Различия между числами страниц и количеством миграций низки, и я думаю, что они не будут напрямую тормозить тестирование (но миграции могут переместить программу в худшее место).

Вы должны идти глубже в perf счетчики, чтобы найти точный тип проблемы (это будет проще, если у вас есть более короткие прогоны теста). Руководство Intel http://www.intel.com/content/dam/www/public/us/en/documents/manuals/64-ia-32-architectures-optimization-manual.pdf поможет вам. Проверьте стр. 587 (B.3.2) для общей иерархии событий (здесь также есть киоски FE и BE), B.3.2.1-B.3.2.3 для информации о стойлах бэкэнда и как начать копать (проверяет события в кэше и т. Д.) и ниже.

Как ядро ​​может влиять на подсистему памяти? Он может настроить различное виртуальное физическое отображение (вряд ли в вашем случае), или он может отодвинуть процесс от данных. У вас не-NUMA-машина, но Haswell не является точной UMA ​​- есть кольцевая шина, и некоторые ядра расположены ближе к контроллеру памяти или к некоторым частям общего LLC (кэш последнего уровня). Вы можете проверить свою программу с taskset утилита, привязывающая его к какому-либо ядру - ядро ​​не будет перемещать его в другое ядро.

ОБНОВЛЕНИЕ: После проверки вашей новой перф статистики от A8 мы видим, что есть больше промахов DLTB для 3.12.6. С изменениями в /proc/pid/maps (много коротких [heap] разделы вместо одного [heap], до сих пор нет точной информации, почему), я думаю, что могут быть различия в прозрачной огромной странице (THP; с 2M огромными страницами требуется меньше записей TLB для того же объема памяти и меньше пропусков tlb), например в 3.12 это не может применяться из-за коротких участков кучи.

Вы можете проверить свои /proc/PID/smaps за AnonHugePages а также /proc/vmstat для значений thp*, чтобы увидеть результаты thp. Значения задокументированы здесь kernel.org/doc/Documentation/vm/transhuge.txt

@osgx Вы нашли причину! После того, как echo никогда не> /sys/kernel/mm/transparent_hugepage/enabled, 3.11.10 занимает 3.12.6!

Хорошие новости!

Хотелось бы получить дополнительную информацию о том, как отключить рандомизацию, и о том, где сообщить об этом как об ошибке (регрессия производительности 7% довольно серьезна).

Я был не прав, этот эффект раздела с несколькими кучами не является случайным рандомизацией (который изменяет только начало кучи). Это сбой слияния VMA в do_brk; не знаю почему, но некоторые изменения для VM_SOFTDIRTY были замечены в mm между 3.11.10 - 3.12.6.

ОБНОВЛЕНИЕ 2: Возможная причина не слияния VMA:

http://lxr.missinglinkelectronics.com/linux+v3.11/mm/mmap.c#L2580 do_brk в 3.11

http://lxr.missinglinkelectronics.com/linux+v3.11/mm/mmap.c#L2577 do_brk в 3.12

3.12 только что добавлено в конце do_brk

2663        vma->vm_flags |= VM_SOFTDIRTY;
2664        return addr;

И чуть выше у нас есть

2635        /* Can we just expand an old private anonymous mapping? */
2636        vma = vma_merge(mm, prev, addr, addr + len, flags,
2637                                        NULL, NULL, pgoff, NULL);

и внутри vma_merge есть тест для vm_flags

http://lxr.missinglinkelectronics.com/linux+v3.11/mm/mmap.c#L994 3.11

http://lxr.missinglinkelectronics.com/linux+v3.12/mm/mmap.c#L994 3.12

1004        /*
1005         * We later require that vma->vm_flags == vm_flags,
1006         * so this tests vma->vm_flags & VM_SPECIAL, too.
1007         */

vma_merge -> can_vma_merge_before -> is_mergeable_vma ...

 898        if (vma->vm_flags ^ vm_flags)
 899                return 0;

Но во время проверки новый vma не помечается как VM_SOFTDIRTY, а старый уже отмечен.

Это изменение может быть вероятным кандидатом http://marc.info/?l=linux-kernel&m=138012715018064. Я говорю это свободно, поскольку у меня нет ресурсов для подтверждения. Стоит отметить, что это было единственное существенное изменение в планировщике между 3.11.10 и 3.12.6.

В любом случае мне очень интересно увидеть конечные результаты ваших выводов, так что держите нас в курсе.

Другие вопросы по тегам