2010-09-29 19 views
6

मैं कुछ मल्टीथ्रेडिंग निर्माण के साथ प्रयोग कर रहा हूं, लेकिन किसी भी तरह ऐसा लगता है कि मल्टीथ्रेडिंग एक थ्रेड से तेज नहीं है। मैंने इसे एक नेस्टेड लूप (1000x1000) के साथ एक बहुत ही सरल परीक्षण में संकुचित कर दिया जिसमें सिस्टम केवल मायने रखता है।
नीचे मैंने एकल थ्रेडिंग और मल्टीथ्रेडिंग और उन्हें निष्पादित करने के लिए कोड पोस्ट किया है।
परिणाम यह है कि एकल धागा लगभग 110 एमएस में लूप को पूरा करता है, जबकि दो थ्रेड भी 112 एमएस लेते हैं।
मुझे नहीं लगता कि समस्या multithreading का ओवरहेड है। अगर मैं केवल थ्रेडपूलएक्सएटर को दोनों रननेबल्स सबमिट करता हूं, तो यह एकल थ्रेड के आधे समय में निष्पादित होता है, जो समझ में आता है। लेकिन उस दूसरे रननेबल को जोड़कर यह 10 गुना धीमा हो जाता है। 3.00 गीगा कोर दोनों 100% चल रहे हैं।
मुझे लगता है कि यह पीसी-विशिष्ट हो सकता है, क्योंकि किसी और के पीसी ने मल्टीथ्रेडिंग पर डबल-स्पीड परिणाम दिखाए हैं। लेकिन फिर, मैं इसके बारे में क्या कर सकता हूं? मेरे पास इंटेल पेंटियम 4 3.00 गीगाहर्ट्ज (2 सीपीयू) और जावा जेआर 6 है।

टेस्ट कोड:मल्टीथ्रेडिंग सिंगल थ्रेड (सरल लूप टेस्ट) से तेज नहीं है

// Single thread: 
long start = System.nanoTime(); // Start timer 
final int[] i = new int[1];  // This is to keep the test fair (see below) 
int i = 0; 
for(int x=0; x<10000; x++) 
{ 
    for(int y=0; y<10000; y++) 
    { 
     i++; // Just counting... 
    } 
} 
int i0[0] = i; 
long end = System.nanoTime(); // Stop timer 

इस कोड के बारे में 110 एमएस में मार डाला गया है।

// Two threads: 

start = System.nanoTime(); // Start timer 

// Two of the same kind of variables to count with as in the single thread. 
final int[] i1 = new int [1]; 
final int[] i2 = new int [1]; 

// First partial task (0-5000) 
Thread t1 = new Thread() { 
    @Override 
    public void run() 
    { 
     int i = 0; 
     for(int x=0; x<5000; x++) 
      for(int y=0; y<10000; y++) 
       i++; 
     i1[0] = i; 
    } 
}; 

// Second partial task (5000-10000) 
Thread t2 = new Thread() { 
    @Override 
    public void run() 
    { 
     int i = 0; 
     for(int x=5000; x<10000; x++) 
      for(int y=0; y<10000; y++) 
       i++; 
     int i2[0] = i; 
    } 
}; 

// Start threads 
t1.start(); 
t2.start(); 

// Wait for completion 
try{ 
    t1.join(); 
    t2.join(); 
}catch(Exception e){ 
    e.printStackTrace(); 
} 

end = System.nanoTime(); // Stop timer 

इस कोड के बारे में 112 एमएस में मार डाला गया है।

संपादित करें: मैंने रननेबल को थ्रेड में बदल दिया और निष्पादक सेवा (समस्या की सादगी के लिए) से छुटकारा पा लिया।

संपादित करें:, कुछ सुझाव

+0

तो, क्या आपने सुझावों का प्रयास किया है? –

+0

आह, पेंटियम 4 - मेरा अद्यतन उत्तर देखें :) – snemarch

उत्तर

11

आप निश्चित रूप से Thread.isAlive() मतदान नहीं करना चाहते हैं - यह किसी भी अच्छे कारण के लिए बहुत सी CPU चक्रों को जलता है। इसके बजाय Thread.join() का उपयोग करें।

इसके अलावा, शायद यह एक अच्छा विचार नहीं है कि धागे सीधे परिणाम सरणी, कैश लाइनों और सभी को बढ़ाते हैं। स्थानीय चर अद्यतन करें, और गणनाओं के दौरान एक ही स्टोर करें।

संपादित करें:

पूरी तरह से अनदेखी की है कि आप, एक पेंटियम 4 का उपयोग कर रहे जहाँ तक मुझे पता के रूप में वहाँ पी 4 का कोई मल्टीकोर संस्करणों है - मल्टीकोर का भ्रम देने के लिए, यह Hyper-Threading है: दो तार्किक कोरनिष्पादन इकाइयों एक भौतिक कोर साझा करें। यदि आपके धागे एक ही निष्पादन इकाइयों पर निर्भर करते हैं, तो आपका प्रदर्शन एकल-थ्रेडेड प्रदर्शन के समान (या इससे भी बदतर) होगा। उदाहरण के लिए, आपको एक थ्रेड में फ्लोटिंग-पॉइंट गणना और प्रदर्शन सुधार प्राप्त करने के लिए दूसरे में पूर्णांक कैल्क्स की आवश्यकता होगी।

