2011-11-15 21 views
5

मेरे previous से एक और हास्केल अनुकूलन प्रश्न। मैं रिकर्सिवली एक सूची उत्पन्न करने, fibs समारोह कई परिचयात्मक हास्केल आलेखों में मिल के लिए इसी तरह की जरूरत है:हास्केल रिकर्सिव सूचियों को अनुकूलित करना

generateSchedule :: [Word32] -> [Word32] 
generateSchedule blkw = take 80 ws 
    where 
    ws   = blkw ++ zipWith4 gen (drop 13 ws) (drop 8 ws) (drop 2 ws) ws 
    gen a b c d = rotate (a `xor` b `xor` c `xor` d) 1 

ऊपर समारोह सबसे अधिक समय और मेरे लिए alloc -consuming समारोह के रूप में आगे निकल गया है। प्रोफाइलर मेरा पीछा आंकड़े दिखाती है:

COST CENTRE  MODULE    %time %alloc ticks  bytes 
generateSchedule Test.Hash.SHA1  22.1 40.4 31  702556640 

मैं इस सूची की गणना करने के अनबॉक्स्ड वैक्टर लागू करने के बारे में सोचा, लेकिन यह करने के लिए के बाद से सूची पुनरावर्ती है एक तरह से समझ नहीं सकता। इसका सी में प्राकृतिक कार्यान्वयन होगा लेकिन मुझे इसे तेजी से बनाने का कोई तरीका नहीं दिख रहा है (अनलॉक करने और परिवर्तनीय घोषणाओं की 80 लाइनों को लिखने के अलावा)। कोई मदद?

अपडेट: मैंने वास्तव में यह देखने के लिए इसे तुरंत अनलॉक कर दिया है कि यह मदद करता है या नहीं। कोड here है। यह बदसूरत है, और वास्तव में यह धीमा था।

COST CENTRE  MODULE    %time %alloc ticks  bytes 
generateSchedule GG.Hash.SHA1  22.7 27.6 40  394270592 
+5

मुझे अभी भी लगता है कि आपको सूचियों को पूरी तरह से मिटाना होगा। मध्यस्थ के रूप में नहीं, अपने डेटा को 'बाइटस्ट्रिंग' के रूप में इनपुट करें और डेटा का उपयोग करें। वेक्टर। स्टेट या कुछ ऐसे। जब इनपुट शब्दों की सूची होती है तो मुझे अनुकूलन में बहुत अधिक बिंदु नहीं दिखता है। यदि आप पूरी तरह से 'partcb' को अनलॉक करते हैं तो आपको इस' जेनरशेड्यूल 'फ़ंक्शन (' partab') की भी आवश्यकता नहीं होगी; यह अनियंत्रण में स्पष्ट (रेखांकित) होगा। इसके अलावा: आप इस में पर्याप्त प्रयास कर रहे हैं कि मैं लक्ष्य के रूप में उत्सुक हूं - क्या यह शिक्षा है या आप उत्पादन कोड में कार्यान्वयन का उपयोग करना चाहते हैं? यदि # 2, क्या 'क्रिप्टोश' से बचने का कोई कारण है? –

+0

