2012-03-05 4 views
15

Tôi có một tập hợp các bản ghi nhị phân được đóng gói trong một tệp và tôi đang đọc chúng bằng cách sử dụng Data.ByteString.Lazy và Data.Binary.Get. Với thực hiện hiện tại của tôi một tập tin 8Mb mất 6 giây để phân tích cú pháp.Tệp nhị phân phân tích hiệu suất kém trong haskell

import qualified Data.ByteString.Lazy as BL 
import Data.Binary.Get 

data Trade = Trade { timestamp :: Int, price :: Int , qty :: Int } deriving (Show) 

getTrades = do 
    empty <- isEmpty 
    if empty 
    then return [] 
    else do 
     timestamp <- getWord32le   
     price <- getWord32le 
     qty <- getWord16le   
     rest <- getTrades 
     let trade = Trade (fromIntegral timestamp) (fromIntegral price) (fromIntegral qty) 
     return (trade : rest) 

main :: IO() 
main = do 
    input <- BL.readFile "trades.bin" 
    let trades = runGet getTrades input 
    print $ length trades 

Tôi có thể làm gì để làm điều này nhanh hơn?

+0

có một chương trong thế giới thực haskell về profiling, và một vài câu hỏi được gắn thẻ [haskell] + [performance] trên so.com - có lẽ đây là sự giúp đỡ cho yo u. – epsilonhalbe

+0

@epsilonhalbe Cảm ơn, tôi đã tìm kiếm tốt và mẫu này là mẫu trong tài liệu cho Data.Binary.Get. Tôi nghi ngờ đó là một vấn đề 'đệ quy gần đường mòn' nhưng nó hơi xa hơn tôi để tìm ra. –

+0

Điều này là khó khăn như Data.Binary.Get xuất hiện nghiêm ngặt - Tôi đã thực hiện một bình luận trước đó về cố gắng để có được lười biếng tốt hơn, nhưng tôi đã xóa nó vì nó đã không được áp dụng. Câu trả lời của Daniel Fischer cho bạn thấy làm thế nào để làm một công việc tốt hơn để được nghiêm khắc. –

Trả lời

17

Mã của bạn giải mã tệp 8MB trong chưa đầy một giây tại đây (ghc-7.4.1) - tất nhiên tôi đã biên dịch với -O2.

Tuy nhiên, nó cần một lượng không gian ngăn xếp. Bạn có thể giảm

  • thời gian
  • không gian ngăn xếp
  • không gian đống

cần thiết bằng cách thêm nghiêm khắc hơn ở các vị trí thích hợp, và sử dụng một ắc để thu thập các phân tích cú pháp-Somali giao dịch xa.

{-# LANGUAGE BangPatterns #-} 
module Main (main) where 

import qualified Data.ByteString.Lazy as BL 
import Data.Binary.Get 

data Trade = Trade { timestamp :: {-# UNPACK #-} !Int 
        , price :: {-# UNPACK #-} !Int 
        , qty :: {-# UNPACK #-} !Int 
        } deriving (Show) 

getTrades :: Get [Trade] 
getTrades = go [] 
    where 
    go !acc = do 
     empty <- isEmpty 
     if empty 
     then return $! reverse acc 
     else do 
      !timestamp <- getWord32le 
      !price <- getWord32le 
      !qty <- getWord16le 
      let !trade = Trade (fromIntegral timestamp) (fromIntegral price) (fromIntegral qty) 
      go (trade : acc) 

main :: IO() 
main = do 
    input <- BL.readFile "trades.bin" 
    let trades = runGet getTrades input 
    print $ length trades 

Các tính nghiêm minh và giải nén đảm bảo rằng không có công việc đang dở dang để quay lại cắn bạn sau bằng cách tham khảo một phần của ByteString rằng nên đã bị lãng quên.

Nếu bạn cần Trade để có trường lười, bạn vẫn có thể giải mã thông qua loại có trường nghiêm ngặt và map chuyển đổi trong danh sách kết quả để hưởng lợi từ việc giải mã chặt chẽ hơn.

Tuy nhiên, mã vẫn dành nhiều thời gian thu thập rác, do đó, các cải tiến khác có thể vẫn cần thiết.

+3

Cảm ơn bạn rất nhiều vì đã có câu trả lời tuyệt vời! Bạn đã giúp nâng cấp lên một chút. –

19

Tái cấu trúc nó một chút (về cơ bản là gấp bên trái) cho hiệu suất tốt hơn nhiều và giảm chi phí GC khá phân tích cú pháp tệp 8388600 byte.

{-# LANGUAGE BangPatterns #-} 
module Main (main) where 

import qualified Data.ByteString.Lazy as BL 
import Data.Binary.Get 

data Trade = Trade 
    { timestamp :: {-# UNPACK #-} !Int 
    , price  :: {-# UNPACK #-} !Int 
    , qty  :: {-# UNPACK #-} !Int 
    } deriving (Show) 

getTrade :: Get Trade 
getTrade = do 
    timestamp <- getWord32le 
    price  <- getWord32le 
    qty  <- getWord16le 
    return $! Trade (fromIntegral timestamp) (fromIntegral price) (fromIntegral qty) 

countTrades :: BL.ByteString -> Int 
countTrades input = stepper (0, input) where 
    stepper (!count, !buffer) 
    | BL.null buffer = count 
    | otherwise  = 
     let (trade, rest, _) = runGetState getTrade buffer 0 
     in stepper (count+1, rest) 

main :: IO() 
main = do 
    input <- BL.readFile "trades.bin" 
    let trades = countTrades input 
    print trades 

Và thống kê thời gian chạy có liên quan. Mặc dù số phân bổ là gần, kích thước heap và GC là khá khác nhau giữa các phiên bản.

Tất cả các ví dụ ở đây được tạo bằng GHC 7.4.1 -O2.

Nguồn gốc, chạy với + RTS -K1G -RTS do quá mức sử dụng ngăn xếp không gian:

 
    426,003,680 bytes allocated in the heap 
    443,141,672 bytes copied during GC 
     99,305,920 bytes maximum residency (9 sample(s)) 
      203 MB total memory in use (0 MB lost due to fragmentation) 

    Total time 0.62s ( 0.81s elapsed) 

    %GC  time  83.3% (86.4% elapsed) 

sửa đổi Daniel:

 
    357,851,536 bytes allocated in the heap 
    220,009,088 bytes copied during GC 
     40,846,168 bytes maximum residency (8 sample(s)) 
       85 MB total memory in use (0 MB lost due to fragmentation) 

    Total time 0.24s ( 0.28s elapsed) 

    %GC  time  69.1% (71.4% elapsed) 

Và bài viết này:

 
    290,725,952 bytes allocated in the heap 
     109,592 bytes copied during GC 
      78,704 bytes maximum residency (10 sample(s)) 
       2 MB total memory in use (0 MB lost due to fragmentation) 

    Total time 0.06s ( 0.07s elapsed) 

    %GC  time  5.0% (6.0% elapsed) 
+1

Cảm ơn cải thiện tốt đẹp! –