Насколько «быстры» современные процессоры? - PullRequest
18 голосов
/ 11 января 2009

Когда я использовал для программирования встроенных систем и более ранних 8/16-битных ПК (6502, 68K, 8086), у меня было довольно хорошее представление о том, как долго (в наносекундах или микросекундах) выполнялась каждая инструкция. В зависимости от семейства один (или четыре) цикла приравниваются к одному «извлечению памяти», и без кэшей, о которых следует беспокоиться, можно угадать время, основываясь на количестве обращений к памяти.

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

Итак, кто-нибудь может предоставить некоторые временные параметры для двух примеров инструкций, скажем, на 2 ГГц Core 2 Duo. Лучшие и худшие случаи (при условии, что в кеше ничего нет / все в кеше) были бы полезны.

Инструкция # 1: Добавить один 32-битный регистр в секунду.

Инструкция # 2: Переместить 32-разрядное значение из регистра в память.

Редактировать : Причина, по которой я спрашиваю об этом, состоит в том, чтобы попытаться разработать «практическое правило», которое позволило бы мне взглянуть на простой код и приблизительно измерить время, затраченное на ближайший порядок величины ,

Редактировать # 2: Множество ответов с интересными моментами, но никто (пока) не записал цифру, измеренную во времени. Я понимаю, что в этом вопросе есть «осложнения», но давайте: если мы сможем оценить количество настройщиков пианино в Нью-Йорке , мы сможем оценить время выполнения кода ...

Возьмите следующий (немой) код:

int32 sum = frigged_value();

// start timing
 for (int i = 0 ; i < 10000; i++)
 {
   for (int j = 0 ; j < 10000; j++)
   {
     sum += (i * j)
   }
   sum = sum / 1000;
 }

// end timing

Как мы можем оценить сколько времени потребуется, чтобы пробежать ... 1 фемтосекунду? 1 гиг?

Ответы [ 14 ]

40 голосов
/ 11 января 2009

Современные процессоры, такие как Core 2 Duo, о которых вы упомянули, являются суперскалярными и конвейерными . Они имеют несколько исполнительных блоков на ядро ​​и фактически работают над несколькими инструкциями одновременно на ядро; это суперскалярная часть. Конвейерная часть означает, что существует задержка с момента, когда инструкция считывается и «выдается», до того момента, когда она завершает выполнение, и это время варьируется в зависимости от зависимостей между этой инструкцией и другими, проходящими через другие исполнительные блоки одновременно. Таким образом, по сути, сроки любой данной инструкции варьируются в зависимости от того, что вокруг нее и от чего она зависит. Это означает, что данная команда имеет вид наилучшего и наихудшего времени выполнения на основе ряда факторов. Из-за нескольких исполнительных блоков вы можете фактически иметь более одной инструкции, выполняющей выполнение за такт ядра, но иногда между завершениями бывает несколько тактов, если конвейер должен останавливаться в ожидании памяти или зависимостей в конвейерах.

Все вышеперечисленное только с точки зрения самого ядра процессора. Затем у вас есть взаимодействие с кешами и конкуренция за пропускную способность с другими ядрами. Шинный интерфейсный модуль ЦПУ занимается получением инструкций и данных, поступающих в ядро, и выводом результатов из ядра через кэш-память.

Грубые правила большого пальца, которые нужно принять с зерном соли:

  • Регистрация в регистре Для выполнения операций требуется 1 ядро ​​ часов. Обычно это должно быть консервативно, особенно если учесть, что больше из них появляются последовательно.
  • Операции загрузки и сохранения, связанные с памятью, требуют 1 шина памяти такта для выполнения. Это должно быть очень консервативно. С высокой частотой обращений к кешу это будет больше похоже на тактовую частоту 2 шины , которая является тактовой частотой шины между ядром процессора и кешем, но не обязательно тактовой частотой ядра.
14 голосов
/ 11 января 2009

Почти невозможно предоставить точную информацию о времени, которую вы ожидаете, таким образом, чтобы она была ПОЛЕЗНА для вас.