शिक्षा। मैं जानना चाहता हूं कि मैं स्वच्छ, बेवकूफ हास्केल लिख सकता हूं जो ऑप्टिमाइज़ेशन चाल का उपयोग किए बिना गति में तुलनीय है जो मुझे इच्छा है कि मैंने इसे सी में लिखा है। एसएचए 1 के मामले में, मुझे ऐसा लगता है कि ऐसा ही मामला है। मैंने [Data.Digest.Pure.SHA] के लिए स्रोत भी देखा (http://hackage.haskell.org/packages/archive/SHA/1.5.0.0/doc/html/src/Data-Digest-Pure-SHA .html)। यह बहुत अच्छा है - सी 4 कार्यान्वयन की गति 2-3x के भीतर। लेकिन यह ऐसा कोड नहीं है जिसे मैं लिखना चाहता हूं। – Ana

+0

एफवाईआई मेरा पूरा, नवीनतम कोड [यहां] (https://gist.github.com/5a7c61e057c94f35b87b)। – Ana

उत्तर

5
import Data.Array.Base 
import Data.Array.ST 
import Data.Array.Unboxed 

generateSchedule :: [Word32] -> UArray Int Word32 
generateSchedule ws0 = runSTUArray $ do 
    arr <- unsafeNewArray_ (0,79) 
    let fromList i [] = fill i 0 
     fromList i (w:ws) = do 
      unsafeWrite arr i w 
      fromList (i+1) ws 
     fill i j 
      | i == 80 = return arr 
      | otherwise = do 
       d <- unsafeRead arr j 
       c <- unsafeRead arr (j+2) 
       b <- unsafeRead arr (j+8) 
       a <- unsafeRead arr (j+13) 
       unsafeWrite arr i (gen a b c d) 
       fill (i+1) (j+1) 
    fromList 0 ws0 

एक अनबॉक्स्ड सरणी अपनी सूची के लिए इसी का निर्माण करेगा। यह इस धारणा पर निर्भर करता है कि सूची तर्क में कम से कम 14 और अधिकतम 80 आइटम हैं, अन्यथा यह बुरी तरह गलत व्यवहार करेगा। मुझे लगता है कि यह हमेशा 16 आइटम (64 बाइट्स) होगा, इसलिए यह आपके लिए सुरक्षित होना चाहिए। (लेकिन इंटरमीडिएट सूची बनाने के बजाए बाइटस्ट्रिंग से सीधे भरना शुरू करना बेहतर है।)

हैशिंग राउंड करने से पहले इसका सख्ती से मूल्यांकन करके, आप सूची निर्माण और आपके पास हैशिंग के बीच स्विचिंग को सहेजते हैं आलसी कथित सूची, जो आवश्यक समय को कम करना चाहिए। एक अनबॉक्स किए गए सरणी का उपयोग करके हम सूचियों के आवंटन ओवरहेड से बचते हैं, जो गति को और बेहतर कर सकता है (लेकिन ghc का आवंटन बहुत तेज़ है, इसलिए उससे बहुत अधिक प्रभाव की उम्मीद न करें)।

अपने हैशिंग राउंड में, अनावश्यक सीमा-जांच से बचने के लिए unsafeAt array t के माध्यम से आवश्यक Word32 प्राप्त करें।

परिशिष्ट: यदि आप, प्रत्येक wn पर एक धमाके कर दिया, हालांकि मुझे यकीन है कि नहीं कर रहा हूँ सूची के निर्माण unrolling तेजी से हो सकता है। चूंकि आपके पास पहले से कोड है, बैंग जोड़ना और जांच करना ज्यादा काम नहीं है, है ना? मैं उत्सुक हूँ।

+0

हाय डैनियल। जैसा आपने सुझाव दिया मैंने किया। संख्याएं 13.4%, बैंग के बाद 15.7% और 30%, 27% पहले हैं। तो यह रनटाइम और आवंटन के लगभग आधे हिस्से में कटौती करता है। महान विचार। – Ana

+1

वेक्टर समाधान दो। धन्यवाद। लेकिन मुझे सभी असुरक्षित * कॉल के साथ गर्म अस्पष्टता नहीं मिल रही है। इसके अलावा, यह उस बिंदु पर जा रहा है जहां हास्केल का लाभ, यानी स्पष्टता, संक्षिप्तता, गति के लिए wrangling कोड द्वारा छायांकित है। अगर मुझे ऐसा करना है, तो मैं इसे मूल रूप से सुझाए गए अनुसार सी में लिखूंगा, मुझे लगता है। – Ana

+0

यहां, 'असुरक्षित' का अर्थ केवल कोई सीमा-जांच नहीं है (और 'unsafeNewArray_' में कोई प्रारंभिकरण नहीं)। चूंकि सीमा-जांच कॉलिंग कोड में है, इसलिए यह वास्तव में सुरक्षित है (यदि 'ws0' की लंबाई पर पूर्व शर्त है)। यह एक दुर्भाग्यपूर्ण नामकरण है, यह 'अनचेक XXX' कार्यों को कॉल करना बेहतर होगा। स्पष्टता और एकता के लिए, ठीक है, यह एक व्यापार है। जब तक हम अधिक कंपाइलर और संलयन जादू प्राप्त नहीं करते हैं, कभी-कभी आपको बदसूरत अनिवार्य कोड लिखना पड़ता है। लेकिन हास्केल में यह अधिक स्थानीयकृत है, एक बार निम्न स्तर तक पहुंचने के बाद, उपयोग कोड अच्छा और रचनात्मक हो सकता है। –

1

हम आलसी घरों को सीधे उत्परिवर्तनीय और शुद्ध सूचियों का उपयोग करने के बीच आधा रास्ते पाने के लिए उपयोग कर सकते हैं। आपको एक पुनरावर्ती परिभाषा के लाभ मिलते हैं, लेकिन इसी कारण से आलस्य और मुक्केबाजी की कीमत का भुगतान करते हैं - हालांकि सूचियों के मुकाबले कम है। निम्नलिखित कोड का उपयोग करता कसौटी दो आलसी सरणी समाधान (मानक सरणियों का उपयोग कर, और वैक्टर) के साथ ही मूल सूची कोड और ऊपर डैनियल परिवर्तनशील uarray कोड का परीक्षण करने के लिए: के लिए मजबूर करने

module Main where 
import Data.Bits 
import Data.List 
import Data.Word 
import qualified Data.Vector as LV 
import Data.Array.ST 
import Data.Array.Unboxed 
import qualified Data.Array as A 
import Data.Array.Base 
import Criterion.Main 

gen :: Word32 -> Word32 -> Word32 -> Word32 -> Word32 
gen a b c d = rotate (a `xor` b `xor` c `xor` d) 1 

gss blkw = LV.toList v 
    where v = LV.fromList $ blkw ++ rest 
      rest = map (\i -> gen (LV.unsafeIndex v (i + 13)) 
           (LV.unsafeIndex v (i + 8)) 
           (LV.unsafeIndex v (i + 2)) 
           (LV.unsafeIndex v i) 
        ) 
       [0..79 - 14] 

gss' blkw = A.elems v 
    where v = A.listArray (0,79) $ blkw ++ rest 
      rest = map (\i -> gen (unsafeAt v (i + 13)) 
           (unsafeAt v (i + 8)) 
           (unsafeAt v (i + 2)) 
           (unsafeAt v i) 
        ) 
       [0..79 - 14] 

generateSchedule :: [Word32] -> [Word32] 
generateSchedule blkw = take 80 ws 
    where 
    ws   = blkw ++ zipWith4 gen (drop 13 ws) (drop 8 ws) (drop 2 ws) ws 

gs :: [Word32] -> [Word32] 
gs ws = elems (generateSched ws) 

generateSched :: [Word32] -> UArray Int Word32 
generateSched ws0 = runSTUArray $ do 
    arr <- unsafeNewArray_ (0,79) 
    let fromList i [] = fill i 0 
     fromList i (w:ws) = do 
      unsafeWrite arr i w 
      fromList (i+1) ws 
     fill i j 
      | i == 80 = return arr 
      | otherwise = do 
       d <- unsafeRead arr j 
       c <- unsafeRead arr (j+2) 
       b <- unsafeRead arr (j+8) 
       a <- unsafeRead arr (j+13) 
       unsafeWrite arr i (gen a b c d) 
       fill (i+1) (j+1) 
    fromList 0 ws0 

args = [0..13] 

main = defaultMain [ 
     bench "list" $ whnf (sum . generateSchedule) args 
     ,bench "vector" $ whnf (sum . gss) args 
     ,bench "array" $ whnf (sum . gss') args 
     ,bench "uarray" $ whnf (sum . gs) args 
     ] 

मैं -O2 साथ कोड संकलित और -funfolding-use-threshold=256 बहुत सारे इनलाइनिंग।

कसौटी मानक दर्शाते हैं कि वेक्टर समाधान थोड़ा बेहतर है, और सरणी समाधान थोड़ा बेहतर अभी भी, लेकिन वह अनबॉक्स्ड परिवर्तनशील समाधान अभी भी भारी बहुमत से जीत:

benchmarking list 
mean: 8.021718 us, lb 7.720636 us, ub 8.605683 us, ci 0.950 
std dev: 2.083916 us, lb 1.237193 us, ub 3.309458 us, ci 0.950 

benchmarking vector 
mean: 6.829923 us, lb 6.725189 us, ub 7.226799 us, ci 0.950 
std dev: 882.3681 ns, lb 76.20755 ns, ub 2.026598 us, ci 0.950 

benchmarking array 
mean: 6.212669 us, lb 5.995038 us, ub 6.635405 us, ci 0.950 
std dev: 1.518521 us, lb 946.8826 ns, ub 2.409086 us, ci 0.950 

benchmarking uarray 
mean: 2.380519 us, lb 2.147896 us, ub 2.715305 us, ci 0.950 
std dev: 1.411092 us, lb 1.083180 us, ub 1.862854 us, ci 0.950 

मैं कुछ बुनियादी रूपरेखा भी भाग गया, और ध्यान दिया कि आलसी/बॉक्स किए गए सरणी समाधान सूची समाधान से थोड़ा बेहतर थे, लेकिन फिर से अनबॉक्स किए गए सरणी दृष्टिकोण से काफी खराब।

+0

कड़ाई से बोलते हुए, यह '[0..79 - लंबाई blkw] '(यानी' [0..15]' होना चाहिए क्योंकि एना हमेशा 64 की इस सूचियां दे रहे हैं), लेकिन आपने 'LV.take' का उपयोग किया था एक बैकअप (स्मार्ट)। अगर कोई मापनीय अंतर है तो मैं उत्सुक हूं। उसी समय, मैं पता लगाने के लिए बहुत आलसी हूँ। –

+0

@ थॉमस: हाँ, मैंने कोड को धराशायी कर दिया और इसे जल्दी से परीक्षण किया और इसलिए कुछ चीजों के बारे में आलसी था। मैं अगले दिन या तो कुछ बेंचमार्क चलाऊंगा और ट्यून और अपडेट करूंगा। – sclv

संबंधित मुद्दे