以下のデータ型の格納可能なベクトル インスタンスを作成しました (元の質問はこちら):
data Atoms = I GHC.Int.Int32 | S GHC.Int.Int16
Storable vector のこれらのインスタンスを定義するためのコードは以下のとおりです。以下のコードで非常に優れたパフォーマンスを得ていますが、その格納可能なインスタンスのパフォーマンスを改善するための一般的な提案に非常に興味があります。一般的な提案とは、次のことを意味します。
- GHC コンパイラのバージョンに固有のものではありません。GHC 6.12.3+ は、以前のバージョンにパフォーマンスのバグが存在し、ここにあるコードに関連する場合、パフォーマンスのバグを除外すると想定できます。
- プラットフォーム固有の提案は問題ありません。x86_64 Linux プラットフォームを想定している可能性があります。
- ハードウェア固有の最適化を利用する提案よりも、アルゴリズムの改善 (大きな O) という形の一般的な提案の方が非常に高く評価されます。しかし、ここでのピーク/ポークのような基本的な操作を考えると、私が知る限り、アルゴリズムの改善の余地はあまりありません (したがって、希少な商品であるため、より価値があります:)
- x86_64 のコンパイラ フラグは許容されます (たとえば、浮動小数点セーフ チェックの削除についてコンパイラに通知するなど)。「-O2 --make」オプションを使用してコードをコンパイルしています。
同様のことを行う (つまり、union/recursive データ型の格納可能なインスタンスを定義する) 既知の優れたライブラリ ソース コードがあれば、それらをチェックすることに非常に興味があります。
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
アップデート:
Daniel と dflemstr からのフィードバックに基づいて、アライメントを書き直し、コンストラクターを Word8 ではなく Word32 型に更新しました。しかし、これを有効にするには、データ コンストラクターもアンパックされた値を持つように更新する必要があるようです。これは私の見落としでした。そもそもアンパックされた値を持つようにデータ コンストラクターを作成する必要がありました ( John Tibbell によるパフォーマンス スライド- スライド #49 を参照)。そのため、データ コンストラクターを書き直し、アライメントとコンストラクターの変更を加えることで、パフォーマンスに大きな影響を与え、ベクターに対する関数 (私のベンチマーク テストでは単純な合計関数) で約 33% 向上しました。以下の関連する変更 (警告 - 移植性はありませんが、私のユースケースでは問題ではありません):
データ コンストラクターの変更:
data Atoms = I {-# UNPACK #-} !GHC.Int.Int32 | S {-# UNPACK #-} !GHC.Int.Int16
保存可能な sizeof と配置の変更:
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