Враховуючи конкретну комп'ютерну систему, чи можна оцінити фактичний точний час виконання фрагмента коду складання


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
Чи це теж неможливо через проблему зупинки? Ми можемо довести якийсь код, чи він зупиниться, але ми не можемо мати алгоритм, який визначає це для всіх можливих кодів.
kutschkem

2
@Falco Це було б властивістю даної системи. Деякі самостійно реалізовані програми C не мають операційної системи; все, що працює - це основний цикл (або навіть не цикл ;-)), який може або не може читати з апаратних адрес для введення.
Пітер - Відновіть Моніку

Відповіді:


47

Я можу навести лише цитування з посібника досить примітивного процесора, процесора 68020 приблизно з 1986 року: "Обчислити точну тривалість виконання послідовності інструкцій важко, навіть якщо ви точно знаєте виконання процесора". Яких у нас немає. І порівняно з сучасним процесором, цей процесор був примітивним .

Я не можу передбачити час виконання цього коду, і ви не можете. Але ви навіть не можете визначити, що таке "час виконання" фрагмента коду, коли процесор має масивні кеші та масивні можливості поза строком. Типовий сучасний процесор може мати 200 інструкцій "у польоті", тобто на різних етапах виконання. Тому час від спроби прочитати байт першої інструкції, до виходу з останньої інструкції може бути досить довгим. Але фактична затримка з усіма іншими роботами, які потребує виконання процесора, може бути (і, як правило, набагато менше).

Звичайно, виконання двох дзвінків в операційну систему робить це абсолютно непередбачуваним. Ви не знаєте, що насправді робить "писання в stdout", тому не можете передбачити час.

І ви не можете знати тактову частоту комп'ютера в той самий момент, коли запустили код. Це може бути в деякому режимі енергозбереження, у комп'ютера може бути знижена тактова частота, оскільки він нагрівся, тому навіть однакова кількість тактових циклів може зайняти різну кількість часу.

Загалом: абсолютно непередбачувано.


12
Я думаю, ваші висновки занадто сильні. Затримка та пропускна здатність є загальними показниками для вимірювання "часу виконання" програми. Також ви можете просто зупинитися на відповідному визначенні "час виконання". Крім того, якщо у вас є повний знімок стану системи, hw та sw та досконалі знання внутрішніх процесорів, ви можете передбачити час виконання. У Intel вони, ймовірно, можуть оцінити час виконання, навіть тут, на SO, ми можемо передбачити затримки та tputs з точністю циклу. У цьому випадку, окрім системних дзвінків, це навіть не так складно.
Маргарет Блум

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), вони навіть змогли використовувати його для зміни графіки під час їх малювання.


Звичайно, 650 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 і обмежуєте їх типовим картриджним ROM, то накопичувач є Кінцевим. У цей момент у вас є машина з кінцевим станом, тому будь-яка програма на ній повинна або зупинятись, або вводити стан, який вона вводила раніше, приводячи до дозволеного нескінченного циклу в кінцевий час.
користувач1937198

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

1
"Це проблема зупинки, яка виявилася нерозв'язною. Якщо ви зіткнулися з цим, вам потрібно вимкнути секундомір і фактично запустити свій код." - це не має сенсу. Ви не можете уникнути доказів Тьюрінга, "фактично" запустивши код, а не моделюючи його. Якщо вона зупиниться, ви можете час, коли потрібно зупинитися. Якщо це не зупиняється, ви ніколи не можете бути впевнені (загалом), чи це зупиниться в майбутньому, чи запуститься назавжди. Це та сама проблема з реальним або змодельованим секундоміром. Принаймні, під час імітації ви можете легше перевірити внутрішній стан на предмет ознак циклічності.
benrg

15

Тут є два аспекти

Як зазначає @ gnasher729, якщо ми знаємо точні вказівки для виконання, все ще важко оцінити точний час виконання через такі речі, як кешування, прогнозування гілок, масштабування тощо.

Однак ситуація ще гірша. Враховуючи шматок складання, неможливо знати, які інструкції будуть виконуватись, або навіть знати, скільки інструкцій буде виконуватися. Це пояснюється теоремою Райса: якби ми могли це точно визначити, то ми могли б використати цю інформацію для вирішення проблеми зупинки, що неможливо.

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


4
Я маю на увазі, Проблема зупинки застосовується безпосередньо тут, оскільки якби ми знали час виконання, ми б знали, чи зупиняється він. Також факт, що немає умов, тут навіть не допомагає, оскільки в x86 mov- Тьюрінг-Повний
BlueRaja - Danny Pflughoeft

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

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

1
@ BlueRaja-DannyPflughoeft Mov є завершеним, але це не в коді коду, який тут має ОП. Але це, окрім int
суті,

2

Чи станеться вибір "комп'ютерної системи", що включає мікроконтролери? Деякі мікроконтролери мають дуже передбачувані терміни виконання, наприклад, 8-бітна серія PIC має чотири тактових цикли за інструкцію, якщо інструкція не ведеться до іншої адреси, не читає з флеш-пам'яті або не є спеціальною двословною інструкцією.

