Есть ли причина использовать С++ 11 std:: int_fast32_t или std:: int_fast16_t по int в межплатформенном коде?

В С++ 11 мы снабжаемся целыми типами фиксированной ширины, такими как std::int32_t и std::int64_t, которые являются необязательными и поэтому не являются оптимальными для написания кросс-платформенного кода. Тем не менее, мы также получили необязательные варианты для типов: например. "быстрые" варианты, например. std::int_fast32_t и std::int_fast64_t, а также варианты "наименьшего размера", например. std::int_least32_t, которые имеют как минимум заданное количество бит в размере.

Код, над которым я работаю, является частью кросс-платформенной библиотеки на С++ 11, которая поддерживает компиляцию на самых популярных компиляторах Unix/Windows/Mac. Возникает вопрос, есть ли преимущество в замене существующих целых типов в коде целыми типами фиксированной ширины С++ 11.

Недостатком использования таких переменных, как std::int16_t и std::int32_t, является отсутствие гарантии того, что они доступны, поскольку они предоставляются только в том случае, если реализация напрямую поддерживает тип (согласно http://en.cppreference.com/w/cpp/types/integer).

Однако, поскольку int не менее 16 бит, а 16 бит - достаточно большие для целых чисел, используемых в коде, как насчет использования std::int_fast16_t по int? Предоставляет ли это возможность заменить все типы int на std::int_fast16_t и все unsigned int на std::uint_fast16_t таким образом или это не нужно?

Алогично, зная, что все поддерживаемые платформы и компиляторы имеют int размером не менее 32 бит, имеет смысл заменить их на std::int_fast32_t и std::uint_fast32_t соответственно?

Ответ 1

int может быть 16, 32 или даже 64 бит на текущих компьютерах и компиляторах. В будущем он может быть больше (скажем, 128 бит).

Если ваш код в порядке с этим, пойдите с ним.

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

int_fast32_t - это когда вам нужно как минимум 32 бита, но вы очень заботитесь о производительности. На аппаратном обеспечении, в котором 32-битное целое загружается как целое число 64 бит, после этого битбайт возвращается к 32-битовому целому в громоздком процессе, int_fast_32_t может быть 64-битным целым числом. Стоимость этого заключается в том, что на неясных платформах ваш код ведет себя по-разному.

Если вы не тестируете на таких платформах, я бы посоветовал это сделать.

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

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

Короче:

Используйте int_fast##_t тогда и только тогда, когда вы протестировали свой код (и будете продолжать его тестировать) на платформах, где размер int меняется, и вы показали, что повышение производительности стоит того, что будет в будущем.

Использование int##_t с общими размерами ## означает, что ваш код не сможет скомпилироваться на платформах, на которых вы его не тестировали. Это хорошо; непроверенный код не является надежным, а ненадежный код обычно хуже, чем бесполезно.

Без использования int32_t и используя int, ваш код будет иногда иметь int, которые равны 32, а иногда ints, которые равны 64 (и в теории больше), а иногда int, которые равны 16. Если вы готовы тестировать и поддерживать каждый такой случай в каждом таком int, идти за ним.

Обратите внимание, что массивы int_fast##_t могут иметь проблемы с кешем: они могут быть необоснованно большими. Например, int_fast16_t может быть 64 бит. Массив из нескольких тысяч или миллионов из них может быть индивидуально быстро работать, но промахи кэша, вызванные их массой, могут сделать их более медленными в целом; и риск того, что вещи поменяются на более медленное хранение, растет.

int_least##_t может быть быстрее в этих случаях.

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

Однако при использовании целочисленных типов фиксированной ширины вы должны обратить особое внимание на то, что int, long и т.д. все еще имеют ту же ширину, что и раньше. Целочисленное продвижение по-прежнему происходит на основе размера int, который зависит от используемого вами компилятора. Интегральное число в вашем коде будет иметь тип int с соответствующей шириной. Это может привести к нежелательному поведению, если вы скомпилируете свой код с помощью другого компилятора. Для получения более подробной информации: fooobar.com/questions/22309/...

Ответ 2

Я только что понял, что OP просто спрашивает о int_fast##_t not int##_t, поскольку позже это необязательно. Тем не менее, я буду поддерживать ответ, который может помочь кому-то.


Я бы добавил что-то. Целые числа фиксированного размера настолько важны (или даже необходимы) для создания API для других языков. Например, когда вы хотите использовать функции pInvoke и передавать данные в их родную С++ DLL из управляемого кода .NET, например. В .NET поддерживается int фиксированный размер (я думаю, что он 32 бит). Итак, если вы использовали int в С++ и считались 64-битным, а не 32-битным, это может вызвать проблемы и сократить последовательность завернутых структур.