Следующие понятия влияют на синхронизацию команд; некоторые могут меняться от момента к моменту:

  • Разложение микроопераций
  • Операция конвейерной обработки
  • Суперскалярное исполнение
  • Вне исполнения заказа
  • SMT / SMP исполнение
  • Режим с плавающей точкой
  • Прогноз ветвления / предварительная выборка
  • задержка кэша
  • Задержка памяти
  • Тактирование тактовой частоты
  • и т.д.

Обратитесь к книге по современной компьютерной архитектуре, если вам нужно какое-либо дальнейшее объяснение вышеупомянутых понятий.

Лучший способ измерить скорость вашего кода - это (удивительно!) Измерить скорость вашего кода, выполняющего ту же рабочую нагрузку и в тех же условиях, которые вы ожидаете, когда «в реальном мире».

8 голосов
/ 11 января 2009

Использование описания, в значительной степени основанного на архитектуре Intel Pentium, в двух словах:

  • процессор имеет несколько «исполнительных блоков», которые могут выполнять различные типы «микроопераций»; инструкции могут быть разделены на несколько микроопераций
  • различные исполнительные блоки по существу работают параллельно
  • каждая микрооперация связывает соответствующий исполнительный блок на определенное количество тактов, поэтому никакая другая инструкция не может использовать этот исполнительный блок: например, «добавление с плавающей запятой» может связать блок «FP execute» на 2 такта
  • исполнительные блоки сгруппированы по «порту», ​​и каждый тактовый цикл, на каждый порт может отправляться новая микрооперация (при условии, что соответствующий исполнительный модуль свободен в этот момент); некоторым юнитам также можно послать «дополнительную операцию» в середине цикла; таким образом, каждый тактовый цикл, определенное количество операций может запускать выполнение;
  • процессор может переупорядочивать микрооперации, когда это не нарушает зависимости (или где результат все еще может быть восстановлен), чтобы использовать преимущества того, какие исполнительные блоки свободны в данный момент
  • поэтому инструкции могут выполняться параллельно, но какие части каких инструкций выполняются одновременно, довольно сложная ситуация
  • общее время для данной инструкции, таким образом, зависит от того, сколько времени ей пришлось «ждать», пока необходимые исполнительные блоки не станут доступными, фактического времени, которое эти операции потратили на выполнение на указанных блоках, плюс любое дополнительное время, необходимое для » подвести итог "

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

  • Intel (и, вероятно, другие производители) публикуют список инструкций пропускная способность и задержка тайминги
  • пропускная способность - это количество тактовых циклов, фактически необходимых для соответствующего исполнительного блока (ов)
  • latency - это число «тактов наихудшего случая», необходимое для того, чтобы после начала выполнения инструкции до того, как результат этого выполнения станет доступным для ввода в другую инструкцию

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

Да, и как конкретный пример. Для следующего кода Java

public void runTest(double[] data, double randomVal) {
  for (int i = data.length-1; i >= 0; i--) {
    data[i] = data[i] + randomVal;
  }
}