पी 4 एचटी कार्यान्वयन की बहुत आलोचना की गई है, नए कार्यान्वयन (हालिया कोर 2) बेहतर होना चाहिए।

+0

+1 - पहला पैराग्राफ शायद सबसे अधिक अंतर है। –

+0

+1 - असल में, दोनों सुझाव प्रक्रिया को तेज करते हैं, धन्यवाद। लेकिन कुछ अजीब बात है: Thread.isAlive() का उपयोग सीधे बढ़ते सरणी के साथ संयोजन में, थ्रेड.जॉइन() (2200 एमएस) का उपयोग करने से तेज (800 एमएस) है, लेकिन आपके दूसरे सुझाव के साथ संयोजन में isAlive() का उपयोग करके धीमा है (1 9 0 एमएस) शामिल होने से() (114 एमएस)। वैसे भी, आपके सुझावों का उपयोग करके प्रणाली को 2200 मीटर से 114 तक डी तक बढ़ा दिया जाता है: डी। हालांकि, आपका दूसरा सुझाव एकल धागे को लगभग 110 एमएस तक गति देता है, इसलिए अभी तक कोई अंतर नहीं है। – RemiX

+0

मल्टीटास्किंग ओएस पर चलने पर 10ms से कम का अंतर वास्तव में आपको कुछ भी नहीं बताता है - आपको गति अंतर को अधिक विश्वसनीय रूप से मापने के लिए पुनरावृत्तियों को बढ़ाने की आवश्यकता होगी :) – snemarch

1

आप मैं के साथ कुछ भी नहीं करते की कोशिश की तो अपने पाश शायद सिर्फ दूर अनुकूलित है।

+0

दरअसल, मैंने नीचे के मूल्य को मुद्रित किया (लेकिन यह कोड में नहीं दिखाया गया है)। – RemiX

+0

समय अनुकूलित होने के साथ संगत होते हैं, लेकिन ऑप्टिमाइज़ नहीं किए जाते हैं। मैं परीक्षण बार-बार देखना चाहता हूं (प्रक्रिया को पुनरारंभ किए बिना)। इस संदर्भ में एक मुद्दा धागे हो सकते हैं कि HotSpot एक अलग थ्रेड में चलता है, और अतिरिक्त थ्रेड कुछ समय के लिए unoptimised कोड चलाने समाप्त हो सकता है। –

+0

टी 2 (केवल तब 10000x10000) के बराबर एक और थ्रेड 107 एमएस (टी 1 और टी 2 से तेज़) में पूरा हो गया है, या ऐसा नहीं है कि आपका क्या मतलब है? – RemiX

2

मैं इस अंतर पर बिल्कुल आश्चर्यचकित नहीं हूं। आप अपने धागे बनाने के लिए जावा के समवर्ती ढांचे का उपयोग कर रहे हैं (हालांकि मुझे कोई गारंटी नहीं दिखाई देती है कि दो धागे भी बनाए जाते हैं क्योंकि पहले नौकरी शुरू होने से पहले पूरा हो सकता है।

शायद लॉकिंग और सिंक्रनाइज़ेशन के सभी प्रकार चल रहे हैं पर्दे के पीछे पर जो आप वास्तव में अपने साधारण परीक्षण के लिए जरूरत नहीं है। संक्षेप में मैं लगता है कि समस्या बहु सूत्रण की भूमि के ऊपर है

+0

मैंने इसे केवल दो थ्रेड के साथ परीक्षण किया और thread1.start() का उपयोग करके, एक ही परिणाम दिखाया। साथ ही, निष्पादक सेवा में एक रननेबल बहुत तेज़ी से काम करता है और आखिरकार, एक और मशीन इस कोड के साथ ठीक काम करती है। – RemiX

4

कुछ हद तक सरणी के आकार में वृद्धि। नहीं है, वास्तव में कोशिश करें।

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

java.util.concurrent में एक कक्षा है जिसमें अप्रयुक्त long फ़ील्ड का एक समूह है। उनका उद्देश्य उन वस्तुओं को अलग करना है जिन्हें अलग-अलग धागे द्वारा विभिन्न कैश लाइनों में अक्सर इस्तेमाल किया जा सकता है।

+0

मैं प्रत्येक थ्रेड के लिए एक अलग सरणी का उपयोग कर रहा हूं, इसलिए मुझे नहीं लगता कि उन्हें पहुंच के लिए लड़ना है ... या क्या मैंने गलत समझा? – RemiX

+4

@RemiX: वे ढेर पर आवंटित दोनों हैं, i2 i1 के ठीक बाद आवंटित किया गया है। एक ही कैशलाइन में समाप्त होने की उनकी एक बहुत अधिक संभावना है। – snemarch

+0

+1 - 2200 एमएस से 280 एमएस केवल सरणी के आकार को बढ़ाकर 10. दुर्भाग्यवश, अपने अन्य सुझावों का उपयोग करके प्रभाव अब और बढ़िया नहीं है। याद रखना अच्छा है, यद्यपि। – RemiX

1

क्या आपने अपने पीसी पर Runtime.getRuntime() के साथ उपलब्ध कोर की संख्या की जांच की है। उपलब्ध प्रोसेसर()?

+0

बस किया, और यह 2 प्रोसेसर कहते हैं। साथ ही, मैं उन्हें कार्य प्रबंधक में काम कर देख सकता हूं। – RemiX

0

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

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