`Integer` против` Int64` против `Word64` - PullRequest
24 голосов
/ 16 января 2012

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

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

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

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

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

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

1 Ответ

22 голосов
/ 16 января 2012

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

Это зависит от реализации. В GHC значения, которые помещаются внутри машинного слова, хранятся непосредственно в конструкторе Integer, поэтому, если вы работаете на 64-битной машине, он должен занимать столько же места, что и Int. Это соответствует конструктору S# из Integer:

data Integer = S# Int#
             | J# Int# ByteArray#

Большие значения (т. Е. Те, которые обозначены J#) сохраняются с GMP .

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

Не совсем - они в штучной упаковке . Int64 - это фактически указатель на неоцененный раздел или указатель из одного слова на информационную таблицу плюс 64-битное целочисленное значение. (См. Комментарий GHC для получения дополнительной информации.)

Если вы действительно хотите что-то, что гарантированно будет 64-битным, без исключений, тогда вы можете использовать распакованный тип, например Int64#, но я настоятельно рекомендую сначала профилировать; распакованные значения довольно болезненно использовать. Например, вы не можете использовать распакованные типы в качестве аргументов для конструкторов типов, поэтому у вас не может быть списка Int64# s. Вы также должны использовать операции, специфичные для целых чисел без коробки. И, конечно, все это исключительно для GHC.

Если вы хотите хранить много 52-битных целых чисел, вы можете использовать vector или repa (построенный на векторе, с такими причудливыми вещами, как автоматический параллелизм) ; они хранят распакованные значения под капотом, но позволяют работать с ними в штучной упаковке. (Конечно, каждое отдельное значение, которое вы вынимаете, будет в штучной упаковке.)

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

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

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

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