Подтвердить что ты не робот

Точно как "быстрые" - это современные процессоры?

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

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

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

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

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

Изменить. Причина, по которой я задаю это, - попытаться разработать "правило большого пальца", которое позволит мне взглянуть на простой код и приблизительное измерение времени, затраченного на ближайший порядок величина.

Редактировать # 2: Множество ответов с интересными моментами, но никто (пока) не поставил цифру, измеренную во времени. Я ценю, что есть "осложнения" на вопрос, но c'mon: Если мы сможем оценить количество пианино-тюнеров в Нью-Йорке мы должны быть в состоянии оценить время выполнения кода...

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

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 gigayear?

4b9b3361

Ответ 1

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

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

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

  • Операции
  • Register to Register выполняют 1 такт ядра для выполнения. Это обычно должно быть консервативным, особенно, поскольку все это появляется в последовательности.
  • Операции загрузки и хранения, связанные с памятью, занимают 1 такт памяти памяти для выполнения. Это должно быть очень консервативно. При высокой частоте попадания в кеш он будет больше похож на два тактовых процессора шины, которые являются тактовой частотой шины между ядром ЦП и кешем, но не обязательно являются часами ядра.

Ответ 2

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

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

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

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

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

Ответ 3

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

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

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

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

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

О, и как конкретный пример. Для следующего кода 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, состоящий из загрузочного-хранилища для каждой позиции в массиве (в этом случае "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
  ...

Каждой группе из загрузочного-добавления-хранилища требуется 5 тактовых циклов.

Ответ 4

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

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

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

Ответ 5

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

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

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

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

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

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

Ответ 6

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

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

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

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

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

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

Ответ 7

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

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

Ответ 8

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

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

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

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

Ответ 9

Много хороших ответов на эту тему уже есть, но одна тема до сих пор не упоминается: неверное предсказание отрасли.

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

Обратите внимание: одна и та же команда может иметь "нулевую стоимость" при первом запуске в цикле и действительно огромную стоимость при следующем выполнении одной и той же команды!

Ответ 10

Все, что вам нужно, находится в соответствующих руководствах по процессорам. Как у AMD, так и у Intel есть PDF файл на своем сайте, описывающий задержки каждой инструкции.

Просто имейте в виду сложность современных процессоров. Они не выполняют одну инструкцию за раз, они могут загружать 3-4 инструкции за цикл, и почти все инструкции конвейерны, поэтому, когда загружаются следующие инструкции, текущие нигде не закончены. Он также переупорядочивает инструкции, чтобы обеспечить более эффективное планирование. Современный процессор может легко выполнять 50 инструкций за раз.

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

Ответ 11

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

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

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

Ответ 12

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

В худшем случае до нескольких миллисекунд (когда ОС обрабатывает файл_файла и должна извлекать данные/инструкцию с диска). Исключая диск /swapping, все еще зависит от того, есть ли у вас машина NUMA, какая у нее топология, в которой находится память node, существует ли параллельный доступ с другого CPU (протоколы синхронизации шины и синхронизации кэш-памяти) и т.д..

Ответ 13

Я рекомендую загрузить руководство по оптимизации программного обеспечения AMD .

Ответ 14

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