C программирование и эффективность переменной error_code - PullRequest
1 голос
/ 02 декабря 2009

Большая часть кода, который я когда-либо читал, использует int для стандартной обработки ошибок (возвращаемые значения из функций и тому подобное). Но мне интересно, есть ли какая-то польза от использования uint_8, если будет компилятор - читай: большинство компиляторов C на большинстве архитектур - выдают инструкции с использованием режима непосредственного адресации, то есть встраивают 1-байтовое целое число в инструкцию? Ключевой инструкцией, о которой я думаю, является сравнение после функции с использованием uint_8 в качестве возвращаемого типа.

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

Делать правильные вещи - вот о чем я думаю. Но, скажем так, ради аргумента, это популярный дешевый микропроцессор для интеллектуальных часов, который сконфигурирован с 1 Кб памяти, но в наборе команд имеет разные режимы адресации: D

Еще один вопрос, чтобы немного специализировать обсуждение (x86), будет: литерал в:

uint_32 x=func(); x==1;

и

uint_8 x=func(); x==1;

того же типа? или компилятор сгенерирует 8-байтовый литерал во втором случае. Если это так, он может использовать ее для генерации инструкции сравнения, в которой литерал является непосредственным значением, а возвращаемое значение int - ссылкой на регистр. См. Типы команд CMP. .

Еще одна ссылка на набор инструкций x86.

Ответы [ 4 ]

4 голосов
/ 02 декабря 2009

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

extern int foo(void) ;
void bar(void)
{
        if(foo() == 31) { //error code 31
                do_something();
        } else {
                do_somehing_else();
        }
}

   0:   55                      push   %ebp
   1:   89 e5                   mov    %esp,%ebp
   3:   83 ec 08                sub    $0x8,%esp
   6:   e8 fc ff ff ff          call   7 <bar+0x7>
   b:   83 f8 1f                cmp    $0x1f,%eax
   e:   74 08                   je     18 <bar+0x18>
  10:   c9                      leave
  11:   e9 fc ff ff ff          jmp    12 <bar+0x12>
  16:   89 f6                   mov    %esi,%esi
  18:   c9                      leave
  19:   e9 fc ff ff ff          jmp    1a <bar+0x1a>

3-байтовая инструкция для cmp. если foo () возвращает символ, мы получаем b: 3c 1f cmp $ 0x1f,% al

Если вы ищете эффективность, хотя. Не думайте, что сравнение в% a1 быстрее, чем в сравнении с% eax

3 голосов
/ 02 декабря 2009

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

И если вы говорите о x86 в приведенном вами примере, вы делаете ложное предположение: немедленный тип должен быть uint8_t.

На самом деле 8-битные непосредственные элементы, встроенные в инструкцию, имеют тип int8_t и могут использоваться с байтами, словами, двойными словами и qwords в нотации C: char, short, int и long long .

Так что в этой архитектуре не будет никакой выгоды, ни размера кода, ни скорости выполнения.

3 голосов
/ 02 декабря 2009

Вы должны использовать типы int или unsigned int для своих расчетов. Использование меньших типов только для соединений (структур / массивов). Причина этого заключается в том, что int обычно определяется как «наиболее естественный» интегральный тип для процессора, а для всех других производных типов может потребоваться обработка для правильной работы. В нашем проекте мы скомпилировали с помощью gcc на Solaris для SPARC случай, когда доступ к 8 и 16-битной переменной добавил инструкцию к коду. При загрузке меньшего типа из памяти он должен был убедиться, что верхняя часть регистра была правильно установлена ​​(расширение знака для подписанного типа или 0 для неподписанного). Это удлинило код и увеличило нагрузку на регистры, что ухудшило другие оптимизации.

У меня есть конкретный пример:

Я объявил две переменные структуры как uint8_t и получил этот код в Sparc Asm:

    if(p->BQ > p->AQ)

было переведено в

ldub    [%l1+165], %o5  ! <variable>.BQ,
ldub    [%l1+166], %g5  ! <variable>.AQ,
and     %o5, 0xff, %g4  ! <variable>.BQ, <variable>.BQ
and     %g5, 0xff, %l0  ! <variable>.AQ, <variable>.AQ
cmp     %g4, %l0    ! <variable>.BQ, <variable>.AQ
bleu,a,pt %icc, .LL586  !

И вот что я получил, когда объявил две переменные как uint_t

lduw    [%l1+168], %g1  ! <variable>.BQ,
lduw    [%l1+172], %g4  ! <variable>.AQ,
cmp     %g1, %g4    ! <variable>.BQ, <variable>.AQ
bleu,a,pt %icc, .LL587  !

На две арифметические операции меньше и на 2 больше регистров для других вещей

1 голос
/ 02 декабря 2009

Процессорам обычно нравится работать с их естественными размерами регистров, которые в C означают 'int'.

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

...