Предложения по оптимизации при написании определения хранимого вектора для структуры union

Я написал экземпляр хранимого вектора для указанного ниже типа данных ( исходный вопрос здесь ):

data Atoms = I GHC.Int.Int32 | S GHC.Int.Int16

Код для определения этих экземпляров для хранимого вектора приведен ниже. Хотя я получаю очень хорошую производительность с приведенным ниже кодом, меня очень интересуют общие предложения по повышению производительности этого сохраняемого экземпляра. Под общим предложением я подразумеваю следующее:

  • Это не относится к версии компилятора GHC. Вы можете предположить, что GHC 6.12.3+ исключит ошибки производительности, если они присутствуют в более ранних версиях и имеют отношение к приведенному здесь коду.
  • Подходящие предложения для конкретных платформ. Вы можете предположить платформу Linux x86_64.
  • Общее предложение, больше в форме улучшения алгоритма (большой O), очень ценится, чем предложение, использующее оптимизацию, специфичную для оборудования. Но, учитывая базовую операцию вроде peek / poke здесь, не так много возможностей для улучшения алгоритмов, насколько я могу судить (и, следовательно, более ценно, потому что это дефицитный товар :)
  • Флаги компилятора для x86_64 приемлемы ( например, сообщая компилятору об удалении проверки безопасности с плавающей запятой и т. д.). Я использую опцию «-O2 --make» для компиляции кода.

Если есть какой-либо известный хороший исходный код библиотеки, который делает подобное (т. Е., определите сохраняемые экземпляры для объединяемых / рекурсивных типов данных), я буду очень заинтересован в их проверке.

import Data.Vector.Storable
import qualified Data.Vector.Storable as V
import Foreign
import Foreign.C.Types
import GHC.Int

data Atoms = I GHC.Int.Int32 | S GHC.Int.Int16
                deriving (Show)

instance Storable Atoms where
  sizeOf _ = 1 + sizeOf (undefined :: Int32)
  alignment _ = 1 + alignment (undefined :: Int32)

  {-# INLINE peek #-}
  peek p = do
            let p1 = (castPtr p::Ptr Word8) `plusPtr` 1 -- get pointer to start of the    element. First byte is type of element
            t <- peek (castPtr p::Ptr Word8)
            case t of
              0 -> do
                    x <- peekElemOff (castPtr p1 :: Ptr GHC.Int.Int32) 0
                    return (I x)
              1 -> do
                    x <- peekElemOff (castPtr p1 :: Ptr GHC.Int.Int16) 0
                    return (S x)

  {-# INLINE poke #-}
  poke p x = case x of
      I a -> do
              poke (castPtr p :: Ptr Word8) 0
              pokeElemOff (castPtr p1) 0 a
      S a -> do
              poke (castPtr p :: Ptr Word8) 1
              pokeElemOff (castPtr p1) 0 a
      where  p1 = (castPtr p :: Ptr Word8) `plusPtr` 1 -- get pointer to start of the     element. First byte is type of element

Обновление:

Основываясь на отзывах Даниэля и dflemstr, я переписал выравнивание, а также обновил конструктор, чтобы он имел тип Word32 вместо Word8. Но похоже, что для того, чтобы это было эффективно, конструктор данных тоже должен быть обновлен, чтобы иметь распакованные значения - это был мой недосмотр. Я должен был написать конструктор данных, чтобы в первую очередь иметь распакованные значения (см. Слайды производительности Джона Тиббелла - слайд № 49). Итак, переписывание конструктора данных в сочетании с выравниванием и изменениями конструктора оказало большое влияние на производительность, улучшив ее примерно на 33% для функций над вектором (простая функция суммирования в моем тесте производительности). Соответствующие изменения ниже (предупреждение - не переносится, но это не проблема для моего варианта использования):

Изменение конструктора данных:

data Atoms = I {-# UNPACK #-} !GHC.Int.Int32 | S {-# UNPACK #-} !GHC.Int.Int16

Сохраняемые изменения размера и выравнивания:

instance Storable Atoms where
  sizeOf _ = 2*sizeOf (undefined :: Int32)
  alignment _ = 4

  {-# INLINE peek #-}
  peek p = do
            let p1 = (castPtr p::Ptr Word32) `plusPtr` 1
            t <- peek (castPtr p::Ptr Word32)
            case t of
              0 -> do
                    x <- peekElemOff (castPtr p1 :: Ptr GHC.Int.Int32) 0
                    return (I x)
              _ -> do
                    x <- peekElemOff (castPtr p1 :: Ptr GHC.Int.Int16) 0
                    return (S x)

  {-# INLINE poke #-}
  poke p x = case x of
      I a -> do
              poke (castPtr p :: Ptr Word32) 0
              pokeElemOff (castPtr p1) 0 a
      S a -> do
              poke (castPtr p :: Ptr Word32) 1
              pokeElemOff (castPtr p1) 0 a
      where  p1 = (castPtr p :: Ptr Word32) `plusPtr` 1

5
задан Community 23 May 2017 в 11:48
поделиться