Мой компилятор MIPS сумасшедший, или я сумасшедший для выбора MIPS?
Я использую процессор MIPS (PIC32) во встроенном проекте, но я начинаю сомневаться в своем выборе. Я понимаю, что процессор RISC, такой как MIPS, будет генерировать больше инструкций, чем можно было бы ожидать, но я не думал, что это будет так. Вот фрагмент из списка разборки:
225: LATDSET = 0x0040;
sw s1,24808(s2)
sw s4,24808(s2)
sw s4,24808(s2)
sw s1,24808(s2)
sw s4,24808(s3)
sw s4,24808(s3)
sw s1,24808(s3)
226: {
227: porte = PORTE;
lw t1,24848(s4)
andi v0,t1,0xffff
lw v1,24848(s6)
andi ra,v1,0xffff
lw v1,24848(s6)
andi ra,v1,0xffff
lw v0,24848(s6)
andi t2,v0,0xffff
lw a2,24848(s5)
andi v1,a2,0xffff
lw t2,24848(s5)
andi v1,t2,0xffff
lw v0,24848(s5)
andi t2,v0,0xffff
228: if (porte & 0x0004)
andi t2,v0,0x4
andi s8,ra,0x4
andi s8,ra,0x4
andi ra,t2,0x4
andi a1,v1,0x4
andi a2,v1,0x4
andi a2,t2,0x4
229: pst_bytes_somi[0] |= sliding_bit;
or t3,t4,s0
xori a3,t2,0x0
movz t3,s0,a3
addu s0,t3,zero
or t3,t4,s1
xori a3,s8,0x0
movz t3,s1,a3
addu s1,t3,zero
or t3,t4,s1
xori a3,s8,0x0
movz t3,s1,a3
addu s1,t3,zero
or v1,t4,s0
xori a3,ra,0x0
movz v1,s0,a3
addu s0,v1,zero
or a0,t4,s2
xori a3,a1,0x0
movz a0,s2,a3
addu s2,a0,zero
or t3,t4,s2
xori a3,a2,0x0
movz t3,s2,a3
addu s2,t3,zero
or v1,t4,s0
xori a3,a2,0x0
movz v1,s0,a3
Это похоже на сумасшедшее количество инструкций для простого чтения / записи и проверка переменных по фиксированным адресам. На другой процессор, я могу каждое утверждение с около 1..3 инструкции, не прибегая к рукописному asm. Очевидно, что тактовая частота довольно высока, но это не 10x выше, чем у меня было бы в другом процессоре (например, dsPIC).
У меня оптимизация на максимум. Мой компилятор C ужасен (это gcc 3.4.4)? Или это типично для MIPS?
6 ответов
наконец-то понял ответ. Список разборки полностью вводит в заблуждение. Компилятор выполняет развертывание цикла, и то, что мы видим под каждым оператором C, на самом деле 8x количество инструкций, потому что он разворачивает цикл 8x. Инструкции не находятся по последовательным адресам! Отключение цикла развертывания в параметрах компилятора приводит к следующему:
225: LATDSET = 0x0040;
sw s3,24808(s2)
226: {
227: porte = PORTE;
lw t1,24848(s5)
andi v0,t1,0xffff
228: if (porte & 0x0004)
andi t2,v0,0x4
229: pst_bytes_somi[0] |= sliding_bit;
or t3,t4,s0
xori a3,t2,0x0
movz t3,s0,a3
addu s0,t3,zero
230:
паника по каждому.
Я думаю, что ваш компилятор ведет себя неправильно... Проверьте, например, это утверждение:
228: if (porte & 0x0004)
andi t2,v0,0x4 (1)
andi s8,ra,0x4 (2)
andi s8,ra,0x4 (3)
andi ra,t2,0x4 (4)
andi a1,v1,0x4 (5)
andi a2,v1,0x4 (6)
andi a2,t2,0x4 (7)
очевидно, что есть инструкции, которые в основном ничего не делают. Инструкция (3) не делает ничего нового, как хранит в s8 тот же результат, вычисленный инструкцией (2). Инструкция (6) также не имеет эффекта, так как она переопределяется следующей инструкцией (7), Я считаю, что любой компилятор, который выполняет некоторую фазу статического анализа, по крайней мере удалит инструкции (3) и (6).
похожие анализ будет применяться к другим частям кода. Например, в первом операторе вы можете увидеть, что некоторые регистры (v0 и v0) загружаются с одним и тем же значением дважды.
Я думаю, что ваш компилятор не делает хорошую работу по оптимизации компилируемого кода.
MIPS-это в основном воплощение всего, что было глупо в дизайне RISC. В наши дни x86 (и x86_64) поглотили почти все стоящие идеи из RISC, и ARM эволюционировал, чтобы быть намного более эффективным, чем традиционный RISC, оставаясь при этом верным концепции RISC о сохранении небольшого, систематического набора инструкций.
чтобы ответить на вопрос, я бы сказал, что вы сумасшедшие для выбора MIPS или, что более важно, для выбора его без первого изучения немного о MIPS ISA и почему это так плохо и сколько неэффективности вам нужно мириться, если вы хотите использовать его. Я бы выбрал ARM для маломощных/встроенных систем в большинстве ситуаций, или еще лучше Intel Atom, если вы можете позволить себе немного больше энергопотребления.
Edit: На самом деле, вторая причина, по которой вы можете быть сумасшедшим... Из комментариев кажется, что вы используете 16-битные целые числа. Вы никогда не должны использовать меньше, чемint
типы В C, за исключением массивов или структуры, которая будет выделяться большими числами (либо в массиве, либо каким-либо другим способом, например, связанным списком/деревом/и т. д.). Использование небольших типов никогда не даст никаких преимуществ, кроме экономии места (что не имеет значения, пока у вас нет большого количества значений такого типа) и почти наверняка менее эффективно, чем использование "нормальных" типов. В случае MIPS разница является экстремальной. Перейти int
и посмотреть, если ваша проблема исчезнет.
единственное, о чем я могу думать, это, возможно,возможно компилятор может вводить дополнительные бессмысленные инструкции для сопряжения скорости процессора с гораздо более медленной скоростью шины данных. Даже этого объяснения недостаточно, поскольку инструкции store / load аналогично имеют избыточность.
поскольку компилятор подозревается, не забывайте, что фокусировка усилий в компиляторе может ослепить вас к типу туннельного зрения. Возможно, ошибки скрыты в других частях и цепи инструментов тоже.
где вы взяли компилятор? Я нахожу, что некоторые из "простых" источников часто отправляют некоторые довольно ужасные инструменты. Встроенные друзья по разработке обычно компилируют свою собственную цепочку инструментов с иногда много лучшие результаты.
Я попытался скомпилировать следующий код с CodeSourcery MIPS GCC 4.4-303 с -O4. Я попробовал это с uint32_t и uint16_t:
#include <stdint.h>
void foo(uint32_t PORTE, uint32_t pst_bytes_somi[], uint32_t sliding_bit) {
uint32_t LATDSET = 0x0040;
{
uint32_t porte = PORTE;
if (porte & 0x0004)
pst_bytes_somi[0] |= sliding_bit;
if (porte & LATDSET)
pst_bytes_somi[1] |= sliding_bit;
}
}
вот разборка с целыми числами uint32_t:
uint32_t porte = PORTE;
if (porte & 0x0004)
0: 30820004 andi v0,a0,0x4
4: 10400004 beqz v0,18 <foo+0x18>
8: 00000000 nop
./foo32.c:7
pst_bytes_somi[0] |= sliding_bit;
c: 8ca20000 lw v0,0(a1)
10: 00461025 or v0,v0,a2
14: aca20000 sw v0,0(a1)
./foo32.c:8
if (porte & LATDSET)
18: 30840040 andi a0,a0,0x40
1c: 10800004 beqz a0,30 <foo+0x30>
20: 00000000 nop
./foo32.c:9
pst_bytes_somi[1] |= sliding_bit;
24: 8ca20004 lw v0,4(a1)
28: 00463025 or a2,v0,a2
2c: aca60004 sw a2,4(a1)
30: 03e00008 jr ra
34: 00000000 nop
вот разборка с целыми числами uint16_t:
if (porte & 0x0004)
4: 30820004 andi v0,a0,0x4
8: 10400004 beqz v0,1c <foo+0x1c>
c: 30c6ffff andi a2,a2,0xffff
./foo16.c:7
pst_bytes_somi[0] |= sliding_bit;
10: 94a20000 lhu v0,0(a1)
14: 00c21025 or v0,a2,v0
18: a4a20000 sh v0,0(a1)
./foo16.c:8
if (porte & LATDSET)
1c: 30840040 andi a0,a0,0x40
20: 10800004 beqz a0,34 <foo+0x34>
24: 00000000 nop
./foo16.c:9
pst_bytes_somi[1] |= sliding_bit;
28: 94a20002 lhu v0,2(a1)
2c: 00c23025 or a2,a2,v0
30: a4a60002 sh a2,2(a1)
34: 03e00008 jr ra
38: 00000000 nop
как вы можете видеть, каждый с карты на две-три инструкции. Использование 16-битных целых чисел делает функцию только одной инструкцией длиннее.