2012-03-05 16 views
15

में खराब प्रदर्शन पार्सिंग बाइनरी फ़ाइल में मेरे पास फ़ाइल में पैक किए गए बाइनरी रिकॉर्ड का एक सेट है और मैं उन्हें डेटा.बेटस्ट्रिंग.लाज़ी और डेटा। बाइनरी.गेट का उपयोग करके पढ़ रहा हूं। मेरे वर्तमान कार्यान्वयन के साथ एक 8 एमबी फ़ाइल पार्स करने में 6 सेकंड लगती है।हैकेल

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 

यह तेज़ी से करने के लिए मैं क्या कर सकता हूं?

+0

प्रोफाइलिंग के बारे में असली दुनिया हैकेल में एक अध्याय है, और कुछ प्रश्नों को टैग किया गया है [हैकेल] + [प्रदर्शन] so.com पर - शायद यह आपके लिए मदद की है यू। – epsilonhalbe

+0

@epsilonhalbe धन्यवाद, मेरे पास अच्छी खोज थी और यह पैटर्न Data.Binary.Get के दस्तावेज़ों में से एक है। मुझे संदेह है कि यह 'लगभग ट्रेल रिकर्सन' समस्या है लेकिन इसे समझने के लिए यह थोड़ा सा है। –

+0

यह डेटा के रूप में मुश्किल है। बाइनरी.गेट सख्त दिखाई देता है - मैंने बेहतर आलस्य पाने की कोशिश करने के बारे में पिछली टिप्पणी की है, लेकिन मैंने इसे हटा दिया है क्योंकि यह लागू नहीं था। डैनियल फिशर का जवाब आपको दिखाता है कि सख्त होने का बेहतर काम कैसे करें। –

उत्तर

17

आपका कोड यहां एक सेकंड से भी कम समय में 8 एमबी फ़ाइल डीकोड करता है (ghc-7.4.1) - बेशक मैंने -O2 के साथ संकलित किया।

हालांकि, इसे स्टैक स्पेस की एक अनोखी मात्रा की आवश्यकता थी। आप

  • के समय को कम कर सकते हैं
  • ढेर अंतरिक्ष
  • ढेर अंतरिक्ष

उपयुक्त स्थानों में अधिक कड़ाई जोड़ने, और एक संचायक का उपयोग करके जरूरत पार्स-तथाकथित इकट्ठा करने के लिए दूर व्यापार।

{-# 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 

कठोरता और unpacking सुनिश्चित करें कि कोई काम वापस आने के लिए ByteString कि पहले से ही भूल किया जाना चाहिए था का एक हिस्सा हवाला बाद में आप काट पूर्ववत छोड़ दिया है बनाता है।

आप Trade की जरूरत है आलसी क्षेत्रों के लिए, आप अभी भी सख्त खेतों और map परिणाम सूची पर रूपांतरण के साथ एक प्रकार के माध्यम से सख्त डिकोडिंग से लाभ प्राप्त करने को डिकोड कर सकते हैं।

हालांकि, कोड अभी भी कचरा इकट्ठा करने में बहुत समय बिताता है, इसलिए और भी सुधार आवश्यक हो सकते हैं।

+3

एक महान उत्तर के लिए बहुत बहुत धन्यवाद! आपने एक नोब स्तर को थोड़ा सा मदद की है। –

19

इसे थोड़ा सा रेफैक्टर करना (मूल रूप से बाएं-गुना) बहुत बेहतर प्रदर्शन देता है और जीसी ओवरहेड को 8388600 बाइट फ़ाइल को थोड़ा सा पार्सिंग करता है।

{-# 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 

और संबंधित रनटाइम आंकड़े। हालांकि आवंटन संख्याएं करीब हैं, फिर भी जीसी और अधिकतम ढेर आकार संशोधन के बीच काफी अलग हैं।

यहां सभी उदाहरण जीएचसी 7.4.1-ओ 2 के साथ बनाए गए थे।

मूल स्रोत, अत्यधिक ढेर अंतरिक्ष उपयोग के कारण के साथ + आरटीएस -K1G -RTS चलाएँ:

 
    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) 

डैनियल संशोधन:

 
    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) 

और इस पोस्ट:

 
    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

धन्यवाद अच्छा सुधार! –