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


23

это кусок кода сборки

section .text
    global _start       ;must be declared for using gcc
_start:                     ;tell linker entry point
    mov edx, len    ;message length
    mov ecx, msg    ;message to write
    mov ebx, 1      ;file descriptor (stdout)
    mov eax, 4      ;system call number (sys_write)
    int 0x80        ;call kernel
    mov eax, 1      ;system call number (sys_exit)
    int 0x80        ;call kernel

section .data

msg db  'Hello, world!',0xa ;our dear string
len equ $ - msg         ;length of our dear string

Учитывая конкретную компьютерную систему, можно ли точно предсказать фактическое время выполнения фрагмента кода сборки.


30
Является ли «запустить код на этом компьютере и использовать секундомер» верным ответом?
Драконис

4
Я подозреваю, что большая часть времени, потраченного на выполнение этого фрагмента кода, ожидает ввода-вывода. Время, необходимое для выполнения отдельных инструкций, несколько предсказуемо, если вы знали расположение памяти в коде и все подробности о процессоре (которые сегодня чрезвычайно сложны), но на скорость и память также влияют память и диск, так что вы ' Я должен был бы знать очень большое количество деталей о них. Поэтому, если вы не учитываете физические явления (которые также влияют на время), вы можете сказать, что это предсказуемо, но невероятно сложно сделать это.
IllidanS4 хочет вернуть Монику

4
всегда можно оценить ...
sudo rm -rf slash

3
Разве это также невозможно из-за проблемы остановки? Для некоторого кода мы можем доказать, остановится ли он, но у нас не может быть алгоритма, который определяет это для всех возможных кодов.
Кучкем

2
@Falco Это было бы свойством данной системы. Некоторые автономные реализации C не имеют операционной системы; все, что работает, это основной цикл (или даже не цикл ;-)), который может или не может считывать аппаратные адреса для ввода.
Питер - восстановить Монику

Ответы:


47

Я могу лишь процитировать из руководства довольно примитивного процессора, процессора 68020, выпущенного примерно в 1986 году: «Вычислить точное время выполнения последовательности инструкций сложно, даже если у вас есть точные знания о реализации процессора». Которого у нас нет. И по сравнению с современным процессором этот процессор был примитивным .

Я не могу предсказать время выполнения этого кода, и вы не можете. Но вы даже не можете определить, что такое «время выполнения» фрагмента кода, когда процессор имеет массивные кэши и огромные возможности выхода из строя. Типичный современный процессор может иметь 200 инструкций «в полете», то есть на разных стадиях исполнения. Таким образом, время от попытки прочитать первый байт инструкции до удаления последней инструкции может быть довольно продолжительным. Но фактическая задержка всей остальной работы, которая требуется процессору, может быть (и обычно) намного меньше.

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

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

В целом: совершенно непредсказуемо.


12
Я думаю, что ваши выводы слишком сильны. Задержка и пропускная способность являются общими показателями для измерения «времени выполнения» программы. Также вы можете просто выбрать подходящее определение «времени выполнения». Более того, если у вас есть полный снимок состояния системы, hw и sw и отличное знание внутренних процессов ЦП, вы можете предсказать время выполнения. В Intel они, вероятно, могут оценить время выполнения, даже здесь, на SO, мы можем прогнозировать задержки и результаты с точностью цикла. В этом случае, кроме системных вызовов, это не так сложно.
Маргарет Блум

10
@MargaretBloom даже тогда. Я помещаю свой телефон слишком близко к духовке, процессор разгоняется, чтобы управлять температурой, ваша оценка времени работы неожиданно слишком низкая. И даже если вы рассчитываете циклы и не выполняете системные вызовы, другие потоки и процессоры могут хорошо играть с содержимым ОЗУ или могут выгружать вашу память на жесткий диск во время выгрузки из-за непредсказуемых обстоятельств, начиная от питания скачки замедляют работу жесткого диска настолько, что конкурирующий поток получает достаточно памяти для того, чтобы разрушить ваш, вплоть до потоков, которые бросают кубики, чтобы увидеть, сколько времени можно потратить.
Джон Дворжак

