2010-10-07 30 views
9

मेरे पास कुछ स्थितियां हैं जहां मुझे फ़ाइलों को बार-बार सूचीबद्ध करने की आवश्यकता है, लेकिन मेरे कार्यान्वयन धीमे हो गए हैं। मेरे पास 92784 फाइलों के साथ एक निर्देशिका संरचना है। find फ़ाइलों को 0.5 सेकंड से कम समय में सूचीबद्ध करता है, लेकिन मेरा हास्केल कार्यान्वयन बहुत धीमा है।निर्देशिकाओं को तेजी से कैसे सूचीबद्ध करें?

मेरा पहला कार्यान्वयन पूरा करने के लिए 9 सेकंड से थोड़ा अधिक समय ले गया, अगले संस्करण 5 सेकंड से थोड़ा अधिक और मैं वर्तमान में दो सेकंड से थोड़ा कम नीचे हूं।

listFilesR :: FilePath -> IO [FilePath] 
listFilesR path = let 
    isDODD "." = False 
    isDODD ".." = False 
    isDODD _ = True 

    in do 
     allfiles <- getDirectoryContents path 
    dirs <- forM allfiles $ \d -> 
     if isDODD d then 
     do let p = path </> d 
      isDir <- doesDirectoryExist p 
      if isDir then listFilesR p else return [d] 
     else return [] 
    return $ concat dirs 

परीक्षण स्मृति के बारे में 100 मेगाबाइट (+ आरटीएस -s) लेता है, और कार्यक्रम जीसी में 40% के आसपास खर्च करता है।

मैं अनुक्रम के साथ एक राइटरट मोनैड में लिस्टिंग करने की सोच रहा था, जो कि कॉन्सट्स और सूची निर्माण को रोकने के लिए मोनॉयड के रूप में था। क्या यह संभवतः मदद करता है? मुझे और क्या करना चाहिए?

संपादित करें: मैंने readDirStream का उपयोग करने के लिए फ़ंक्शन को संपादित किया है, और यह स्मृति को नीचे रखने में मदद करता है। अभी भी कुछ आवंटन हो रहा है, लेकिन उत्पादकता दर> 9 5% है और यह एक सेकंड से भी कम समय में चलती है।

यह वर्तमान संस्करण है:

list path = do 
    de <- openDirStream path 
    readDirStream de >>= go de 
    closeDirStream de 
    where 
    go d [] = return() 
    go d "." = readDirStream d >>= go d 
    go d ".." = readDirStream d >>= go d 
    go d x = let newpath = path </> x 
     in do 
      e <- doesDirectoryExist newpath 
      if e 
     then 
      list newpath >> readDirStream d >>= go d 
     else putStrLn newpath >> readDirStream d >>= go d 

उत्तर

5

मुझे लगता है कि System.Directory.getDirectoryContents एक पूरी सूची निर्माण करती है और इसलिए अधिक स्मृति का उपयोग करता है। System.Posix.Directory का उपयोग करने के बारे में कैसे? System.Posix.Directory.readDirStream एक प्रविष्टि एक-एक करके देता है।

इसके अलावा, FileManip library उपयोगी हो सकता है हालांकि मैंने कभी इसका उपयोग नहीं किया है।

+0

मैंने System.Posix.Directory और iteratees का उपयोग करके एक संस्करण बनाया है, अगर यह बेहतर होता तो यह बहुत कुछ नहीं करता था। मुझे मिली एक अजीब बात यह थी कि System.Posix.Directory मुझे लगता है कि कार्यक्षमता प्रदान करने के लिए प्रतीत नहीं होता है।"readdir" एक "संरचना dirent" के लिए एक सूचक देता है, लेकिन ऐसा लगता है कि आप निर्देशिका स्ट्रीम से प्राप्त कर सकते हैं एकमात्र चीज फ़ाइल नाम है - जिसका अर्थ है कि आपको एक और कॉल करना है (संभवतः statDirectoryExist के माध्यम से stat() के लिए) यह एक निर्देशिका है। यह समस्या का एक हिस्सा भी हो सकता है - खोजने के लिए किसी अन्य सिस्कोल को खोजने की आवश्यकता नहीं है कि यह निर्देशिका है या नहीं। – mokus

+0

