2011-02-05 11 views
10

性能上の理由から、ByteString(現在は厳密)のゼロコピーキャストをVectorにしたいと思います。 Vectorは、ボンネットの下にちょうどByteArray#あり、そしてByteStringは、このようなものに見えるかもしれませんForeignPtrですので:確かに右ではありませんForeignPtrをByteArrayにキャストしようとしていますか#(関数:: ByteString - > Vectorの場合)

caseBStoVector :: ByteString -> Vector a 
caseBStoVector (BS fptr off len) = 
    withForeignPtr fptr $ \ptr -> do 
     let ptr' = plusPtr ptr off 
      p = alignPtr ptr' (alignment (undefined :: a)) 
      barr = ptrToByteArray# p len -- I want this function, or something similar 
      barr' = ByteArray barr 
      alignI = minusPtr p ptr 
      size = (len-alignI) `div` sizeOf (undefined :: a) 
     return (Vector 0 size barr') 

を。機能がない場合でも、ptrToByteArray#ptrwithForeignPtrの範囲外にエスケープする必要があるようです。だから、私のquesetionsは以下のとおりです。誰もがByteArray#について少し話すことができる場合

  1. この投稿は、おそらく私は感謝するだろうなど、それは(GCed)管理されているか、それは表現だ、ByteArray#の私の原始的な理解をアドバタイズします。

  2. GCedヒープにByteArray#があり、外部にはForeignPtrが存在するという事実は基本的な問題であると思われます。すべてのアクセス操作が異なります。おそらく私はVector= ByteArray !Int !Intから別の間接指定を使って何かに再定義するべきでしょうか? Somebodyは= Location !Int !Intのようにdata Location = LocBA ByteArray | LocFPtr ForeignPtrであり、両方のタイプのラッピング演算を提供しますか?このインダイレクションはパフォーマンスをあまりにも低下させる可能性があります。

  3. これら2つを結びつけていないと、ForeignPtrの任意の要素タイプにもっと効率的にアクセスできます。任意のStorableまたはPrimitiveタイプの配列としてForeignPtr(またはByteString)を扱うライブラリを知っている人はいますか?これは、私にはまだベクトルパッケージからのストリームの融合と調整を失うでしょう。

答えて

8

免責事項:ここに記載されているすべての実装の詳細は、GHC固有のものであり、掲載時点の問題の図書館の内部表現です。

この応答は事実の数年後ですが、実際にはバイラルコンテンツのポインタを取得することは可能です。 GCがヒープ内のデータを移動するのが好きで、GCヒープ以外のものがリークする可能性があるので問題になります。これは必ずしも理想的ではありません。 GHCは、でこれを解決:

newPinnedByteArray# :: Int# -> State# s -> (#State# s, MutableByteArray# s#)

プリミティブByteArrayとして定義(内部typedefさCチャーアレイ)は静的アドレスに固定することができます。 GCはそれらを移動させないことを保証します。あなたはこの機能をポインタにByteArrayの参照を変換することができます:

byteArrayContents# :: ByteArray# -> Addr#

アドレスタイプは、PtrをとForeignPtr種類の基礎を形成します。 Ptrsはファントムタイプでマークされたアドレスであり、ForeignPtrsはGHCメモリとIORefファイナライザへのオプションの参照です。

免責事項:これはのみです。ByteStringがHaskellの場合はのみが動作します。それ以外の場合は、bytearrayへの参照を取得することはできません。あなたは任意のアドレスを逆参照することはできません。 bytearrayにあなたの方法をキャストまたは強制しようとしないでください。その方法はsegfaultsです。例:

{-# LANGUAGE MagicHash, UnboxedTuples #-} 

import GHC.IO 
import GHC.Prim 
import GHC.Types 

main :: IO() 
main = test 

test :: IO()  -- Create the test array. 
test = IO $ \s0 -> case newPinnedByteArray# 8# s0 of {(# s1, mbarr# #) -> 
        -- Write something and read it back as baseline. 
        case writeInt64Array# mbarr# 0# 1# s1 of {s2 -> 
        case readInt64Array# mbarr# 0# s2 of {(# s3, x# #) -> 
        -- Print it. Should match what was written. 
        case unIO (print (I# x#)) s3 of {(# s4, _ #) -> 
        -- Convert bytearray to pointer. 
        case byteArrayContents# (unsafeCoerce# mbarr#) of {addr# -> 
        -- Dereference the pointer. 
        case readInt64OffAddr# addr# 0# s4 of {(# s5, x'# #) -> 
        -- Print what's read. Should match the above. 
        case unIO (print (I# x'#)) s5 of {(# s6, _ #) -> 
        -- Coerce the pointer into an array and try to read. 
        case readInt64Array# (unsafeCoerce# addr#) 0# s6 of {(# s7, y# #) -> 
        -- Haskell is not C. Arrays are not pointers. 
        -- This won't match. It might segfault. At best, it's garbage. 
        case unIO (print (I# y#)) s7 of (# s8, _ #) -> (# s8,() #)}}}}}}}} 


Output: 
    1 
    1 
(some garbage value) 

延ByteStringからのByteArrayを取得するには、Data.ByteString.Internalとパターンマッチからコンストラクタをインポートする必要があります。

data ByteString = PS !(ForeignPtr Word8) !Int !Int 
(\(PS foreignPointer offset length) -> foreignPointer) 

ここで、ForeignPtrから品物を取り出す必要があります。この部分は完全に実装固有のものです。 GHCの場合は、GHC.ForeignPtrからインポートします。

data ForeignPtr a = ForeignPtr Addr# ForeignPtrContents 
(\(ForeignPtr addr# foreignPointerContents) -> foreignPointerContents) 

data ForeignPtrContents = PlainForeignPtr !(IORef (Finalizers, [IO()])) 
         | MallocPtr  (MutableByteArray# RealWorld) !(IORef (Finalizers, [IO()])) 
         | PlainPtr  (MutableByteArray# RealWorld) 

GHCでは、ByteStringは固定バイト配列にラップされたPlainPtrsで構築されます。彼らはファイナライザを持っていません。彼らは範囲外になると、通常のHaskellデータのようにGCされます。しかし、Addrsは数えません。 GHCはGCヒープの外にあるものを指していると仮定します。 bytearray自体がスコープから外れた場合は、ぶら下がったポインタが残っています。

data PlainPtr = (MutableByteArray# RealWorld) 
(\(PlainPtr mutableByteArray#) -> mutableByteArray#) 

MutableByteArraysは、ByteArraysと同じです。真のゼロコピー構築が必要な場合は、unsafeCoerce#またはunsafeFreeze#のいずれかをbytearrayに設定してください。それ以外の場合、GHCは複製を作成します。

mbarrTobarr :: MutableByteArray# s -> ByteArray# 
mbarrTobarr = unsafeCoerce# 

これで、ByteStringの生の内容がベクターに変換されるようになりました。

最高の願い、

2

:: ForeignPtr -> Maybe ByteArray#をハックする可能性がありますが、一般的に行うことはできません。

Data.Vector.Storableモジュールを参照してください。それには、関数unsafeFromForeignPtr :: ForeignPtr a -> Int -> Int -> Vector aが含まれています。それはあなたが望むように聞こえる。

Data.Vector.Storable.Mutableの亜種もあります。

+0

うわー、私はその機能を見逃しています。それは良いスタートです。私の通常の 'unsafePerformIO ... withForeignPtr ... peekElemOff idx'の組み合わせよりも、それ以上のパフォーマンスを得ることはできませんが、それは確かにきれいです。 –

+0

また、興味深いのはvector-mmapパッケージです –

関連する問題