Есть ли разница в сроках исполнения между "unsigned int" и "int" на IPhone?
Или переформулировать вопрос: существует ли штраф за выполнение при использовании неподписанных значений?
И вообще: какой самый сильный тип (16 бит подписан?, 32 бит подписан? и т.д.) на процессоре IPhone ARM?
Ответы
Ответ 1
Это всегда зависит:
Для целых чисел со стрелками в качестве счетчиков и лимитов немного быстрее, потому что в 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 имеет относительный небольшой кеш данных и часто подключается к относительной медленной памяти. Если вы сможете более эффективно использовать кеш, выбрав меньшие типы данных, код может работать быстрее, даже если теоретический цикл-счет увеличивается.
Здесь вы должны поэкспериментировать.
Ответ 2
Стандарт 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;
}
Что касается использования подписанного или неподписанного, существуют и другие требования, чем производительность, например, действительно ли вам нужно использовать неподписанные типы или что вы хотите сделать в случае переполнения. Учитывая то, что я знаю о своем собственном коде, я готов поспорить, что в вашем коде есть другие замедления, кроме выбора примитивных целочисленных типов; -).
Ответ 3
ARM - это 32-битная архитектура, поэтому самые быстрые 32-разрядные целые. Однако 16-разрядные целые числа и 8-битные целые числа только немного медленнее. Подписанный против неподписанного не имеет большого значения, кроме особых обстоятельств (как отмечают другие ответы здесь). Таким образом, 64-битные целые числа будут эмулироваться двумя или более 32-битными операциями.
Когда речь идет о типах с плавающей точкой, в процессоре iPhone (ARM11 с аппаратной плавающей точкой VFP) 32-битные поплавки несколько быстрее, чем 64-разрядные удваиваются.
Ответ 4
Мне любопытно ответить Нилсу, чтобы эти вопросы были направлены на него. Это не ответ на исходный вопрос.
В этом цикле компилятор должен сделать убедитесь, что цикл даже завершается, если сначала больше, чем последний, потому что я будет перенесено из 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_MIN
- компилятор также может сказать, что это поведение undefined, весь код, который зависит от него, имеет поведение undefined, пусть определяет undefined как "это никогда не произойдет", пусть удаляет второй, если полностью. Конечно, кода меньше, поэтому код "оптимизирован".
Как я уже сказал, я уверен, что возможны легитимные оптимизации, как указывает Нилс, но опубликованный цикл не является среди них, насколько я могу судить.
Что касается исходного вопроса:
- используйте typedef
- test:)
Ответ 5
Так как беззнаковый и подписанный int имеют одинаковый размер и, в основном, одну и ту же производительность, беспокоиться о любой возможной оптимизации такого рода (если это возможно, а это не так) на этом этапе - это злая преждевременная оптимизация (поиск в Google узнать больше), даже на iPhone.
Прежде всего следует аргументы о правильности и экономии мысли, если это не ваша самая верхняя точка доступа, и вы измерили фактическую значительную разницу в производительности. В противном случае, это просто пустая трата времени, которую вы могли бы потратить на 2x ускорение другими способами.
EDIT: верно, что поведение подписанного переполнения undefined и что компиляторы могут (и в настоящее время) использовать это для оптимизации, как указал Хрвое Пржеша в его ответе и @martinkunev в своем комментарии.