2017-01-03 15 views
0
7703.572978 task-clock (msec)   # 0.996 CPUs utilized   
      1,575 context-switches   # 0.204 K/sec     
      18 cpu-migrations   # 0.002 K/sec     
     65,975 page-faults    # 0.009 M/sec     
25,719,058,036 cycles     # 3.340 GHz      
<not supported> stalled-cycles-frontend 
<not supported> stalled-cycles-backend 
12,323,855,909 instructions    # 0.48 insns per cycle   
    2,337,484,352 branches     # 303.429 M/sec     
    200,227,908 branch-misses    # 8.57% of all branches   
    3,167,237,318 L1-dcache-loads   # 411.139 M/sec     
    454,416,650 L1-dcache-load-misses  # 14.35% of all L1-dcache hits 
    326,345,389 LLC-loads     # 42.363 M/sec     
<not supported> LLC-load-misses:HG  

Я профилировал свой код, написанный libCCC в C по perf stat. Он сортирует дважды связанный список, который вызывает множество операций обхода списка, что означает, что он может запрашивать много данных, расположенных с разных адресов памяти. Однако современный процессор поддерживает конвейерную обработку нескольких этапов, прогнозирование ветвлений и выполнение вне очереди, поэтому они должны увеличить средний объем инструкций, выполненных за тот же промежуток времени. Фактически, из данных анализа обрабатывается только команда в течение двух циклов. Каковы причины, которые могут вызвать это явление?Почему IPC ниже, чем один на современном процессоре?

+1

Обход списка включает в себя цепи зависимостей при доступе к памяти, а образы кэша L1 обычно имеют задержку в три или четыре цикла в современных высокопроизводительных ядрах. Выполнение вне порядка помогает, когда есть операции, которые не зависят от ожидающих результатов. –

ответ

1

Ваш процессор просто ждет памяти, вот и все. Именно этот эффект оправдывает HyperThreading: современные процессоры могут переключаться достаточно быстро, чтобы одно ядро ​​могло работать на двух потоках, выполняя команды от одного, в то время как другой поток ожидает в памяти.

+0

Вы имеете в виду, что в моем случае, даже если мой процессор поддерживает гиперпоточность, ядро ​​все еще останавливается (не показано) на обоих потоках и ничего не делает при извлечении данных из основной памяти? –

+0

@KevinDong: ну, доступ к основной памяти может занять 60 циклов или около того, поэтому вполне возможно, что одно физическое ядро ​​ожидает двух считываний по памяти от имени двух потоков. IIRC Sun однажды сделал процессор T1, который мог бы перегружать 8 потоков на ядро. – MSalters

+0

Возможно, перемещение связанного списка может значительно улучшить работу на машинах, которые поддерживают более высокий уровень гиперпоточности. Затем он может ждать данные одновременно ;-) –

0

Просто потому, что у вас есть конвейер, никоим образом не означает, что он всегда будет использоваться эффективно. Как и добавление кеша, любой из них может стоить вам производительности, а не улучшать производительность. Поэтому нет никаких оснований предполагать, как бы «современный» ваш процессор, что вы всегда будете получать магические характеристики. Проблема начинается с вашего кода, это ваше приложение по какой-то причине, как вы пишете свое приложение, на каком языке вы используете какой компилятор вы используете, какие параметры компилятора вы используете, - это первые шаги в производительности, а затем платформа, RAM, кеш, диск, операционная система и т. д. все играют определенную роль. Прошло много лет с тех пор, как процессор стал узким местом, предполагая, что вы можете прокормить процессор так быстро, как он может потреблять, и предполагая, что последовательности команд дружественны к конвейеру, и у вас нет доступа к данным, а затем вы можете кричать, но реальность Процессор тратит много времени на ожидание подачи данных или инструкций. Вы можете написать очень простые тесты, даже малые, как два цикла команд, и увидеть, что производительность сильно варьируется в зависимости от различных факторов, характера выборки процессора, выравнивания, заставляющего его извлекать гораздо больше, чем нужно, перемещая выравнивание к чувствительной области строки кэша может привести к чтению дополнительной строки кэша. И это всего лишь пара/несколько инструкций, подумайте о том, сколько инструкций используется вашим приложением. Затем смешайте доступ к данным с этим. Бенчмаркинг - это несколько бесполезная задача, отличная от того, чтобы показать, что она действительно не означает ничего, кроме того, что вы можете манипулировать результатами, чтобы что-то выглядело хорошо или плохо.

В вашем случае вы просто не пишете свой код, чтобы быть дружелюбным к компилятору, чтобы он выполнялся, скорее всего, ваши обращения к данным, как вы структурировали свои данные (возможно, используя массивы байтов вместо больших массивов, используя структуры с предметы разного размера и т. д.), не дай бог разбить ваши данные на мощность двух кратных границ границ кеша.

Начните настройку своих данных или кода, измените функции поддержки в исходном коде, у вас может быть функция a, b, c, определенная в этом порядке, измените порядок a, c, b, как это изменит производительность, если вообще ? Добавьте фиктивную глобальную функцию рядом с началом вашего проекта (или зайдите в ремешок для загрузки и добавьте или удалите nops), возможно, встроенный, затем два, а затем три, а затем четыре и т. Д. Или другие аналогичные безопасные инструкции в этой фиктивной функции. Измените размер ваших переменных, измените размер элементов массива, если это возможно, перестройте порядок переменных, определенных в структурах. Один или все из них плюс много других вещей могут повлиять на результаты вашей работы.

Нижняя линия, имеющая современный процессор, не имеет ничего общего с часами на каждую усредненную инструкцию для любой случайной программы. Вы хотите получить много инструкций за такт, за которые вы должны работать (и нечего предполагать, что как только вы нажмете сладкое пятно на своем компьютере, вы перенёте эту программу на другой совместимый компьютер, который программа там выполняет, это может быть собака медленнее на других подобных машинах класса и быть быстрым на одном).