Ответ 1
Сколько бит будет занимать 52-битное значение, если оно хранится как
Integer
?
Это зависит от реализации. С помощью GHC значения, которые находятся внутри машинного слова, хранятся непосредственно в конструкторе Integer
, поэтому, если вы находитесь на 64-битной машине, он должен занимать такое же пространство, что и Int. Это соответствует конструктору S#
Integer
:
data Integer = S# Int#
| J# Int# ByteArray#
Более крупные значения (т.е. представленные в J#
) сохраняются с GMP.
Правильно ли, что
Int64
иWord64
позволяют хранить 64-битные данные и весить ровно 64 бита для любого значения?
Не совсем - они в коробке. Int64
на самом деле является указателем на неоцененный thunk или указатель на один указатель на информационную таблицу плюс 64-битное целочисленное значение. (Дополнительную информацию см. В комментарии GHC).
Если вы действительно хотите что-то, что может быть 64 бита, никаких исключений, то вы можете использовать unboxed типа типа Int64#
, но я бы настоятельно рекомендовал сначала профайлинг; unboxed значения весьма болезненны для использования. Например, вы не можете использовать unboxed types в качестве аргументов для типа конструкторов, поэтому вы не можете иметь список Int64#
s. Вы также должны использовать операции, специфичные для целых чисел unboxed. И, конечно же, все это чрезвычайно специфично для GHC.
Если вы хотите хранить много 52-битных целых чисел, вы можете использовать vector или repa (построенный на векторе, с такими необычными вещами, как автоматический parallelism); они сохраняют значения unboxed под капотом, но позволяют работать с ними в коробке. (Конечно, каждое индивидуальное значение, которое вы вынимаете, будет в коробке.)
Являются ли какие-либо из этих типов более эффективными или предпочтительными по любым другим причинам, кроме размера, например. реализация собственных кодов или оптимизация связанных с процессором инструкций?
Да; использование Integer
берет ветвь для каждой операции, так как она должна различать случаи машинного слова и бинума; и, конечно же, он должен обрабатывать переполнение. Целевые типы фиксированного размера избегают этих накладных расходов.
И на всякий случай: какой из них вы порекомендуете для хранения 52-битного значения в приложении, чрезвычайно чувствительного с точки зрения производительности?
Если вы используете 64-разрядную машину: Int64
или, если нужно, Int64#
.
Если вы используете 32-разрядную машину: вероятно, Integer
, так как в 32-разрядном Int64
эмулируется с вызовами FFI для функций GHC, которые, вероятно, не очень оптимизированы, но я бы попробовал как сравните его. С помощью Integer
вы получите максимальную производительность для небольших целых чисел, а GMP сильно оптимизирован, поэтому, вероятно, это будет лучше для более крупных, чем вы думаете.
Вы можете выбрать между Int64
и Integer
во время компиляции с использованием препроцессора C (включен с {-# LANGUAGE CPP #-}
); Я думаю, что было бы легко заставить Cabal управлять #define
на основе ширины слова целевой архитектуры. Остерегайтесь, конечно, что они не то же самое; вам нужно быть осторожным, чтобы избежать "переполнения" в коде Integer
и, например, Int64
- это экземпляр Bounded
, но Integer
нет. Возможно, проще всего настроить только одну ширину слова (и, следовательно, тип) для производительности и жить с более низкой производительностью с другой.
Я бы предложил создать собственный тип Int52
в качестве обертки newtype
поверх Int64
или обертки Word52
поверх Word64
- просто выберите то, что лучше соответствует вашим данным, не должно быть никакого влияния на производительность; если бы это были только произвольные биты, я бы пошел с Int64
, просто потому, что Int
более распространен, чем Word
.
Вы можете определить все экземпляры для автоматической обработки упаковки (попробуйте :info Int64
в GHCi, чтобы узнать, какие экземпляры вы хотите определить) и предоставьте "небезопасные" операции, которые применяются непосредственно под newtype
для производительности критические ситуации, когда вы знаете, что переполнения не будет.
Затем, если вы не экспортируете конструктор newtype
, вы всегда можете заменить реализацию Int52
позже, не изменяя какой-либо из остальной части вашего кода. Не беспокойтесь о накладных расходах отдельного типа - представление времени выполнения newtype
полностью совпадает с базовым типом; они существуют только во время компиляции.