Перерви вразливо порушать цей тип часу, але можна багато зробити без обробника перерв у конфігурації "голого металу".

Використовуючи складання та спеціальний стиль кодування, можна написати код, який завжди займе один і той же час. Зараз це не так часто, що у більшості варіантів PIC є кілька таймерів, але це можливо.


2

Ще в епоху 8-бітних комп'ютерів деякі ігри робили щось подібне. Програмісти використовуватимуть точну кількість часу, необхідного для виконання вказівок, виходячи з часу, який їм потрібно, та відомої тактової частоти процесора, щоб синхронізуватись із точними термінами відео- та аудіоапаратури. Ще в ті часи дисплей являв собою монітор з катодною променевою трубкою, який проходив би по кожному рядку екрана з фіксованою швидкістю і малював цей ряд пікселів, включаючи і вимикаючи катодний промінь, щоб активувати або дезактивувати люмінофори. Оскільки програмістам потрібно було сказати відеопрограмному забезпеченню, що потрібно відображати до того, як промінь досягне тієї частини екрана, і вписати решту коду в будь-який час, що залишився, вони назвали це "гоночним променем".

Це абсолютно не працюватиме на будь-якому сучасному комп’ютері чи для коду, як ваш приклад.

Чому ні? Ось деякі речі, які б зіпсували прості, передбачувані терміни:

Швидкість процесора та вибір пам'яті - це вузькі місця на час виконання. Марно витрачати гроші на запуск центрального процесора швидше, ніж він може отримати інструкції щодо виконання, або встановити пам'ять, яка може доставляти байти швидше, ніж процесор може їх прийняти. Через це старі комп’ютери працювали з обома годинниками. Сучасні процесори працюють набагато швидше, ніж основна пам'ять. Вони керують цим, керуючись кешами інструкцій та даних. Центральний процесор все ще зупиниться, якщо йому коли-небудь доведеться чекати байтів, яких немає в кеші. Тому ті самі вказівки будуть працювати набагато швидше, якщо вони вже є в кеші, ніж якщо їх немає.

Крім того, сучасні процесори мають довгі конвеєри. Вони продовжують свою високу пропускну здатність, маючи іншу частину мікросхеми, попередньо проводячи наступні декілька інструкцій на конвеєрі. Це не вдасться, якщо процесор не знає, що буде наступна інструкція, що може статися, якщо є відділення. Тому процесори намагаються передбачити умовні стрибки. (У цьому фрагменті коду у вас немає жодного, але, можливо, стався непередбачуваний умовний стрибок до нього, який засмітив конвеєр. Крім того, хороший привід пов’язати цю легендарну відповідь.) Аналогічно, системи, які закликають int 80перейти в режим ядра, насправді використовують складну функцію процесора, ворота переривання, що вводить непередбачувану затримку.

Якщо ваша ОС використовує попереджувальну багатозадачність, потік, що виконує цей код, може втратити часовий відрізок у будь-який момент.

Перегони променя також спрацьовували лише тому, що програма працювала на голому металі та стукала прямо на обладнання. Тут ви телефонуєте int 80зробити системний дзвінок. Це передає контроль операційній системі, що не дає гарантії часу. Потім ви повідомляєте це I / O в довільному потоці, який, можливо, був перенаправлений на будь-який пристрій. Вам занадто абстрактно, щоб сказати, скільки часу займає введення-виведення, але це, безумовно, буде домінувати над витраченим часом на виконання інструкцій.

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


1

Це дещо дотично, але космічний човник мав 4 надлишкових комп’ютери, які залежали від точного синхронізації, тобто їх точно збігався час виконання.

Найперша спроба запуску космічного човника була очищена, коли комп'ютер програмного забезпечення резервного польоту (BFS) відмовився синхронізуватися з чотирма комп'ютерами ПАВС. Деталі в "Почуття помилок навколо світу" тут . Захоплююче читайте про те, як програмне забезпечення було розроблено для відповідності циклу циклу та може дати вам цікавий досвід.


0

Я думаю, ми тут змішуємо дві різні проблеми. (Так, я знаю, що це вже говорили інші, але сподіваюся, що я можу це висловити більш чітко.)

Спочатку нам потрібно перейти від вихідного коду до послідовності інструкцій, яка фактично виконується (для чого потрібні знання вхідних даних, а також код - скільки разів ви обходите цикл? Яка гілка береться після тесту? ). Через проблему зупинки послідовність інструкцій може бути нескінченною (не припиняється), і ви не завжди можете визначити це статично, навіть маючи знання вхідних даних.

Встановивши послідовність інструкцій, які потрібно виконати, ви хочете визначити час виконання. Це, безумовно, можна оцінити за допомогою деяких знань про архітектуру системи. Але проблема полягає в тому, що на багатьох сучасних машинах час виконання сильно залежить від кешування даних пам’яті, а значить, це залежить стільки ж від вхідних даних, скільки від виконаних інструкцій. Це також залежить від правильного відгадування умовних напрямків відгалуження, що знову ж таки залежить від даних. Тож це буде лише оцінка, це не буде точною.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.