2016-05-31 4 views
2

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

Вот ссылка на инструкцию ррс:

https://www.ibm.com/support/knowledgecenter/ssw_aix_53/com.ibm.aix.aixassem/doc/alangref/bc.htm

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

unsigned int ctr = n; 
while(ctr--) 
    a[ctr] += b[ctr]; 

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

Другой подход:

unsigned int ctr = n+1; 
while(--ctr) { 
    a[ctr-1] += b[ctr-1]; 
} 

Филиала в настоящее время произойдет после декремента, но есть постоянный роуминг сделать уродливый код. Переменная «index», которая меньше, чем счетчик, заставит ее выглядеть немного красивее, я думаю. Рассматривая доступные команды ppc, дополнительный расчет при поиске а и b-адреса может по-прежнему соответствовать одной команде, так как загрузка также может выполнять арифметику адреса (добавить). Не так уверен в других наборах инструкций. Моя основная проблема заключается в том, что если n + 1 больше макс. Q: Будет ли декремент возвращать его до максимума и петли, как обычно?

В: Существует ли более распространенный шаблон в C для разрешения общей инструкции?


Редактировать: ARM имеет операцию декремента и ветвления, но ветви, только если значение не равно нулю. Кажется, что есть дополнительное условие, подобное ppc bc. Как я вижу, это с точки зрения C, это очень одно и то же, поэтому я ожидаю, что фрагмент кода будет скомбинирован с этой формой без какого-либо стандартного нарушения C. http://www.heyrick.co.uk/armwiki/Conditional_execution


Edit: Intel имеет практически тот же разветвление инструкцию как ARM: http://cse.unl.edu/~goddard/Courses/CSCE351/IntelArchitecture/InstructionSetSummary.pdf

+0

Считываемость высокая? Считываемость настолько высока, что до/пост приращение/декремент были удалены из Swift3. Я бы попробовал memcpy или memmove. – gnasher729

+0

У меня лично нет проблем с приращением до/после. memcpy/memmove не является вариантом: он не копирует, он добавляет значения ('+ =' вместо '='). – Aconcagua

ответ

1

Что об этом:

do 
{ 
    a[ctr] += b[ctr]; 
} 
while(--ctr); 

Вам потребуется дополнительная проверка, однако:

if(n != 0) 
{ 
    /*...*/ 
} 

если вы не можете гарантировать это другими способами ...

О, и быть в курсе, что ИЕ различные конечных значения в зависимости от какого цикла варианта вы выбираете (0 в моем и вашем втором, ~ 0 в вашем первом) ...

2

Это будет зависеть от усилия авторов оптимизации вашего компилятора.

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

loop: 
    blah 
    blah 

    bdz ... out 
    b loop 
out: 

Гораздо вероятнее будет декремент и ветвь, если не равен нулю, что также поддерживает КПП.

loop: 
    blah 
    blah 

    bdnz ... loop 

fallthru: 

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

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

+0

Как они управляют n = 0? Ваш ответ дал больше идей. У меня была идея, прежде чем условная ветвь всегда была в начале цикла. Теперь я не уверен. Имея условный поиск нуля перед циклом и проверку ветвей состояния, если он должен быть запущен снова, может быть более полезным в зависимости от того, существует ли общий размер петли прокситора и сколько команд выбираются одновременно. Фу, я начинаю сожалеть, задавая вопрос в первую очередь. Столько внимания. – Andreas

+0

Как-то похоже на do-while, не так ли? – Aconcagua

+1

В тот день, когда dec/bnz был новым и классным, а шины данных были 16 бит в ширину, макс, было много компиляторов, которые генерировали циклы, которые в основном начался с «прыжка на дно», затем в нижней части цикла был указатель «декремент-ветвь-ноль-ноль-топ». (Это было до того, как промахи кеша стали доминирующим фактором в генерации кода.) По сути, цикл do-while был «естественной» формой, а while и для циклов «делали» циклы с прыжком вниз вверху , :-) –

2

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

Поскольку вы связываете ссылку AIX, я предполагаю, что вы используете xlc. У меня нет доступа к машине AIX, но у меня есть доступ к xlc на машине Z. Эквивалентная Z-копия является инструкцией Branch On Count (BCTR).

Я попробовал 5 примеров и проверил списки

int len = strlen(argv[1]); 
//Loop header 
argv[1][counter] += argv[2][counter]; 

со следующими заголовками цикла:

for (int i = 0; i < len; i++) 
for (int i = len-1; i >= 0; i--) 
while(--len) 
while(len--) 
while(len){ 
    len--; 

Все 5 примеров использования сук, на счете в -O1 и выше, и ни один из них не используют он на opt 0.

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

+0

Все ли примеры генерируют одну и ту же сборку? Потому что это было бы здорово. Btw while (- len) должен следить за тем, чтобы начать с нуля. – Andreas

+0

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