Почему для малых целых чисел в MSP430-GCC предпочтительнее int, чем unsigned char
В руководстве msgpcc (GCC для микроконтроллеров MSP430) авторы писали:
Используйте int вместо char или unsigned char, если вы хотите небольшое целое число внутри функции. Созданный код будет более эффективным, и в большинстве случаев хранение фактически не теряется.
Зачем int
эффективнее?
UPD. И почему (u)int_fast8_t
в mspgcc, определенном для (unsigned) char
не (unsigned) int
, Насколько я понимаю, (u)int_fast*_t
должен быть определен для наиболее эффективного типа с достаточным размером.
4 ответа
В общем, это не обязательно относится к этому процессору, оно связано с расширением и маскированием знака, что требует дополнительных инструкций для точной реализации исходного кода на языке C. 8-битное значение со знаком в 16-, 32- или 64-битном процессоре МОЖЕТ включать дополнительные инструкции для расширения знака. 8-битное добавление на 32-битном процессоре может потребовать дополнительных инструкций для и с 0xFF и т. Д.
Вы должны провести несколько простых экспериментов, это заняло несколько итераций, но я быстро нашел что-то, что показало разницу.
unsigned int fun ( unsigned int a, unsigned int b )
{
return(a+b)<<3;
}
unsigned char bfun ( unsigned char a, unsigned char b )
{
return(a+b)<<3;
}
int sfun ( int a, int b )
{
return(a+b)<<3;
}
char sbfun ( char a, char b )
{
return(a+b)<<3;
}
производит
00000000 <fun>:
0: 0f 5e add r14, r15
2: 0f 5f rla r15
4: 0f 5f rla r15
6: 0f 5f rla r15
8: 30 41 ret
0000000a <bfun>:
a: 4f 5e add.b r14, r15
c: 4f 5f rla.b r15
e: 4f 5f rla.b r15
10: 4f 5f rla.b r15
12: 30 41 ret
00000014 <sfun>:
14: 0f 5e add r14, r15
16: 0f 5f rla r15
18: 0f 5f rla r15
1a: 0f 5f rla r15
1c: 30 41 ret
0000001e <sbfun>:
1e: 8f 11 sxt r15
20: 8e 11 sxt r14
22: 0f 5e add r14, r15
24: 0f 5f rla r15
26: 0f 5f rla r15
28: 0f 5f rla r15
2a: 4f 4f mov.b r15, r15
2c: 30 41 ret
Msp430 имеет версии инструкций в виде слова и байта, так что простое сложение или вычитание не должно делать отсечение или расширение знака, которое вы ожидаете при использовании переменных размером меньше регистра. Как программист, мы могли бы знать, что собирались кормить sbfun только очень маленькими числами, но компилятор не должен и должен добросовестно реализовывать наш код в письменном виде, генерируя больше кода между sfun и sbfun. Нетрудно провести эти эксперименты с различными компиляторами и процессорами, чтобы увидеть это в действии, единственная уловка - создать код, который процессор не имеет простых инструкций для решения.
другой пример
unsigned int fun ( unsigned int a, unsigned int b )
{
return(a+b)>>1;
}
unsigned char bfun ( unsigned char a, unsigned char b )
{
return(a+b)>>1;
}
производит
00000000 <fun>:
0: 0f 5e add r14, r15
2: 12 c3 clrc
4: 0f 10 rrc r15
6: 30 41 ret
00000008 <bfun>:
8: 4f 4f mov.b r15, r15
a: 4e 4e mov.b r14, r14
c: 0f 5e add r14, r15
e: 0f 11 rra r15
10: 4f 4f mov.b r15, r15
12: 30 41 ret
Общее практическое правило заключается в том, что процессоры быстрее всего работают с целыми числами их собственного размера слова.
Это, конечно, полностью зависит от архитектуры, см. Ответы на этот похожий вопрос для получения дополнительной информации по этому вопросу.
TI опубликовала примечание по применению для этой темы для своих микроконтроллеров Tiva-C (формально Stellaris).
В разделе "Введение" в таблице представлен список факторов, влияющих на производительность и размер. Метка фактора Размер переменной гласит, что "использование переменных меньше оптимальных может означать дополнительные инструкции для подписи или беззнакового расширения...".
Также в разделе "Размер переменных" указано:
"Когда локальные переменные меньше размера регистра, то обычно требуется дополнительный код. На части Stellaris это означает, что локальные переменные размера байта и полуслова (char и short int соответственно) требуют дополнительного кода. Так как код перенесен из В 8-битном или 16-битном микроконтроллере могут быть преобразованы локальные объекты в меньшие размеры (чтобы избежать слишком большой проблемы), это означает, что такой код будет работать медленнее и занимать больше места для кода, чем необходимо ".
Пожалуйста, смотрите: http://www.ti.com/lit/an/spma014/spma014.pdf
Следующее обрабатывается компилятором, но все еще актуально для рассматриваемой проблемы:
MSP430 - это 16-битный микропроцессор. Символ только 8-битный и требует упаковки, чтобы все слова были выровнены. Например, 3 символа не будут правильно выровнены в памяти. Вместо этого используйте целое число, которое является 16-разрядным и всегда будет выровнено.
Когда вы используете переменные размеры, кратные 16 (например, 16 и 32), вы также можете использовать память более эффективно. Вы не закончите с отступами, чтобы выровнять память.
int
соответствует собственному размеру рассматриваемого процессора (16 бит), поэтому при запросе магазина unsigned char
переменной, компилятору, возможно, придется выдавать дополнительный код, чтобы гарантировать, что значение находится между 0 и 255.