Hotspot 1.6.12 JIT-компилирует последовательность внутренних циклов в следующий код Intel, состоящий из load-add-store для каждой позиции в массиве (в данном случае «randomVal» хранится в XMM0a):

  0b3     MOVSD  XMM1a,[EBP + #16]
  0b8     ADDSD  XMM1a,XMM0a
  0bc     MOVSD  [EBP + #16],XMM1a
  0c1     MOVSD  XMM1a,[EBP + #8]
  0c6     ADDSD  XMM1a,XMM0a
  0ca     MOVSD  [EBP + #8],XMM1a
  ...

каждая группа из load-add-store занимает 5 тактов .

7 голосов
/ 11 января 2009

Вид предсказания, о котором вы просите, безнадежен.

Если вы хотите иметь практическое правило, вот несколько практических правил:

  • За время, необходимое для получения слова из кэша уровня 2, процессор может выполнить не менее 10 инструкций. Так что беспокойтесь о доступе к памяти, а не о количестве команд --- вычисления в регистрах почти бесплатны.

  • За время, необходимое для получения слова из ОЗУ, процессор может выполнить тысячи инструкций (это число варьируется на пару порядков величины в зависимости от деталей вашего оборудования). Убедитесь, что это происходит только на холодном кеше; иначе ничто не имеет значения.

  • Если вы работаете на процессорах x86, регистров недостаточно. Старайтесь не иметь более 5 живых переменных в вашем коде в любой момент. Или, что еще лучше, перейдите на AMD64 (x86_64) и удвойте количество регистров. С 16 регистрами и параметрами, переданными в регистрах, вы можете не беспокоиться о регистрах.

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

7 голосов
/ 11 января 2009

Современные процессоры делают еще более сложные вещи.

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

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

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

Микроархитектура Intel P6 выполняет неупорядоченное выполнение и переименование регистров. Архитектура Core 2 является последней производной от P6.

Чтобы на самом деле ответить на ваш вопрос - вы в принципе не можете определить производительность вручную перед лицом всех этих архитектурных оптимизаций.

7 голосов
/ 11 января 2009

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

Итак, лучший случай - ноль (при выполнении параллельно с другими инструкциями). Но как это тебе поможет?

На этой веб-странице показаны некоторые тесты, включая некоторые результаты в% MIPS / MHz. Как вы можете видеть, во многих тестах выполняется несколько инструкций за такт. Графики также показывают влияние размера кеша и скорости памяти.

5 голосов
/ 21 февраля 2009

Это только ответ на часть вашего вопроса, но я нашел эту таблицу из Википедии на местность ссылки полезной. Он описывает скорость доступа и объем памяти на разных уровнях иерархии памяти, используя приблизительное время 2006:

  • Регистры ЦП (8-32 регистра) - немедленный доступ (0-1 тактов)
  • Кэш-память ЦП L1 (от 32 до 128 КиБ) - быстрый доступ (3 такта)
  • Кэш-память L2 CPU (от 128 КиБ до 12 МиБ) - немного более медленный доступ (10 тактов)
  • Основная физическая память (ОЗУ) (от 256 МБ до 4 ГБ) - медленный доступ (100 тактов)
  • Диск (файловая система) (от 1 ГиБ до 1 ТиБ) - очень медленный (10 000 000 тактовых циклов)
  • Удаленная память (например, другие компьютеры или Интернет) (практически не ограничена) - скорость варьируется
4 голосов
/ 27 января 2009

В этой теме уже много хороших ответов, но одна тема до сих пор не упомянута: ошибочное прогнозирование ветки .

Поскольку все современные процессоры конвейерны, когда декодер инструкций встречается с инструкцией, такой как «переход, если равен», он не знает, в какую сторону будет переходить инструкция, и поэтому он просто догадывается. Затем он продолжает вводить инструкции в конвейер, основываясь на этом предположении. Если он сделал правильное предсказание, скорость и задержка команды перехода по существу равны нулю. Если он делает неправильное предположение, скорость и задержка одной и той же команды перехода могут составлять 50 или 100 циклов.

Обратите внимание, что одна и та же инструкция может иметь «нулевую стоимость» в первый раз, когда она выполняется в цикле, и действительно огромную стоимость при следующем выполнении той же инструкции!

4 голосов
/ 26 января 2009

Вы можете скачать руководства Intel 64 и IA-32 здесь .

Но что вам действительно нужно, так это материал от Agner Fog .

У него много дополнительной информации, например, его руководство "Таблицы инструкций: списки задержек команд, пропускной способности и сбоев микроопераций для процессоров Intel и AMD" .

Или тестовые программы для подсчета тактов (он использует счетчик меток времени ).

3 голосов
/ 11 января 2009

Интересная цитата Алана Кея 2004 года :

Просто вдобавок, чтобы дать вам интересный тест - примерно в той же системе, примерно оптимизированный таким же образом, тест 1979 года в Xerox PARC сегодня работает только в 50 раз быстрее. Закон Мура дал нам улучшение в 40–60 тысяч раз за это время. Таким образом, эффективность из-за плохой архитектуры ЦП снизилась примерно в 1000 раз.

Смысл, по-видимому, заключается в том, что улучшения производительности ЦП сосредоточены в тех областях, где они относительно мало влияют на программное обеспечение, которое мы действительно пишем.

...