Подтвердить что ты не робот

`Integer` против` Int64` против `Word64`

У меня есть некоторые данные, которые могут быть представлены беззнаковым типом Integral, а его самое большое значение - 52 бита. AFAIK только Integer, Int64 и Word64 удовлетворяют этим требованиям.

Вся информация, которую я мог узнать об этих типах, заключалась в том, что Integer подписан и имеет плавающий неограниченный размер бита, Int64 и Word64 являются фиксированными и подписанными и неподписанными соответственно. То, что я не узнал, это информация о фактической реализации этих типов:

  • Сколько битов будет занимать 52-разрядное значение, если оно хранится как Integer?

  • Я уверен, что Int64 и Word64 позволяют хранить 64-битные данные и весить ровно 64 бита для любого значения?

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

  • И на всякий случай: какой из них вы порекомендуете для хранения 52-битного значения в приложении, чрезвычайно чувствительного с точки зрения производительности?

4b9b3361

Ответ 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 полностью совпадает с базовым типом; они существуют только во время компиляции.