2010-08-04 15 views
18

हाल ही में एक ऐसे प्रोजेक्ट पर काम करने के लिए जिसकी मुझे आवश्यकता है, उससे कुछ और आईओ इंटरैक्शन की आवश्यकता है, मुझे लगा जैसे मैं नियमित पुस्तकालयों (विशेष रूप से कॉमन्स आईओ) को देखना चाहता था और कुछ और से निपटना चाहता था गहराई आईओ मुद्दों।जावा मल्टीथ्रेडेड फ़ाइल डाउनलोड प्रदर्शन

अकादमिक परीक्षण के रूप में, मैंने एक बुनियादी, बहु-थ्रेडेड HTTP डाउनलोडर को लागू करने का निर्णय लिया। विचार सरल है: डाउनलोड करने के लिए एक यूआरएल प्रदान करें, और कोड फ़ाइल डाउनलोड करेगा। डाउनलोड गति को बढ़ाने के लिए, फ़ाइल को खंडित किया गया है और जितना संभव हो उतना बैंडविड्थ का उपयोग करने के लिए प्रत्येक खंड एक साथ डाउनलोड किया जाता है (HTTP Range: bytes=x-x शीर्षलेख का उपयोग करके)।

मेरे पास एक कामकाजी प्रोटोटाइप है, लेकिन जैसा कि आपने अनुमान लगाया होगा, यह बिल्कुल आदर्श नहीं है। फिलहाल मैं मैन्युअल रूप से 3 "डाउनलोडर" धागे शुरू करता हूं जो प्रत्येक फ़ाइल के 1/3 डाउनलोड करते हैं। ये धागे वास्तव में डिस्क पर फ़ाइलों को लिखने के लिए एक सामान्य, सिंक्रनाइज़ "फ़ाइल लेखक" उदाहरण का उपयोग करते हैं। जब सभी धागे किए जाते हैं, तो "फ़ाइल लेखक" पूरा हो जाता है और कोई भी खुली धाराएं बंद होती हैं। कोड में से कुछ के टुकड़े आप यह अनुमान लगा रहे हैं:

धागा शुरू हुआ:

ExecutorService downloadExecutor = Executors.newFixedThreadPool(3); 
... 
downloadExecutor.execute(new Downloader(fileWriter, download, start1, end1)); 
downloadExecutor.execute(new Downloader(fileWriter, download, start2, end2)); 
downloadExecutor.execute(new Downloader(fileWriter, download, start3, end3)); 

प्रत्येक "डाउनलोडर" धागा एक हिस्सा (बफ़र) डाउनलोड करता है और डिस्क पर लिखने के लिए "फ़ाइल लेखक" का उपयोग करता है:

public synchronized void write(byte[] bytes, int len, long start) throws IOException 
{ 
     output.seek(start); 
     output.write(bytes, 0, len); 
} 
:

int bytesRead = 0; 
byte[] buffer = new byte[1024*1024]; 
InputStream inStream = entity.getContent(); 
long seekOffset = chunkStart; 
while ((bytesRead = inStream.read(buffer)) != -1) 
{ 
    fileWriter.write(buffer, bytesRead, seekOffset); 
    seekOffset += bytesRead; 
} 

"फ़ाइल लेखक" एक RandomAccessFileseek() करने और write() डिस्क के लिए मात्रा का उपयोग कर डिस्क के लिए लिखता है

सभी चीजों को माना जाता है, यह दृष्टिकोण काम करने लगता है। हालांकि, यह बहुत अच्छी तरह से काम नहीं करता है। मैं निम्नलिखित बिंदुओं पर कुछ सलाह/सहायता/राय की सराहना करता हूं। बहुत सराहना की।

  1. इस कोड का CPU उपयोग छत के माध्यम से है। यह ऐसा करने के लिए आधे मेरे सीपीयू (2 कोरों में से प्रत्येक का 50%) का उपयोग कर रहा है, जो तुलनीय डाउनलोड टूल्स की तुलना में तेजी से अधिक है जो सीपीयू को मुश्किल से तनाव देता है। मैं थोड़ा सा रहस्यमय हूं कि यह सीपीयू उपयोग कहां से आता है, क्योंकि मुझे इसकी उम्मीद नहीं थी।
  2. आमतौर पर, 312 धागे में से 1 प्रतीत होता है जो के पीछे महत्वपूर्ण है। अन्य 2 धागे खत्म हो जाएंगे, जिसके बाद यह तीसरा धागा लेता है (जो कि पहले खंड के साथ सबसे पहले धागा लगता है) पूरा करने के लिए 30 या अधिक सेकंड। मैं टास्क मैनेजर से देख सकता हूं कि जाव प्रक्रिया अभी भी छोटे आईओ लिख रही है, लेकिन मुझे नहीं पता कि यह क्यों होता है (मैं दौड़ की स्थिति का अनुमान लगा रहा हूं?)।
  3. इस तथ्य के बावजूद कि मैंने काफी बड़ा बफर (1 एमबी) चुना है, मुझे लगता है कि InputStream लगभग वास्तव में कभी भी बफर भरता नहीं है, जो मुझे पसंद होने से अधिक आईओ लिखता है। मैं इस धारणा के तहत हूं कि इस परिदृश्य में, आईओ पहुंच को न्यूनतम रखने के लिए सबसे अच्छा होगा, लेकिन मुझे यह सुनिश्चित नहीं है कि यह सबसे अच्छा तरीका है या नहीं।
  4. मुझे एहसास है कि जावा ऐसा कुछ करने के लिए आदर्श भाषा नहीं हो सकता है, लेकिन मुझे पूरा विश्वास है कि मेरे वर्तमान कार्यान्वयन में मुझे जितना अधिक प्रदर्शन करना है। एनआईओ इस मामले में तलाश लायक है?

