Надежность профилирования времени разборки Xcode Instrument

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

Скриншот инструмента

Я бы не ожидал mov инструкция брать 23,3% времени, пока div Инструкция брать практически нечего. Это заставляет меня полагать, что эти результаты ненадежны. Это правда и известно? Или я просто испытываю ошибку инструментов? Или есть какой-то вариант, который мне нужно использовать для получения надежных результатов?

Есть ли какие-либо ссылки на этот вопрос?

2 ответа

Решение

Прежде всего, возможно, что некоторые подсчеты действительно принадлежат divss взимается с более поздними инструкциями, которые называются "занос". (Также см. Остальную часть этой ветки комментариев для получения дополнительной информации.) Предположительно, Xcode похож на Linux perf и использует фиксированный cpu_clk_unhalted.thread счетчик для cycles вместо одного из программируемых счетчиков. Это не "точное" событие (PEBS), поэтому возможны заносы. Как указывает @BeeOnRope, вы можете использовать событие PEBS, которое происходит один раз за цикл (например, UOPS_RETIRED < 16) в качестве замены PEBS для счетчика фиксированных циклов, устраняя некоторую зависимость от поведения прерывания.

Но то, как счетчики в основном работают для конвейерного / неупорядоченного исполнения, также объясняет большую часть того, что вы видите. Или это может; вы не показали полный цикл, поэтому мы не можем имитировать код на простой модели конвейера, как это делает IACA, или вручную, используя руководства по аппаратному обеспечению, такие как http://agner.org/optimize/ и руководство по оптимизации Intel. (И вы даже не указали, какая у вас микроархитектура. Я думаю, это какой-то член семейства Intel Sandybridge на Mac).


Рассчитывает на cycles обычно взимаются с инструкции, ожидающей результата, а не с инструкции, которая медленно выводит результат. Конвейерные процессоры не останавливаются, пока вы не попытаетесь прочитать результат, который еще не готов.

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

Так что же происходит, когда возникает прерывание? См . Ответ Энди Глеу об этом, который объясняет внутреннюю часть прерываний счетчика параметров в конвейере микроархитектуры Intel P6 и почему (до PEBS) они всегда откладывались. Семейство Sandybridge похоже на P6 для этого.

Я думаю, что разумная ментальная модель для прерываний счетчика перфокарт на процессорах Intel состоит в том, что он отбрасывает любые мопы, которые еще не были отправлены в исполнительный модуль. Но отправленные ALU-мопы уже отправляются по конвейеру на пенсию (если нет более молодых мопов, которые были отброшены), а не прерываются, что имеет смысл, поскольку максимальная дополнительная задержка составляет ~16 циклов для sqrtpd и очистка очереди магазина может легко занять больше времени. (Отложенные магазины, которые уже вышли на пенсию, не могут быть отменены). IDK о грузах / магазинах, которые не вышли на пенсию; по крайней мере, нагрузки, вероятно, отбрасываются.

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

Таким образом, распределение cycles Счетчик показывает, какие инструкции тратят больше всего времени, будучи самой старой еще не отправленной инструкцией в планировщике. (Или в случае остановок внешнего интерфейса, по каким инструкциям процессор останавливается, пытаясь получить / декодировать / выдать). Помните, это обычно означает, что он показывает вам инструкции, ожидающие ввода, а не инструкции, которые медленно их генерируют.

(Хм, это может быть неправильно, и я не так много тестировал. Я обычно использую perf stat смотреть на общий счетчик всего цикла в микробенчмарке, а не статистические профили с perf record, addss а также mulss задержка выше, чем andps так что вы ожидаете andps чтобы получить счетчики, ожидающие ввода xmm5, если предложенная мной модель была правильной.)

В любом случае, общая проблема заключается в том, что при выполнении нескольких инструкций в полете HW "винит", когда cycles счетчик оборачивается?


Обратите внимание, что divss медленен для получения результата, но является только однопроцессной инструкцией (в отличие от целого числа div который микрокодируется на AMD и Intel). Если вы не ограничиваете его задержку или не полностью конвейерную пропускную способность, это не медленнее, чем mulss потому что он также может перекрывать окружающий код.

(divss / divps не полностью конвейеризован. На Haswell например, независимый divps можно начинать каждые 7 циклов. Но каждому нужно всего 10-13 циклов, чтобы получить свой результат. Все остальные исполнительные блоки полностью конвейерны; возможность начать новую операцию с независимыми данными каждый цикл.)

Рассмотрим большой цикл, который ограничивает пропускную способность, а не задержку какой-либо зависимости, переносимой циклом, и требует только divss запускать один раз за 20 инструкций FP. С помощью divss константой вместо mulss с обратной константой не должно иметь (почти) никакой разницы в производительности. (На практике неупорядоченное планирование не является идеальным, и более длинные цепочки зависимостей вредят некоторым, даже если они не переносятся в цикле, потому что они требуют большего количества команд, чтобы скрыть все эти задержки и поддерживать максимальную пропускную способность. ядро, чтобы найти параллелизм на уровне команд.)

Во всяком случае, дело здесь в том, что divss является одним мопом, и это имеет смысл, чтобы не получить много очков для cycles событие, в зависимости от окружающего кода.


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


Что ваш профиль может сказать нам:

  • divss не нужно ждать, пока его входные данные будут готовы. (The movaps %xmm3, %xmm5 перед divss иногда занимает несколько циклов, но divss никогда не делает.)

  • Мы можем приблизиться к узким местам в пропускной способности divss

  • Цепочка зависимостей с участием xmm5 после divss получает некоторые счета. Внеочередное выполнение должно работать, чтобы сохранить несколько независимых итераций этого в полете одновременно.

  • maxss / movaps Цепочка зависимостей, переносимая циклами, может быть существенным узким местом. (Особенно если ты на Скайлэйке, где divss Пропускная способность составляет один на 3 такта, но maxss задержка составляет 4 цикла. А конфликт ресурсов из-за конкуренции за порты 0 и 1 приведет к задержке maxss.)


Высокие показатели для movaps может быть из-за этого следующего maxss, формируя единственную зависимую от цикла передачу в той части цикла, которую вы показываете. Так что вполне вероятно, что maxss действительно медленно, чтобы дать результаты. Но если бы это действительно была цепочка депов, которую носили петли, которая была главным узким местом, вы бы ожидали увидеть много подсчетов на maxss сам, так как он будет ждать его ввода от последней итерации.

Но, возможно, исключение mov является "особенным", и все счета по какой-то причине оплачиваются movaps? На процессорах Ivybridge и более поздних версиях для копий регистра не требуется исполнительный модуль, а вместо этого они обрабатываются на этапе выпуска / переименования конвейера.

Это правда и известно?

Да, это известная проблема с инструментами профилирования на Intel x86. Я наблюдал это (время, которое подозрительно отводилось на кажущиеся невинными инструкции) как с Linux perf_events, так и с Intel VTune. Об этом также сообщали другие люди.

Лучшая и более честная визуализация собранных результатов суммировала бы все выборки внутри каждого базового блока и продемонстрировала бы результирующее значение, связанное с базовым блоком, а не с его отдельными инструкциями. Не на 100% надежно, но немного лучше и честно,

Или есть какой-то вариант, который мне нужно использовать для получения надежных результатов?

Я не знаю, будет ли более новое оборудование для профилирования, а именно инструменты на основе Intel Processor Trace (доступное начиная с Broadwell, но улучшенное в Skylake) вместо более старой PEBS, давать более точные данные. Я думаю, что сначала нужно поэкспериментировать с такими инструментами.

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