6

PNG ファイルをロードし、圧縮されていない RGBA バイトを取得してから、それらを gzip または zlib パッケージに送信しようとしています。

pngload パッケージは画像データを (StorableArray (Int, Int) Word8) として返し、圧縮パッケージは遅延 ByteString を取ります。したがって、(StorableArray (Int, Int) Word8 -> ByteString) 関数を構築しようとしています。

これまでのところ、次のことを試しました。

import qualified Codec.Image.PNG as PNG
import Control.Monad (mapM)
import Data.Array.Storable (withStorableArray)
import qualified Data.ByteString.Lazy as LB (ByteString, pack, take)
import Data.Word (Word8)
import Foreign (Ptr, peekByteOff)

main = do
    -- Load PNG into "image"...
    bytes <- withStorableArray 
        (PNG.imageData image)
        (bytesFromPointer lengthOfImageData)

bytesFromPointer :: Int -> Ptr Word8 -> IO LB.ByteString
bytesFromPointer count pointer = LB.pack $ 
    mapM (peekByteOff pointer) [0..(count-1)]

これにより、スタックのメモリが不足するため、明らかに間違ったことをしています。Ptr と ForeignPtr で試すことができることは他にもありますが、そこには多くの「安全でない」機能があります。

ここで何か助けていただければ幸いです。私はかなり困惑しています。

4

2 に答える 2

7

一般的に、パックとアンパックはパフォーマンスにとって悪い考えです。Ptrがあり、長さがバイト単位の場合、2つの異なる方法で厳密なバイト文字列を生成できます。

このような:

import qualified Codec.Image.PNG as PNG
import Control.Monad
import Data.Array.Storable (withStorableArray)

import Codec.Compression.GZip

import qualified Data.ByteString.Lazy   as L
import qualified Data.ByteString.Unsafe as S

import Data.Word
import Foreign

-- Pack a Ptr Word8 as a strict bytestring, then box it to a lazy one, very
-- efficiently
bytesFromPointer :: Int -> Ptr Word8 -> IO L.ByteString
bytesFromPointer n ptr = do
    s <- S.unsafePackCStringLen (castPtr ptr, n)
    return $! L.fromChunks [s]

-- Dummies, since they were not provided 
image = undefined
lengthOfImageData = 10^3

-- Load a PNG, and compress it, writing it back to disk
main = do
    bytes <- withStorableArray
        (PNG.imageData image)
        (bytesFromPointer lengthOfImageData)
    L.writeFile "foo" . compress $ bytes

StorableArrayからPtrを再パッケージ化するO(1)バージョンを使用しています。「packCStringLen」を使用して、最初にコピーすることをお勧めします。

于 2010-06-27T17:03:12.577 に答える
3

「bytesFromPointer」の問題は、パックされた表現である StorableArray を pngload から取得し、それを別のパックされた表現である ByteString に変換し、中間リストを通過することです。遅延は、中間リストがメモリ内に構築されないことを意味する場合がありますが、ここではそうではありません。

関数「mapM」が最初の違反者です。展開mapM (peekByteOff pointer) [0..(count-1)]すると得られる

el0 <- peekByteOff pointer 0
el1 <- peekByteOff pointer 1
el2 <- peekByteOff pointer 2
...
eln <- peekByteOff pointer (count-1)
return [el0,el1,el2,...eln]

これらのアクションはすべて IO モナド内で発生するため、順番に実行されます。これは、リストが構築される前に出力リストのすべての要素を構築する必要があり、怠惰があなたを助けるチャンスがないことを意味します。

Don Stewart が指摘しているように、リストが遅延して作成されたとしても、「パック」機能は依然としてパフォーマンスを台無しにします。「pack」の問題は、正しい量のメモリを割り当てるために、リスト内にいくつの要素があるかを知る必要があることです。リストの長さを見つけるには、プログラムはそれを最後までトラバースする必要があります。長さを計算する必要があるため、バイト文字列にパックする前に、リストを完全にロードする必要があります。

「mapM」は「pack」とともにコードの匂いだと思います。「mapM」を「mapM_」に置き換えることもできますが、この場合は「packCStringLen」などのバイト文字列作成関数を使用することをお勧めします。

于 2010-06-27T18:52:34.443 に答える