Я написал экземпляр хранимого вектора для указанного ниже типа данных ( исходный вопрос здесь ):
data Atoms = I GHC.Int.Int32 | S GHC.Int.Int16
Код для определения этих экземпляров для хранимого вектора приведен ниже. Хотя я получаю очень хорошую производительность с приведенным ниже кодом, меня очень интересуют общие предложения по повышению производительности этого сохраняемого экземпляра. Под общим предложением я подразумеваю следующее:
Если есть какой-либо известный хороший исходный код библиотеки, который делает подобное (т. Е., определите сохраняемые экземпляры для объединяемых / рекурсивных типов данных), я буду очень заинтересован в их проверке.
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