это кусок кода сборки
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
Учитывая конкретную компьютерную систему, можно ли точно предсказать фактическое время выполнения фрагмента кода сборки.
Ответы:
Я могу лишь процитировать из руководства довольно примитивного процессора, процессора 68020, выпущенного примерно в 1986 году: «Вычислить точное время выполнения последовательности инструкций сложно, даже если у вас есть точные знания о реализации процессора». Которого у нас нет. И по сравнению с современным процессором этот процессор был примитивным .
Я не могу предсказать время выполнения этого кода, и вы не можете. Но вы даже не можете определить, что такое «время выполнения» фрагмента кода, когда процессор имеет массивные кэши и огромные возможности выхода из строя. Типичный современный процессор может иметь 200 инструкций «в полете», то есть на разных стадиях исполнения. Таким образом, время от попытки прочитать первый байт инструкции до удаления последней инструкции может быть довольно продолжительным. Но фактическая задержка всей остальной работы, которая требуется процессору, может быть (и обычно) намного меньше.
Конечно, два вызова операционной системы делают это абсолютно непредсказуемым. Вы не знаете, что на самом деле делает запись в стандартный вывод, поэтому вы не можете предсказать время.
И вы не можете знать тактовую частоту компьютера в тот момент, когда вы запускаете код. Может быть, в каком-то режиме энергосбережения компьютер может снизить тактовую частоту, потому что он нагрелся, поэтому даже одинаковое количество тактов может занимать разное количество времени.
В целом: совершенно непредсказуемо.
источник
Вы не можете сделать это в целом, но в некоторых отношениях вы очень можете, и было несколько исторических случаев, в которых вы действительно должны были.
Atari 2600 (или Atari Видеосистема компьютера) был один из самых ранних домашнего видео игровых систем и впервые была выпущена в 1978 г. В отличие от более поздних систем эпохи, Atari не может позволить себе , чтобы дать устройству буфер кадра, а это означает , что процессор имел запускать код на каждой линии сканирования, чтобы определить, что производить - если выполнение этого кода занимает более 17,08 микросекунд (интервал HBlank), графика не будет правильно настроена до того, как линия сканирования начнет их рисовать. Хуже того, если программист хотел нарисовать более сложный контент, чем обычно разрешал Atari, он должен был измерить точное время для инструкций и изменить графические регистры во время прорисовки луча с интервалом 57,29 микросекунд для всей строки сканирования.
Однако Atari 2600, как и многие другие системы, основанные на 6502, имел очень важную функцию, обеспечивающую тщательное управление временем, требуемое для этого сценария: центральный процессор, оперативная память и телевизионный сигнал запускались с тактовой частотой на основе одного и того же мастера. Часы. Телевизионный сигнал проходил с тактовой частоты 3,98 МГц, разделяя приведенное выше время на целое число «цветовых часов», которые управляли телевизионным сигналом, а цикл тактовых импульсов ЦП и ОЗУ составлял ровно три цветовых тактовых сигнала, позволяя такту ЦП быть точная мера времени относительно текущего телевизионного сигнала прогресса. (За дополнительной информацией обращайтесь к Руководству программиста Stella , написанному для эмулятора Stella Atari 2600 ).
Эта операционная среда, кроме того, означала, что каждая инструкция ЦП имела определенное количество циклов, которое потребуется в каждом случае, и многие 6502 разработчика опубликовали эту информацию в справочных таблицах. Например, рассмотрим эту запись для
CMP
инструкции (Сравнить память с аккумулятором), взятой из этой таблицы :Используя всю эту информацию, Atari 2600 (и другие разработчики 6502) смогли точно определить, сколько времени потребовалось для выполнения их кода, и создать подпрограммы, которые выполняли то, что им было нужно, и при этом соответствовали требованиям синхронизации телевизионных сигналов Atari. И поскольку это время было настолько точным (особенно для инструкций, которые тратят время, например, NOP), они даже смогли использовать его для изменения графики во время рисования.
Конечно, 6502 Atari - это очень специфический случай, и все это возможно только потому, что в системе было все следующее:
Все эти вещи собрались вместе, чтобы создать систему, в которой можно было создавать наборы инструкций, которые занимали точное время - и для этого приложения это именно то, что требовалось. Большинство систем не имеют такой степени точности просто потому, что в этом нет необходимости - расчеты либо выполняются, когда они выполняются, либо, если требуется точное количество времени, могут запрашиваться независимые часы. Но если необходимость правильная (например, в некоторых встроенных системах), она все равно может появиться, и вы сможете точно определить, сколько времени потребуется вашему коду для выполнения в этих средах.
И я должен также добавить большой массовый отказ от ответственности, который все это относится только к созданию набора инструкций по сборке, который займет точное количество времени. Если то, что вы хотите сделать, это взять какой-то произвольный фрагмент сборки, даже в этих средах, и спросить «Сколько времени это займет для выполнения?», Вы категорически не можете этого сделать - это проблема остановки , которая доказала свою неразрешимость.
РЕДАКТИРОВАТЬ 1: В предыдущей версии этого ответа я утверждал, что Atari 2600 не мог информировать процессор о том, где он находится в телевизионном сигнале, что вынудило его вести подсчет и синхронизацию всей программы с самого начала. Как отмечалось в комментариях, это относится к некоторым системам, таким как ZX Spectrum, но не относится к Atari 2600, поскольку он содержит аппаратный регистр, который останавливает процессор до следующего интервала горизонтального гашения, а также функция для начала вертикального интервала гашения по желанию. Следовательно, проблема подсчета циклов ограничена каждой строкой развертки и становится точной только в том случае, если разработчик желает изменить содержимое по мере отрисовки линии развертки.
источник
Здесь есть два аспекта
Как указывает @ gnasher729, если мы знаем точные инструкции для выполнения, все еще сложно оценить точное время выполнения из-за таких вещей, как кэширование, предсказание переходов, масштабирование и т. Д.
Однако ситуация еще хуже. Учитывая часть сборки, невозможно знать, какие инструкции будут выполняться, или даже знать, сколько инструкций будет выполнено. Это из-за теоремы Райс: если бы мы могли определить это точно, то мы могли бы использовать эту информацию для решения проблемы остановки, что невозможно.
Код ассемблера может содержать переходы и переходы, которых достаточно, чтобы сделать полный след программы, возможно, бесконечным. Была проведена работа над консервативными приближениями времени выполнения, которые дают верхние границы выполнения, с помощью таких вещей, как семантика затрат или системы аннотированных типов. Я не знаком с чем-то конкретно для сборки, но я не удивлюсь, если что-то подобное существует.
источник
mov
sys_exit
и , таким образом , остановить секундомер. Если мы ограничиваемся завершением программ, что является разумным для такого практического вопроса, то тогда ответ на самом деле да (при условии, что у вас есть идеальный снимок состояния, hw и sw, системы непосредственно перед запуском программы).int
Будет ли выбор «компьютерной системы» включать микроконтроллеры? Некоторые микроконтроллеры имеют очень предсказуемое время выполнения, например, 8-разрядные серии PIC имеют четыре тактовых цикла на инструкцию, если только инструкция не разветвляется на другой адрес, не считывает данные из флэш-памяти или не является специальной инструкцией из двух слов.
Прерывания будут явно нарушать этот вид времени, но можно сделать многое без обработчика прерываний в «голой металлической» конфигурации.
Используя сборку и специальный стиль кодирования, можно написать код, выполнение которого всегда будет занимать одно и то же время. Сейчас не так часто, что большинство вариантов PIC имеют несколько таймеров, но это возможно.
источник
Еще в эпоху 8-битных компьютеров некоторые игры делали что-то подобное. Программисты будут использовать точное время, необходимое для выполнения инструкций, основываясь на количестве времени, которое они занимают, и известной тактовой частоте ЦП, чтобы синхронизироваться с точным временем видео и аудио оборудования. В те времена дисплей представлял собой монитор с электронно-лучевой трубкой, который циклически проходил по каждой строке экрана с фиксированной скоростью и окрашивал этот ряд пикселей, включая и выключая катодный луч, чтобы активировать или деактивировать люминофоры. Поскольку программистам нужно было сообщать видеооборудованию, что отображать непосредственно перед тем, как луч достигнет этой части экрана, и вставить оставшуюся часть кода в оставшееся время, они назвали это «гонкой луча».
Это абсолютно не будет работать на любом современном компьютере или для кода, как ваш пример.
Почему бы нет? Вот некоторые вещи, которые могут испортить простое, предсказуемое время:
Скорость процессора и выборка памяти являются узкими местами во время выполнения. Это пустая трата денег, чтобы запустить процессор быстрее, чем он может получить инструкции для выполнения, или установить память, которая может доставлять байты быстрее, чем процессор может их принять. Из-за этого старые компьютеры работали с одинаковыми часами. Современные процессоры работают намного быстрее основной памяти. Они управляют этим, имея кеш инструкций и данных. Процессор все равно остановится, если ему когда-нибудь понадобится дождаться байтов, которых нет в кэше. Поэтому те же инструкции будут выполняться намного быстрее, если они уже находятся в кэше, чем если бы они не были.
Кроме того, современные процессоры имеют длинные конвейеры. Они сохраняют свою высокую пропускную способность, заставляя другую часть чипа выполнять предварительную работу над следующими несколькими инструкциями в конвейере. Это не удастся, если процессор не знает, какой будет следующая инструкция, что может произойти, если есть ветвь. Поэтому процессоры пытаются предсказать условные переходы. (У вас нет этого фрагмента кода, но, возможно, произошел непредсказуемый условный переход к нему, который засорил конвейер. Кроме того, хороший повод связать этот легендарный ответ.) Точно так же системы, которые вызывают
int 80
перехват в режиме ядра, на самом деле используют сложную функцию процессора, шлюз прерывания, который вводит непредсказуемую задержку.Если ваша ОС использует вытесняющую многозадачность, поток, выполняющий этот код, может в любой момент потерять свой временной интервал.
Гонка луча также работала только потому, что программа работала на голом металле и стучала прямо по железу. Здесь вы звоните,
int 80
чтобы сделать системный вызов. Это передает управление операционной системе, что не дает вам никакой гарантии синхронизации. Затем вы говорите ему выполнить ввод / вывод в произвольном потоке, который мог быть перенаправлен на любое устройство. Слишком абстрактно, чтобы вы говорили, сколько времени занимает ввод-вывод, но он, несомненно, будет доминировать во времени, затрачиваемом на выполнение инструкций.Если вам нужна точная синхронизация в современной системе, вам нужно ввести цикл задержки. Вы должны заставить более быстрые итерации выполняться со скоростью самых медленных, обратное невозможно. Одна из причин, по которой люди делают это в реальном мире, состоит в том, чтобы предотвратить утечку криптографической информации злоумышленнику, который может рассчитывать, что запросы занимают больше времени, чем другие.
источник
Это несколько тангенциально, но у космического челнока было 4 резервных компьютера, которые зависели от точной синхронизации, то есть от точного соответствия времени выполнения.
Самая первая попытка запуска космического челнока была отброшена, когда компьютер Backup Flight Software (BFS) отказался синхронизироваться с четырьмя компьютерами Primary Avionics Software System (PASS). Подробности в "Жуке, услышанном вокруг света" здесь . Увлекательное чтение о том, как программное обеспечение было разработано, чтобы соответствовать циклу за циклом, и может дать вам интересную информацию.
источник
Я думаю, что мы смешиваем две разные проблемы здесь. (И да, я знаю, что это было сказано другими, но я надеюсь, что смогу выразить это более четко.)
Сначала нам нужно перейти от исходного кода к последовательности фактически выполняемых инструкций (которая требует знания входных данных, а также кода - сколько раз вы обходите цикл? Какая ветвь берется после теста? ). Из-за проблемы остановки последовательность инструкций может быть бесконечной (не прекращение), и вы не всегда можете определить это статически, даже со знанием входных данных.
Установив последовательность инструкций для выполнения, вы затем хотите определить время выполнения. Это, безусловно, можно оценить с некоторыми знаниями об архитектуре системы. Но проблема в том, что на многих современных машинах время выполнения сильно зависит от кэширования выборок памяти, что означает, что оно зависит как от входных данных, так и от выполняемых инструкций. Это также зависит от правильного угадывания условных направлений ветвления, что опять-таки зависит от данных. Так что это будет только оценка, она не будет точной.
источник