Perf startup: почему простой статический исполняемый файл, который выполняет MOV + SYS_exit, имеет так много остановленных циклов (и инструкций)?

Я пытаюсь понять, как измерить производительность, и решил написать очень простую программу:

section .text
    global _start

_start:
    mov rax, 60
    syscall

И я запускал программу с помощью perf stat ./bin. Я был удивлен тем, что stalled-cycles-frontend был слишком высоким.

      0.038132      task-clock (msec)         #    0.148 CPUs utilized          
             0      context-switches          #    0.000 K/sec                  
             0      cpu-migrations            #    0.000 K/sec                  
             2      page-faults               #    0.052 M/sec                  
       107,386      cycles                    #    2.816 GHz                    
        81,229      stalled-cycles-frontend   #   75.64% frontend cycles idle   
        47,654      instructions              #    0.44  insn per cycle         
                                              #    1.70  stalled cycles per insn
         8,601      branches                  #  225.559 M/sec                  
           929      branch-misses             #   10.80% of all branches        

   0.000256994 seconds time elapsed

Как я понимаю stalled-cycles-frontend, это означает, что передняя панель процессора должна ждать завершения некоторой операции (например, bus-transaction).

Итак, что привело к тому, что фронт процессора ожидал большую часть времени в этом простейшем случае?

И ошибки 2 страницы? Зачем? Я не читаю страницы памяти.

Ответ 1

Ошибки страниц включают страницы кода.

perf stat включает служебные данные запуска.

IDK детали того, как perf начинает подсчет, но, по-видимому, он должен запрограммировать счетчики производительности в режиме ядра, поэтому они подсчитывают, пока CPU переключается обратно в пользовательский режим (остановка для многих циклов, особенно на ядре с защитой Meltdown, которая делает недействительными TLB).

Я полагаю, что большинство команд 47,654, которые были записаны, были кодом ядра. Возможно, включая обработчик ошибок страницы!

Я думаю, что ваш процесс никогда не идет user- > kernel- > user, весь процесс - это kernel- > user- > kernel (startup, syscall для вызова sys_exit, а затем никогда не возвращается в пользовательское пространство), поэтому никогда не было случая, когда TLB были бы горячими, но, возможно, при запуске внутри ядра после системного вызова sys_exit. И все же пропуски TLB не являются ошибками страниц, но это объясняет много остановленных циклов.

Сам пользовательский > переход ядра объясняет около 150 остановленных циклов, BTW. syscall быстрее, чем пропустить кеш (за исключением того, что он не конвейерный, и фактически сбрасывает весь конвейер, то есть уровень привилегий не переименовывается.)