6
Кроме того, «полное знание состояния системы, hw и sw» - довольно сложная задача, метинкс. Добавьте «10 мс заранее», и вы уже просите невозможного. И если в реализации вашего процессора аппаратная генерация случайных чисел использует квантовые явления (вероятно, это так), и какой-то поток в ЦП вызывает его, то даже знание полного состояния вселенной в 3000 км вокруг компьютера спасет вас. И в MWI, вы даже не можете угадать правильно.
Джон Дворжак

8
@Nat: Даже в криптографии «постоянное время» на самом деле не означает абсолютно постоянное - это просто означает, что время выполнения не имеет систематических изменений, которые зависят от секретных данных и могут быть статистически коррелированы с этим. И на практике часто просто предполагается, что, если выбранный путь к коду и образец доступа к памяти не зависят от секретных данных, и если конкретные инструкции, которые, как известно, занимают переменное количество времени, избегаются (или их входные данные маскируются для надеюсь, устранить корреляцию), это, вероятно, достаточно хорошо. Кроме того, вы действительно должны просто измерить это.
Ильмари Каронен

2
68020 - сложный зверь ... попробуйте MCS51 ....
rackandboneman

30

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

Atari 2600 (или Atari Видеосистема компьютера) был один из самых ранних домашнего видео игровых систем и впервые была выпущена в 1978 г. В отличие от более поздних систем эпохи, Atari не может позволить себе , чтобы дать устройству буфер кадра, а это означает , что процессор имел запускать код на каждой линии сканирования, чтобы определить, что производить - если выполнение этого кода занимает более 17,08 микросекунд (интервал HBlank), графика не будет правильно настроена до того, как линия сканирования начнет их рисовать. Хуже того, если программист хотел нарисовать более сложный контент, чем обычно разрешал Atari, он должен был измерить точное время для инструкций и изменить графические регистры во время прорисовки луча с интервалом 57,29 микросекунд для всей строки сканирования.

Однако Atari 2600, как и многие другие системы, основанные на 6502, имел очень важную функцию, обеспечивающую тщательное управление временем, требуемое для этого сценария: центральный процессор, оперативная память и телевизионный сигнал запускались с тактовой частотой на основе одного и того же мастера. Часы. Телевизионный сигнал проходил с тактовой частоты 3,98 МГц, разделяя приведенное выше время на целое число «цветовых часов», которые управляли телевизионным сигналом, а цикл тактовых импульсов ЦП и ОЗУ составлял ровно три цветовых тактовых сигнала, позволяя такту ЦП быть точная мера времени относительно текущего телевизионного сигнала прогресса. (За дополнительной информацией обращайтесь к Руководству программиста Stella , написанному для эмулятора Stella Atari 2600 ).

Эта операционная среда, кроме того, означала, что каждая инструкция ЦП имела определенное количество циклов, которое потребуется в каждом случае, и многие 6502 разработчика опубликовали эту информацию в справочных таблицах. Например, рассмотрим эту запись для CMPинструкции (Сравнить память с аккумулятором), взятой из этой таблицы :

CMP  Compare Memory with Accumulator

     A - M                            N Z C I D V
                                    + + + - - -

     addressing    assembler    opc  bytes  cycles
     --------------------------------------------
     immediate     CMP #oper     C9    2     2
     zeropage      CMP oper      C5    2     3
     zeropage,X    CMP oper,X    D5    2     4
     absolute      CMP oper      CD    3     4
     absolute,X    CMP oper,X    DD    3     4*
     absolute,Y    CMP oper,Y    D9    3     4*
     (indirect,X)  CMP (oper,X)  C1    2     6
     (indirect),Y  CMP (oper),Y  D1    2     5*

*  add 1 to cycles if page boundary is crossed

