2010-10-16 5 views
0
typedef unsigned char uChar; 
typedef signed char sChar; 

typedef unsigned short uShort; 
typedef signed short sShort; 

typedef unsigned int uInt; 
typedef signed int sInt; 

typedef unsigned long uLong; 
typedef signed long sLong; 

У меня есть список typedefs, поэтому, когда я определяю переменные, я могу быть точным. Например, если мне нужны только цифры 0-5, я бы использовал uChar. Но я работаю на C++ и делаю двигатель. Я читал о booleans на .NET, занимая X байты, и из-за выравнивания памяти было бы быстрее использовать ints.Производительность против правильности/предпочтения?

Есть ли причина использовать int, а не uChar из-за выравнивания памяти, производительности или такого?

+3

Попробуйте использовать boost.cstdint вместо ваших определений типов – Abyx

+0

Даже не используют TYPEDEF годов они просто в путь и ничего не спасать. – GManNickG

+0

, а не uchar вы можете, t type unsigned char? Вы всегда можете измерить размер объекта с SizeOf() –

ответ

13

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

+1

Да, что говорит Duffymo. Очень маловероятно, что это даже отразится на первой десятке проблем производительности, которые у вас есть для проекта. –

+0

Аминь брат. , , – Cliff

+0

Смешно, что мой единственный ответ проголосовали. Похоже на единодушное мнение. – duffymo

10
  • Эти типефы не более точные, чем то, что они называют. Они больше terse, но нестандартный.
  • Если вы хотите быть более точным, используйте #include <stdint.h>, чтобы получить int8_t, uint32_t и т.д.
  • Если вам нужно беспокоиться о выравнивании памяти, вы узнаете, с помощью других средств.
  • Если вам нужно сохранить большое количество логических элементов, просмотрите std::bitset и std::vector<bool>.
  • Если вам нужно хранить один Boolean, используйте bool!
5

Вы действительно не хотите тратить время на вещи, которые не находятся на критическом пути. После того, как у вас есть что-то работающее, вы должны просмотреть профиль и посмотреть, где проблемы. Затем вы можете ускорить возникновение проблем.

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

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

1

Макет прототипа и использование профайлера на нем, прежде чем вы даже начнете думать о микро-оптимизации perf. Помните: если он является постоянным (или даже небольшим изменением коэффективности), Big-O рассматривает его одинаково.

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

Кроме того, неявные слепки делают ошибки более вероятными при использовании неподписанных типов.

Например:

#include<iostream> 

void SomeFunction(uint32_t value) 
{ 
    if(value < 0) 
    { 
    // unreachable code. What do we do instead? 
    throw std::runtime_error("value must be non-negative"); 
    } 
} 

uint32_t SomeOtherFunction() 
{ 
    return (uint32_t)2000000000 + (uint32_t)2000000000; 
} 

int main(int argc, char* argv[]) 
{ 
    int someValue = -1; 
    SomeFunction(someValue); 
    someValue = SomeOtherFunction(); 
    std::cout << someValue; 
} 

-294967296

1

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

 
    uInt16 var1; 
    Int32 var2; 

    var1++; 
    var2 = var1; 

Процессор ARM я использую, имеет только 32-разрядные инструкции для операций регистра, но может сделать 8, 16 и 32-разрядные нагрузки и магазинов. Если var1 находится в памяти, его можно использовать так же хорошо, как если бы это было 32-битное целое число, но если он находится в регистре, компилятор должен будет добавить инструкцию, чтобы очистить верхнее слово, прежде чем копировать его в var2. Если var1 был подписанным 16-разрядным целым числом, загрузка его из памяти будет медленнее, чем если бы он был без знака (из-за необходимого расширения знака), но если он хранился в регистре, компилятору не потребовалось бы беспокоиться о верхнем биты.

1

Да, использование int вместо char часто приводит к заметному харизу. Таким образом, использование int языком C, чтобы попытаться сопоставить собственный размер регистров в процессоре.

Рекомендуется использовать неподписанные ints везде, где это возможно, и только по редкому/конкретному случаю использовать что-то, отличное от unsigned int. Избегайте использования чего-то меньшего, чем int, если у вас нет действительно веской причины. Если вы пытаетесь использовать меньше, чем объекты int, чтобы получить некоторую производительность халявы по привычке программирования, вам нужно изменить свою привычку другим способом. То же самое касается unsigned, используйте unsigned что угодно, если у вас нет действительно веских оснований для использования подписанных.