@mokus: जानकारी के लिए धन्यवाद। पॉज़िक्स सिस्टम में, [readdir] (http://www.opengroup.org/onlinepubs/009695399/functions/readdir.html) द्वारा निर्देशिका पढ़ना वापस नहीं आता है कि लौटाई गई प्रविष्टि एक निर्देशिका है या नहीं, और इसलिए आपको एक अलग की आवश्यकता है syscall (आमतौर पर स्टेट या lstat) यह तय करने के लिए कि यह एक निर्देशिका है या नहीं। इसलिए, आपके द्वारा वर्णित System.Posix.Directory का व्यवहार अजीब नहीं है। खोज कमांड के कुछ कार्यान्वयन हार्ड-लिंक-गिनती चाल का उपयोग करते हैं ताकि अस्थायी कॉल को स्टेट को छोड़ दिया जा सके, जो ट्रैवर्सल को तेज़ी से बनाता है। –

+1

मेरे सिस्टम (मैक ओएस) पर, "स्ट्रेट dirent" में एक फ़ील्ड "d_type" है, जिसका एक संभावित मान "DT_DIR" है। विकिपीडिया संकेत देता है कि यह POSIX spec में वैकल्पिक है, लेकिन यह सुनिश्चित है कि निर्देशिकास्ट्रीम के लिए 'isDir' या 'fileType' ऑपरेशन प्रदान करने के लिए यह एक मजबूत मामला होगा जो उपलब्ध होने पर उस जानकारी का उपयोग करेगा और अन्यथा कॉल कॉल करेगा। यहां तक ​​कि यदि यह आवश्यक मानक नहीं है, यदि उसके प्लेटफ़ॉर्म में यह है, तो मुझे लगता है कि अगर इसका उपयोग नहीं हो रहा है तो मुझे आश्चर्य होगा। – mokus

1

एक समस्या यह है यह, निर्देशिका सामग्री की पूरी सूची के निर्माण के लिए कार्यक्रम उन लोगों के साथ कुछ भी कर सकते से पहले किया है। आलसी आईओ आमतौर पर डूब जाता है, लेकिन असुरक्षित इंटरलेवियो का उपयोग करके मेमोरी उपयोग में काफी कटौती होती है।

listFilesR :: FilePath -> IO [FilePath] 
listFilesR path = 
    let 
    isDODD "." = False 
    isDODD ".." = False 
    isDODD _ = True 
    in unsafeInterleaveIO $ do 
    allfiles <- getDirectoryContents path 
    dirs <- forM allfiles $ \d -> 
     if isDODD d then 
     do let p = path </> d 
      isDir <- doesDirectoryExist p 
      if isDir then listFilesR p else return [d] 
     else return [] 
    return $ concat dirs 
+0

जो लगभग 0.4 सेकंड और 20 मेगाबाइट से बाहर निकल गया। तो थोड़ा बेहतर – Masse

3

आपके कोड को प्रोफाइल करने से पता चलता है कि अधिकांश CPU समय getDirectoryContents, doesDirectoryExist और </> में चला जाता है। इसका मतलब है कि केवल डेटा संरचना को बदलने से बहुत मदद नहीं मिलेगी। यदि आप find के प्रदर्शन से मेल खाना चाहते हैं तो आपको फाइल सिस्टम तक पहुंचने के लिए निचले स्तर के फ़ंक्शंस का उपयोग करना चाहिए, संभवतः उन लोगों को जो Tsuyoshi ने इंगित किया था।

1

क्या यह पढ़ने के साथ संयुक्त कुछ प्रकार के कैश सिस्टम का उपयोग करने का विकल्प होगा? मैं एक एसिंक इंडेक्सिंग सेवा/थ्रेड के बारे में सोच रहा था जिसने पृष्ठभूमि में इस कैश को अद्यतित रखा था, शायद आप कैश को एक साधारण एसक्यूएल-डीबी के रूप में कर सकते हैं जो इसके बाद पूछताछ करते समय आपको कुछ अच्छा प्रदर्शन देगा?

क्या आप अपने "प्रोजेक्ट/विचार" पर कुछ भी विस्तारित कर सकते हैं ताकि हम कुछ विकल्प के साथ आ सकें?

मैं खुद को "पूर्ण अनुक्रमणिका" के लिए नहीं जाना चाहूंगा क्योंकि मैं ज्यादातर वेबबेस सेवाओं का निर्माण करता हूं और "resposnetime" मेरे लिए आलोचना करता है, दूसरी ओर - यदि यह एक नया सर्वर शुरू करने का प्रारंभिक तरीका है तो मुझे यकीन है ग्राहकों को पहली बार इंतजार करना दिमाग नहीं होगा। मैं परिणाम को बाद में लुकअप के लिए डीबी में संग्रहीत करूंगा।

+0

मैं हमेशा नए विचारों के लिए खुला हूं। मैं डेस्कटॉप उपयोग के लिए हाइपर एस्ट्रायर, एक पूर्ण टेक्स्ट सर्च इंजन के लिए एक रैपर लिख रहा हूं। मैं एक भारी कमांड लाइन उपयोगकर्ता हूं, इसलिए मैं एक मूल गैटरर और खोजकर्ता करने की सोच रहा था। इस समय मैंने अपनी बैश-स्क्रिप्ट को हास्केल में परिवर्तित कर दिया है, लेकिन यह अभी भी एकत्रण और खोज के लिए estcmd आदेशों का उपयोग करता है, और सिस्टम प्रक्रिया कॉल बदसूरत हैं। और देशी gatherer के लिए मुझे कम से कम पहले पास के साथ प्रत्येक फ़ाइल को पार्स करने की आवश्यकता है। लेकिन मैं की सूची के बारे में सोच नहीं सकता, केवल फाइलें जो पिछली बार जोड़े या संशोधित हैं। – Masse

+0

ठीक है - आप किस प्रकार के ओएस के लिए निर्माण कर रहे हैं? उदाहरण के लिए। विंडोज़ में नई फाइलों, नामकरण इत्यादि के लिए "निर्देशिका घटनाएं" हैं। यदि आपके पास कुछ प्रकार का "रूट" फ़ोल्डर है तो आप रिकर्सिव ट्रिगरिंग के साथ "रूट इवेंट हैंडलर" डाल सकते हैं। हैवेन्ट ने इसे स्वयं करने की कोशिश की, लेकिन पहली बार कैटलॉग को अनुक्रमणित करने के बाद मैं उस दिशा में देखता हूं। – BerggreenDK

+0

लिनक्स में एक वैश्विक फ़ाइल कैश है, इसलिए आपको एक लिखना नहीं है और इसे एप्लिकेशन के बीच साझा किया जाता है। इसमें निर्देशिका की घटनाएं भी हैं। –

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

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