Используя всю эту информацию, Atari 2600 (и другие разработчики 6502) смогли точно определить, сколько времени потребовалось для выполнения их кода, и создать подпрограммы, которые выполняли то, что им было нужно, и при этом соответствовали требованиям синхронизации телевизионных сигналов Atari. И поскольку это время было настолько точным (особенно для инструкций, которые тратят время, например, NOP), они даже смогли использовать его для изменения графики во время рисования.


Конечно, 6502 Atari - это очень специфический случай, и все это возможно только потому, что в системе было все следующее:

  • Основные часы, которые запускали все, включая оперативную память. Современные системы имеют независимые тактовые частоты для процессора и оперативной памяти, причем тактовая частота ОЗУ часто медленнее, а две не обязательно синхронизированы.
  • Никакого кеширования - 6502 всегда обращался к DRAM напрямую. Современные системы имеют кэши SRAM, которые затрудняют прогнозирование состояния - хотя, возможно, все еще можно предсказать поведение системы с кешем, это, безусловно, более сложно.
  • Другие программы не запускаются одновременно - программа на картридже полностью контролировала систему. Современные системы запускают несколько программ одновременно, используя недетерминированные алгоритмы планирования.
  • Тактовая частота достаточно низкая, чтобы сигналы могли распространяться по системе во времени. В современной системе с тактовой частотой 4 ГГц (например) требуется 6,67 такта фотона света, чтобы преодолеть длину полуметровой материнской платы - вы никогда не ожидаете, что современный процессор будет взаимодействовать с чем-то еще на плате всего за один цикл, поскольку для того, чтобы сигнал на плате даже достиг устройства, требуется более одного цикла.
  • Точно определенная тактовая частота, которая редко изменяется (1,19 МГц в случае Atari) - частоты процессора современных систем постоянно меняются, в то время как Atari не может сделать это без воздействия на телевизионный сигнал.
  • Опубликованные тайминги цикла - x86 не определяет, сколько времени занимает любая из его инструкций.

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


И я должен также добавить большой массовый отказ от ответственности, который все это относится только к созданию набора инструкций по сборке, который займет точное количество времени. Если то, что вы хотите сделать, это взять какой-то произвольный фрагмент сборки, даже в этих средах, и спросить «Сколько времени это займет для выполнения?», Вы категорически не можете этого сделать - это проблема остановки , которая доказала свою неразрешимость.


РЕДАКТИРОВАТЬ 1: В предыдущей версии этого ответа я утверждал, что Atari 2600 не мог информировать процессор о том, где он находится в телевизионном сигнале, что вынудило его вести подсчет и синхронизацию всей программы с самого начала. Как отмечалось в комментариях, это относится к некоторым системам, таким как ZX Spectrum, но не относится к Atari 2600, поскольку он содержит аппаратный регистр, который останавливает процессор до следующего интервала горизонтального гашения, а также функция для начала вертикального интервала гашения по желанию. Следовательно, проблема подсчета циклов ограничена каждой строкой развертки и становится точной только в том случае, если разработчик желает изменить содержимое по мере отрисовки линии развертки.


4
Следует также отметить, что большинство игр не работали идеально - вы могли увидеть много артефактов в видеовыходе из-за несоответствующей синхронизации видеосигнала, либо из-за ошибки программиста (неправильная оценка тактовой частоты процессора), либо просто из-за слишком большого количества работа, которую нужно сделать. Это также было очень хрупко - если вам нужно исправить ошибку или добавить новые функции, вы, скорее всего, нарушите время, иногда неизбежно. Это было весело, но также и кошмар :) Я даже не уверен, что тактовая частота всегда была точно правильной - например, при перегреве, помехах и т. Д. Но это определенно показывает, что это было трудно даже тогда.
Луан

1
Хороший ответ, хотя я хотел бы придираться к тому, что вам не нужно считать количество циклов для каждой инструкции на Atari 2600. Он имеет две функции, которые помогут вам не делать этого: таймер обратного отсчета, который вы инициализируете, и затем опросите, чтобы увидеть, достиг ли он 0, и регистр, который останавливает ЦП до начала следующего горизонтального гашения. Многие другие устройства, такие как ZX Spectrum, не имеют ничего подобного, и вам действительно нужно подсчитывать каждый отдельный цикл, проведенный после прерывания вертикального гашения, чтобы узнать, где вы находитесь на экране.
Мартин Вилканс

