Или переформулировать вопрос: существует ли штраф за выполнение при использовании неподписанных значений?
И вообще: какой самый сильный тип (16 бит подписан?, 32 бит подписан? и т.д.) на процессоре IPhone ARM?
Или переформулировать вопрос: существует ли штраф за выполнение при использовании неподписанных значений?
И вообще: какой самый сильный тип (16 бит подписан?, 32 бит подписан? и т.д.) на процессоре IPhone ARM?
Это всегда зависит:
Для целых чисел со стрелками в качестве счетчиков и лимитов немного быстрее, потому что в C компилятор может предположить, что переполнение никогда не происходит.
Рассмотрим это: у вас есть цикл с неподписанным счетчиком цикла следующим образом:
void function (unsigned int first, unsigned int last)
{
unsigned int i;
for (i=first; i!=last; i++)
{
// do something here...
}
}
В этом цикле компилятор должен убедиться, что цикл даже завершится, если он больше, чем последний, потому что я перенесу из UINT_MAX в 0 при переполнении (просто для обозначения одного примера - есть и другие случаи). Это устраняет возможность оптимизации циклов. С подписанными счетчиками циклов компилятор предполагает, что обход не происходит и может генерировать лучший код.
Для целочисленного деления otoh беззнаковые целые числа являются более быстрыми на ARM. ARM не имеет единицы аппаратного разделения, поэтому разделение выполняется в программном обеспечении и всегда выполняется с неподписанными значениями. Вы сохраните несколько циклов для дополнительного кода, необходимого для превращения подписанного деления в беззнаковое подразделение.
Для всех других вещей, таких как арифметика, логика, загрузка и запись в память, выбор знака не сделает разницу любой.
Что касается размера данных: как отметил Руна, они имеют более или менее равную скорость с 32-разрядными типами, которые являются самыми быстрыми. Байты и слова иногда необходимо отрегулировать после обработки, поскольку они находятся в 32-битном регистре, а верхние (неиспользуемые) биты должны быть отмечены знаком или нулем.
Однако процессор ARM имеет относительный небольшой кеш данных и часто подключается к относительной медленной памяти. Если вы сможете более эффективно использовать кеш, выбрав меньшие типы данных, код может работать быстрее, даже если теоретический цикл-счет увеличивается.
Здесь вы должны поэкспериментировать.
Стандарт C99 позволяет ответить на ваш общий вопрос; самый быстрый тип в целевой системе, который превышает определенную требуемую ширину, определяется в stdint.h
. Представьте, что мне нужно как минимум 8-битное целое число:
#include <stdio.h>
#include <stdint.h>
int main (int argc, char **argv)
{
uint_fast8_t i;
printf("Width of uint_fast8_t is %d\n", sizeof(i));
return 0;
}
Что касается использования подписанного или неподписанного, существуют и другие требования, чем производительность, например, действительно ли вам нужно использовать неподписанные типы или что вы хотите сделать в случае переполнения. Учитывая то, что я знаю о своем собственном коде, я готов поспорить, что в вашем коде есть другие замедления, кроме выбора примитивных целочисленных типов; -).
ARM - это 32-битная архитектура, поэтому самые быстрые 32-разрядные целые. Однако 16-разрядные целые числа и 8-битные целые числа только немного медленнее. Подписанный против неподписанного не имеет большого значения, кроме особых обстоятельств (как отмечают другие ответы здесь). Таким образом, 64-битные целые числа будут эмулироваться двумя или более 32-битными операциями.
Когда речь идет о типах с плавающей точкой, в процессоре iPhone (ARM11 с аппаратной плавающей точкой VFP) 32-битные поплавки несколько быстрее, чем 64-разрядные удваиваются.
Мне любопытно ответить Нилсу, чтобы эти вопросы были направлены на него. Это не ответ на исходный вопрос.
В этом цикле компилятор должен сделать убедитесь, что цикл даже завершается, если сначала больше, чем последний, потому что я будет перенесено из UINT_MAX в 0 на Переполнение
for (i=first; i!=last; i++)
{
// do something here...
}
Я не думаю, что это так. Компилятору нужно только проверить, что i!=last
в начале каждой итерации цикла:
i=first;
if (i == last) goto END;
START:
// do sth
++i;
if (i != last) goto START;
END:
Знаки переменных не изменят код, поэтому, на мой взгляд, пример неверен. Я даже скомпилировал код с помощью msvc08/release и сравнил результаты ассемблера - в основном то же (кроме типов переходов) во всех подписанных /unsiged и!=/< комбинации.
Теперь я согласен с тем, что в некоторых случаях компилятор может оптимизировать код, но я не могу придумать никаких хороших примеров - если кто-нибудь может, ответьте.
Я могу думать только о "плохом" примере:
signed i, j, k;
if (i > k)
{
i += j;
if (i > k)
{
}
}
i+= j
может переполняться, но подписанное переполнение undefined в C, поэтому все идет. Возможны две вещи:
Как я уже сказал, я уверен, что возможны легитимные оптимизации, как указывает Нилс, но опубликованный цикл не является среди них, насколько я могу судить.
Что касается исходного вопроса:
Так как беззнаковый и подписанный int имеют одинаковый размер и, в основном, одну и ту же производительность, беспокоиться о любой возможной оптимизации такого рода (если это возможно, а это не так) на этом этапе - это злая преждевременная оптимизация (поиск в Google узнать больше), даже на iPhone. Прежде всего следует аргументы о правильности и экономии мысли, если это не ваша самая верхняя точка доступа, и вы измерили фактическую значительную разницу в производительности. В противном случае, это просто пустая трата времени, которую вы могли бы потратить на 2x ускорение другими способами.