Оптимизация компилятора допускается с помощью типов int, наименьшего и быстрого нефиксированной ширины C / C ++ - PullRequest
0 голосов
/ 22 февраля 2019

Ясно, что интегральные типы фиксированной ширины должны использоваться, когда размер важен.

Однако я прочитал (Руководство по стилю игры Insomniac Games), что "int" должно быть предпочтительным для счетчиков цикла / функции args /коды возврата / т. д., когда размер не важен - было дано обоснование того, что типы фиксированной ширины могут препятствовать определенной оптимизации компилятора.

Теперь я хотел бы провести различие между «оптимизацией компилятора» и «более подходящий typedef для целевой архитектуры ".Последний имеет глобальный охват, и мое предположение, вероятно, имеет очень ограниченное влияние, если компилятор не может каким-то образом рассуждать о глобальной производительности программы, параметризованной этим typedef.Первый имеет локальную область видимости, где компилятор может свободно оптимизировать количество используемых байтов и операции, основанные, среди прочего, на нагрузке / использовании локального регистра.

Разрешает ли стандарт "оптимизация компилятора" (как мы определили) для типов без фиксированной ширины?Есть ли хорошие примеры этого?

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

Ответы [ 5 ]

0 голосов
/ 23 февраля 2019

Я думаю, что вы пытаетесь задать вопрос:

Разрешено ли компилятору выполнять дополнительную оптимизацию для типа с нефиксированной шириной, например int сверх того, что было бы разрешенодля типа фиксированной ширины, такого как int32_t , который на текущей платформе имеет одинаковую длину ?

То есть вас не интересует та часть, где размерразрешено выбирать нефиксированный тип ширины соответствующим образом для аппаратного обеспечения - вы знаете об этом и спрашиваете, если сверх этого доступны дополнительные оптимизации?

Ответ, насколькоЯ в курсе или видел, это нет .Нет и в том смысле, что компиляторы на самом деле не оптимизируют int иначе, чем int32_t (на платформах, где int 32-битный), а также в том смысле, что не существует оптимизаций, разрешенных стандартом для int которые также не допускаются для int32_t 1 (эта вторая часть неверна - см. комментарии).

Самый простой способ убедиться в том, что различныецелые числа фиксированной ширины - это все определения типов для различных базовых целочисленных примитивных типов - поэтому на платформе с 32-разрядными целыми числами int32_t, вероятно, будет typedef (возможно, косвенно) int.Таким образом, с точки зрения поведения и оптимизации типы идентичны, и, как только вы попадаете в мир IR компилятора, исходный тип, вероятно, даже не будет доступен без перехода через опцию (т. Е. int иint32_t будет генерировать тот же IR).

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


1 Конечно,ответ на вопрос "Разрешено ли компилятору для оптимизации int лучше, чем int32_t - да , поскольку нет особых требований к оптимизации, чтобы компилятор мог это сделатьчто-то странное или наоборот, например, оптимизация int32_t лучше, чем int. Хотя это не очень интересно.

0 голосов
/ 22 февраля 2019

Тем не менее, я прочитал (Руководство по стилю Insomniac Games), что для счетчиков циклов предпочтительнее "int"

Вы должны использовать size_t всякий раз, когда выполняете итерации по массиву,У int есть и другие проблемы, кроме производительности, такие как подпись, а также проблемы при портировании.

Со стандартной точки зрения, для сценария, где «n» является размером int,не существует случая, когда int_fastn_t должен работать хуже, чем int, или компилятор / стандартная lib / ABI / system имеет ошибку.

Разрешает ли стандарт "оптимизация компилятора" (как мыопределено) для типов не фиксированной ширины?Есть ли хорошие примеры этого?

Конечно, компилятор может довольно дико оптимизировать использование целочисленных типов, если это не влияет на результат результата.Независимо от того, являются ли они int или int32_t.

Например, 8-битный компилятор ЦП может оптимизировать int a=1; int b=1; ... c = a + b; для выполнения на 8-битной арифметике, игнорируя целочисленные преобразования и фактический размер int.Однако, скорее всего, ему потребуется выделить 16 бит памяти для хранения результата.

Но если мы дадим ему какой-то гнилой код, такой как char a = 0x80; int b = a >> 1;, ему придется выполнить оптимизацию, чтобы побочные эффекты от целых чиселпродвижение принимаются во внимание.То есть результат мог бы быть 0xFFC0, а не 0x40, как можно было ожидать (предполагая подписанный символ, дополнение 2, арифметический сдвиг).Из-за этого часть a >> 1 не может быть оптимизирована до 8-битного типа - она ​​должна выполняться с 16-битной арифметикой.

0 голосов
/ 22 февраля 2019

Есть много вещей не так с int_fast типами - особенно, что они могут быть медленнее, чем int!

#include <stdio.h>
#include <inttypes.h>
int main(void) {
    printf("%zu\n", sizeof (int_fast32_t));
}

Запустите это на x86-64, и он напечатает 8 ... но этоне имеет смысла - использование 64-битных регистров часто требует префиксов в битовом режиме x86-64, а «поведение при переполнении не определено» означает, что при использовании 32-битного int не имеет значения, являются ли старшие 32 бита из 64регистр битов устанавливается после арифметики - поведение «все еще корректно».


Что еще хуже, чем использование подписанных быстрых или наименьших типов, использование небольшого целого числа без знака вместо size_t или целое число со знаком для счетчика цикла - теперь компилятор должен сгенерировать дополнительный код для «обеспечения правильного поведения циклического переноса».

0 голосов
/ 22 февраля 2019

Я не очень хорошо знаком с набором команд x86, но если вы не можете гарантировать, что практически каждая арифметическая команда и команда перемещения также допускают дополнительное смещение и (знак) расширяются, тогда предполагается, что меньшие типы «как минимум так же быстры», как и большиеиз них это не так.

Сложность x86 затрудняет создание простых примеров, поэтому давайте рассмотрим вместо этого микроконтроллер ARM.

Позволяет определить две функции сложения, которые различаются только типом возвращаемого значения.,«add32» возвращает целое число полной ширины регистра, а «add8» возвращает только один байт.

int32_t add32(int32_t a, int32_t b) { return a + b; }
int8_t add8(int32_t a, int32_t b) { return a + b; }

Компиляция этих функций с -Os дает следующую сборку:

add32(int, int):
        add     r0, r0, r1
        bx      lr
add8(int, int):
        add     r0, r0, r1
        sxtb    r0, r0 // Sign-extend single byte
        bx      lr

Обратите внимание, что функция, которая возвращает только байт, на одну инструкцию длиннее.Он должен усекать 32-битное дополнение до одного байта.

Вот ссылка на проводник code @ compiler: https://godbolt.org/z/ABFQKe

0 голосов
/ 22 февраля 2019

Эмпирическое правило заключается в использовании int в том, что стандарт определяет этот интегральный тип как естественный тип данных ЦП (при условии, что он достаточно широк для диапазона от INT_MIN до INT_MAXВот откуда берутся лучшие показатели.

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