1
Я бы сказал, что проблема Остановки не относится строго к Atari. Если вы исключите возможности ввода-вывода Atari и ограничите его типичным ПЗУ картриджа, то объем хранилища будет конечным. В этот момент у вас есть конечный автомат, поэтому любая программа на нем должна либо остановиться, либо войти в состояние, в которое она входила ранее, что приведет к доказуемому бесконечному циклу за конечное время.
user1937198

2
@ user1937198 128 байтов состояния (плюс все, что находится в регистрах) - это БОЛЬШЕ, чем достаточно пространства состояний, чтобы сделать различие между этим и теоретической бесконечной лентой машины Тьюринга различием, которое имеет значение только в теории. Черт, мы практически не можем искать 128 битов чего-то вроде ключа AES .... Пространство состояний растет очень быстро, когда вы добавляете биты. Не забывайте, что эквивалент «Отключить прерывания; остановка была бы почти наверняка возможна.
Дэн Миллс

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

15

Здесь есть два аспекта

Как указывает @ gnasher729, если мы знаем точные инструкции для выполнения, все еще сложно оценить точное время выполнения из-за таких вещей, как кэширование, предсказание переходов, масштабирование и т. Д.

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

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


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

7
Райс и проблема остановки - это утверждения о произвольных (любых) программах, но здесь ОП указал один конкретный фрагмент кода в вопросе. Вы можете определить семантические и ограничивающие свойства отдельных или ограниченных категорий программ, верно? Просто нет общей процедуры, которая бы охватывала все программы.
Дэниел Р. Коллинз

2
Мы можем окончательно знать , какая команда будет работать дальше, что мы не можем сказать, если мы когда - нибудь ударил , sys_exitи , таким образом , остановить секундомер. Если мы ограничиваемся завершением программ, что является разумным для такого практического вопроса, то тогда ответ на самом деле да (при условии, что у вас есть идеальный снимок состояния, hw и sw, системы непосредственно перед запуском программы).
Маргарет Блум

1
@ BlueRaja-DannyPflughoeft Mov полностью завершен, но не в куске кода, который есть у OP. Но в любом случае это не int
главное

2

Будет ли выбор «компьютерной системы» включать микроконтроллеры? Некоторые микроконтроллеры имеют очень предсказуемое время выполнения, например, 8-разрядные серии PIC имеют четыре тактовых цикла на инструкцию, если только инструкция не разветвляется на другой адрес, не считывает данные из флэш-памяти или не является специальной инструкцией из двух слов.

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

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


2

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

Это абсолютно не будет работать на любом современном компьютере или для кода, как ваш пример.

Почему бы нет? Вот некоторые вещи, которые могут испортить простое, предсказуемое время:

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

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

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

Гонка луча также работала только потому, что программа работала на голом металле и стучала прямо по железу. Здесь вы звоните, int 80чтобы сделать системный вызов. Это передает управление операционной системе, что не дает вам никакой гарантии синхронизации. Затем вы говорите ему выполнить ввод / вывод в произвольном потоке, который мог быть перенаправлен на любое устройство. Слишком абстрактно, чтобы вы говорили, сколько времени занимает ввод-вывод, но он, несомненно, будет доминировать во времени, затрачиваемом на выполнение инструкций.

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


1

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

Самая первая попытка запуска космического челнока была отброшена, когда компьютер Backup Flight Software (BFS) отказался синхронизироваться с четырьмя компьютерами Primary Avionics Software System (PASS). Подробности в "Жуке, услышанном вокруг света" здесь . Увлекательное чтение о том, как программное обеспечение было разработано, чтобы соответствовать циклу за циклом, и может дать вам интересную информацию.


0

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

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

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.