Как процессор обрабатывает условия? - PullRequest
5 голосов
/ 25 марта 2009

Итак, SUPER низкоуровневый, как выглядит IF (), как он обрабатывается процессором x86?

Ответы [ 7 ]

13 голосов
/ 25 марта 2009

Процессор имеет инструкции "Ветвление, если", что при выполнении определенного условия он разветвляется, а в противном случае он переходит к следующей инструкции.

Так

if(A)
{
    dosomething;
}

станет

load A into register 0
if the zero flag is set (ie, register 0 contains 0x00) then jump to endcondition)
dosomething
endcondition:

Более сложные условия (if(A || B && C)) становятся последовательностью инструкций, которая оставляет регистр в нулевом или нулевом состоянии, поэтому команда branchif может переходить или не переходить на основе условных флагов.

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

Как указывает Муча в комментариях, некоторые архитектуры позволяют применять условные выражения к некоторым, многим или даже ко всем инструкциям, поэтому вы можете иметь не только инструкции «ветвь если», но также «и если», добавить if ',' move if 'и т. д.

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

Это тема для другого вопроса, хотя ...

-Adam

5 голосов
/ 25 марта 2009

Довольно просто использовать выходные данные компилятора C (используйте переключатель -S на gcc), чтобы увидеть, какой вывод будет генерировать данный фрагмент кода C при компиляции. Будьте осторожны при использовании оптимизации в игрушечных программах. Если вы не будете осторожны, оптимизатор часто оптимизирует условия, которые всегда будут идти в ту или иную сторону (более подробное объяснение см. в этой статье о микробенчмарках ).

Например, тривиальная программа на C:

#include <stdio.h>

int main (int argc, char **argv) {
    int ii = 10;
    int jj = 20;
    if (jj > ii) {
        puts ("jj > ii \n");
    }
    return 0;
}

компилируется на следующий язык ассемблера:

    .file   "foo.c"
    .section    .rodata
.LC0:
    .string "jj > ii \n"
    .text
.globl main
    .type   main, @function
main:
    leal    4(%esp), %ecx
    andl    $-16, %esp
    pushl   -4(%ecx)
    pushl   %ebp
    movl    %esp, %ebp
    pushl   %ecx
    subl    $20, %esp
    movl    $10, -8(%ebp)
    movl    $20, -12(%ebp)
    movl    -12(%ebp), %eax
    cmpl    -8(%ebp), %eax
    jle .L2
    movl    $.LC0, (%esp)
    call    puts
.L2:
    movl    $0, %eax
    addl    $20, %esp
    popl    %ecx
    popl    %ebp
    leal    -4(%ecx), %esp
    ret
    .size   main, .-main
    .ident  "GCC: (Ubuntu 4.3.2-1ubuntu12) 4.3.2"
    .section    .note.GNU-stack,"",@progbits

Для краткого изучения происходящего:

  • В первом разделе (.rodata) объявляется константа со строкой 'jj > ii \n')

  • Второй раздел инициализирует содержимое переменных ii и jj в стеке.

  • Бит из cmpl -8(%ebp), %eax выполняет фактическое сравнение; инструкция jle пропускает вызов к puts, что по сути является логикой обратного оператора if.

  • После метки '.L2' система убирает верх стека и возвращается из вызова.

2 голосов
/ 25 марта 2009

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

1 голос
/ 25 марта 2009

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

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

Один код операции (на самом деле их, вероятно, несколько), однако позволяет процессору «Разветвляться», «сравнивая» значения в двух других регистрах процессора, и, если один больше другого, он копирует один адрес памяти в регистр команд, тогда как, если другой является наибольшим, он копирует второй, другой адрес памяти в регистр команд.

Это примерно такой же "низкий" уровень, каким его можно выразить, не говоря о реле и транзисторах ...

0 голосов
/ 25 марта 2009

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

Например, код:

int x;

if ( y < 5 )
  x = 5;
else
  x = y;

может быть скомпилировано, как если бы было написано:

y -= 5
int r = y < 0; // r is 1 if y < 5, 0 otherwise
r -= 1         // r is 0x00000000 if y < 5, 0xffffffff otherwise
x = y & r      // x is 0 if y < 5, (y-5) otherwise 
x += 5;        // x is 5 if y < 5, y otherwise

, который можно преобразовать в машинный код без каких-либо ветвей

0 голосов
/ 25 марта 2009

Вот довольно хороший обзор того, как такие структуры могут компилироваться в архитектуре x86: http://en.wikibooks.org/wiki/X86_Disassembly/Branches#If-Then

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

if (a==0) {
    b= 1;
}

примерно так:

cmp    0, eax
cmovzl ebx, 1

без веток, если ваш компилятор настроен на работу с i686 + (и похоже, компиляторы сложны и непостижимы). SET [условие] - еще одна похожая условная инструкция.

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

0 голосов
/ 25 марта 2009

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

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

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

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

...