В основном разобрать (или скомпилировать в asm), посмотреть на то, что генерируют ваши любимые и другие компиляторы, заметить неравномерную адресацию, вызванную символами, отметить маскировку верхних битов, расширение знака для подписанных символов и т. Д. Эти иногда свободны, а иногда и не зависят от того, откуда этот байт идет и на платформу. Также попробуйте как минимум x86 и руку, возможно, mips, gcc 3.x, 4.x и llvm. В частности, обратите внимание, как один символ, смешанный со списком int в строке деклараций, может привести к тому, что ints не будут выровнены, что отлично подходит для x86 с точки зрения адреса, но будет стоить в производительности (на x86 даже с кеш). Сначала поместите ваши выровненные переменные, а затем не выровнены последними. Другие платформы, которые не могут или не хотят делать неприсоединенные обращения, будут тратить лишние байты в качестве дополнения, так что вы не обязательно сохраняете память. Преждевременная оптимизация пытается настроить на переменную длину. Используйте простые привычки, такие как использование unsigned ints для всего, если у вас нет конкретной причины, сначала помещайте ваши большие, выровненные, переменные и структуры в список объявлений, а неглавные - последние (shorts then chars).

Умножает (и делит) делает эту привычку уродливой, избегая умножения и деления кода - лучшая привычка иметь. Если вы должны использовать один, достаточно хорошо осведомлены о его реализации. Гораздо лучше размножать два символа вместо двух ints, например (если номера поддерживают его), поэтому, если вы знаете, что ints действительно 7 бит или 5 бит или любые количества, приведите их в порядок для умножения и разрешите аппаратное умножение произойдет вместо мягкого умножения. (может быть спящий баг, если эти переменные размеры меняются !!). Несмотря на то, что многие процессоры имеют аппаратное умножение, очень редко можно использовать его напрямую. Если вы не поможете компилятору, он должен сделать вызов библиотеки, чтобы проверить переполнение между прочим, и может в конечном итоге сделать мягкое умножение в результате очень дорогостоящим. Разделения плохи, потому что большинство процессоров не включают разделение. И если они это сделают, вы можете попасть в ту же ловушку. с умножением бит N бит * N бит превращается в 2 * N бит результата, в котором возникает проблема с умножением. При делении числа остаются неизменными или уменьшаются. В обоих случаях isas не всегда обеспечивают достаточное количество бит для перекрытия переполнения, и требуется вызов библиотеки, чтобы обойти ограничения аппаратного обеспечения процессоров.

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

 
float a; 
float b; 
... 
b = a * 1.0; 

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

 
float a; 
float b; 
... 
b = a * 1.0F; 

Кроме того, большинство ISAS не имеет FPU поэтому следует избегать точек математики с плавающей даже больше, чем избежать фиксированных умножают точки и водоразделы. Предположим, что у большинства fpus есть ошибки. Трудно написать хороший код с плавающей запятой (программист часто выбрасывает справедливую сумму точности, просто не зная, как его использовать и писать для него код).

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

EDIT добавление точности с плавающей точкой пример:

 
float fun1 (float a) 
{ 
    return(a*7.1); 
} 

float fun2 (float a) 
{ 
    return(a*7.1F); 
} 
 

the first function contained: 
    mulsd .LC0(%rip), %xmm0 
using a 64 bit floating point constant 
.LC0 
    .long 1717986918 
    .long 1075603046 

and the second function contains the desired single precision multiply 
    mulss .LC1(%rip), %xmm0 
with a single precision constant 
.LC1 
    .long 1088631603 
 
char fun1 (char a) 
{ 
    return(a+7); 
} 
int fun2 (int a) 
{ 
    return(a+7); 
} 
 
fun1: 
    add r0, r0, #7 
    and r0, r0, #255 
    bx lr 
fun2: 
    add r0, r0, #7 
    bx lr 
+0

Не будет ли компилятор оптимизировать умножение и деление поплавка? Я не пишу в ASM здесь, это C++. Кроме того, есть ли у вас источники, чтобы поддержать это? Дело не в том, что я вам не доверяю, но ... – Jookia

+0

он не должен оптимизировать, точно так же, как ваша программа сказала, что я хочу, чтобы эта переменная была символом, а не компилятором. Точно так же константа, как 1.0, является двойной, и компилятор должен уважать то, что продвигает все уравнение до двойника. То же, что и при выполнении математической операции с int и char, символ преобразуется в int, тогда возникает математика. то результат преобразуется в соответствие с типом результата. –

+0

Я говорил о написании на C/C++. –

Смежные вопросы