नोट: मैं है कि कौन सी HTTP बातचीत करने के लिए, Apache httpclient का उपयोग जहां entity.getContent() से (मामले में किसी को भी सोच रहा है) आता है।

+0

मिले यहाँ एक अच्छा संबंधित विषय: http://stackoverflow.com/questions/921262/how-to-download-and-save-a-file-from-internet-using-java दे सकता है कि आज रात आज़माएं जब मैं घर जाऊं :) – tmbrggmn

+0

अद्यतन: उच्च CPU उपयोग निष्पादक सेवा पर थोड़ी देर() लूप के कारण था (निर्धारित विधि)। रवींद्र! – tmbrggmn

+0

मुझे लगता है कि नेटवर्क कॉन्फ़िगरेशन पर भी निर्भर करता है, और नेटवर्क इंटरफेस कार्ड (भौतिक)। भले ही आपके पास एक ही फ़ाइल को डाउनलोड करने पर काम करने वाले कई धागे हैं लेकिन एनआईसी, जो बाइट्स को क्रमबद्ध करने के लिए ज़िम्मेदार है, बाधा बन सकती है !! – TriCore

उत्तर

6

मेरे अपने सवालों के जवाब देने के लिए:

  1. वृद्धि हुई CPU उपयोग एक while() {} पाश कि धागे को समाप्त करने के लिए इंतज़ार कर रहा था की वजह से था। जैसा कि यह निकलता है, awaitTerminationExecutor को समाप्त करने के लिए प्रतीक्षा करने के लिए एक बेहतर विकल्प है :)
  2. (और 3 और 4) यह जानवर की प्रकृति प्रतीत होता है; अंत में मैंने उन विभिन्न थ्रेडों के सावधानीपूर्वक सिंक्रनाइज़ेशन का उपयोग करके जो करना चाहता था, वह हासिल किया जो प्रत्येक डेटा का एक हिस्सा डाउनलोड करता है (ठीक है, विशेष रूप से इन हिस्सों के डिस्क को वापस डिस्क पर लिखता है)।
2

विंडोज पर सर्वश्रेष्ठ प्रदर्शन के लिए मेरा तत्काल विचार IO completions ports का उपयोग करना होगा।जो मुझे नहीं पता है (ए) क्या अन्य ओएस में समान अवधारणाएं हैं, और (बी) क्या कोई उपयुक्त जावा रैपर हैं? यदि पोर्टेबिलिटी आपके लिए महत्वपूर्ण नहीं है, हालांकि, जेएनआई के साथ अपना खुद का रैपर रोल करना संभव हो सकता है।

3

संभवतः अपाचे HTTP क्लाइंट छोटे बफर के साथ कुछ बफरिंग कर रहा है। इसे HTTP शीर्षलेख को उचित रूप से पढ़ने के लिए एक बफर की आवश्यकता होगी, और शायद चंकित एन्कोडिंग को संभालना होगा।

0

एक बहुत बड़ी सॉकेट प्राप्त बफर सेट करें। लेकिन वास्तव में आपका प्रदर्शन नेटवर्क बैंडविड्थ द्वारा सीमित होगा, सीपीयू बैंडविड्थ नहीं। आप जो भी कर रहे हैं वह वास्तव में प्रत्येक डाउनलोडर को नेटवर्क बैंडविड्थ के 1/3 आवंटित कर रहा है। अगर आपको बहुत लाभ मिलता है तो मुझे आश्चर्य होगा।

+1

हस्तांतरण की शुरुआत में संक्षेप में तीन कनेक्शन एक से तेज हो सकते हैं। यह इष्टतम विंडो आकार खोजने के लिए टीसीपी को थोड़ा सा लगता है, इसलिए यदि आप समांतर कनेक्शन का उपयोग करते हैं, तो यह प्रक्रिया 3x तेज हो जाती है! – Karmastan

+0

यही कारण है कि मैं पहली जगह में बदल रहा हूं, फ़ाइल को 3 हिस्सों में विभाजित करने से मुझे एक ही फ़ाइल को 3 गुना तेजी से डाउनलोड करने की अनुमति मिलती है, मानते हैं कि एक सिंगल हिस्सा कनेक्शन को संतृप्त करने में बहुत धीमा है। हालांकि डाउनलोड की गति अधिकतम हो गई है, इसलिए यह कोई मुद्दा नहीं है। यह सीपीयू उपयोग है जिसके बारे में मुझे चिंता है। क्या यह इस तथ्य से संबंधित हो सकता है कि कोड ग्रहण में निष्पादित हो रहा है? – tmbrggmn

+3

ठीक है आप एन -1 अनावश्यक खोज कर रहे हैं, और आखिरी बार मैंने इसे देखा, जो दशकों पहले था, एक आश्चर्यजनक महंगा ऑपरेशन था। प्रत्येक लेखक को केवल एक बार खोजना पड़ता है; उसके बाद यह अनुक्रमिक I/O है। – EJP

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