विंडोज पर मल्टीथ्रेडेड जावा एप्लिकेशन का बहुत कम सीपीयू उपयोग


18

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

मुसीबत

मैं यूनिक्स पर और विंडोज सिस्टम पर 44 भौतिक कोर तक और 256g तक मेमोरी के साथ एप्लिकेशन चला सकता हूं, लेकिन विंडोज पर गणना समय बड़ी समस्याओं के लिए लिनक्स पर अधिक से अधिक परिमाण का एक क्रम है। विंडोज को न केवल बहुत अधिक मेमोरी की आवश्यकता होती है, लेकिन समय के साथ सीपीयू का उपयोग कुछ घंटों के बाद शुरुआत में 25% से गिरकर 5% हो जाता है। यहाँ विंडोज में टास्क मैनेजर का स्क्रीनशॉट दिया गया है:

कार्य प्रबंधक CPU उपयोग

टिप्पणियों

  • समग्र समस्या के बड़े उदाहरणों के लिए समाधान समय घंटों से लेकर दिनों तक और 32g तक मेमोरी (यूनिक्स पर) तक खपत होती है। एक subproblem के लिए समाधान समय एमएस रेंज में हैं।
  • मैं इस समस्या का सामना छोटी समस्याओं पर नहीं करता हूं जिन्हें हल करने में केवल कुछ मिनट लगते हैं।
  • लिनक्स दोनों सॉकेट्स को आउट-ऑफ-द-बॉक्स का उपयोग करता है, जबकि विंडोज को मुझे BIOS में स्पष्ट रूप से मेमोरी इंटरलेक्टिंग को सक्रिय करने की आवश्यकता होती है ताकि एप्लिकेशन दोनों कोर का उपयोग करे। चाहे मैं ऐसा न करूं लेकिन समय के साथ समग्र सीपीयू उपयोग के बिगड़ने पर कोई प्रभाव नहीं पड़ता है।
  • जब मैं VisualVM में थ्रेड्स को देखता हूं तो सभी पूल थ्रेड चल रहे हैं, कोई भी प्रतीक्षा या अन्य पर नहीं है।
  • VisualVM के अनुसार, 90% CPU समय एक मूल फ़ंक्शन कॉल पर खर्च होता है (एक छोटे रैखिक कार्यक्रम को हल करना)
  • कचरा संग्रहण कोई समस्या नहीं है क्योंकि एप्लिकेशन बहुत सारी वस्तुओं का निर्माण और संदर्भ नहीं देता है। इसके अलावा, अधिकांश मेमोरी को ऑफ-हीप आवंटित किया गया लगता है। 4 जी के ढेर लिनक्स पर पर्याप्त हैं और सबसे बड़े उदाहरण के लिए विंडोज पर 8 जी हैं।

मैंने क्या कोशिश की है

  • सभी प्रकार के जेवीएम आर्ग्स, उच्च एक्सएमएस, उच्च मेटास्टेस, यूएनएनयूएमएए फ्लैग, अन्य जीसी।
  • विभिन्न जेवीएम (हॉटस्पॉट 8, 9, 10, 11)।
  • विभिन्न रैखिक प्रोग्रामिंग सॉल्वर (CLP, Xpress, Cplex, Gurobi) के विभिन्न देशी पुस्तकालय।

प्रशन

  • एक बड़े बहु-थ्रेडेड जावा एप्लिकेशन के लिनक्स और विंडोज के बीच प्रदर्शन अंतर को बढ़ाता है जो देशी कॉल का भारी उपयोग करता है?
  • क्या ऐसा कुछ भी है जिसे मैं कार्यान्वयन में बदल सकता हूं जो कि विंडोज की मदद करेगा, उदाहरण के लिए, क्या मुझे एक एक्सेलसॉर सेवा का उपयोग करने से बचना चाहिए जो हजारों कॉलबल्स प्राप्त करता है और इसके बजाय क्या करता है?

क्या आपने ForkJoinPoolइसके बजाय कोशिश की है ExecutorService? यदि आपकी समस्या CPU बाध्य है तो 25% CPU उपयोग वास्तव में कम है।
करोल दोबेकई

1
आपकी समस्या कुछ इस तरह की है कि CPU को 100% पर धकेलना चाहिए और फिर भी आप 25% पर हैं। कुछ समस्याओं के ForkJoinPoolलिए मैनुअल शेड्यूलिंग से अधिक कुशल है।
करोल डोबबेकी

2
हॉटस्पॉट संस्करणों के माध्यम से साइकिल चलाना, क्या आपने सुनिश्चित किया है कि आप "सर्वर" का उपयोग कर रहे हैं और "क्लाइंट" संस्करण का नहीं? लिनक्स पर आपका CPU उपयोग क्या है? साथ ही, कई दिनों का विंडोज अपटाइम प्रभावशाली है! आपका रहस्य क्या है? : पी
इरिकसन

3
हो सकता है कि उपयोग करने का प्रयास Xperf एक उत्पन्न करने के लिए FlameGraph । यह आपको कुछ जानकारी दे सकता है कि सीपीयू क्या कर रहा है (उम्मीद है कि उपयोगकर्ता और कर्नेल मोड दोनों), लेकिन मैंने इसे विंडोज पर कभी नहीं किया।
करोल डोबबेकी

1
@ निल्स, रन (यूनिक्स / विन) दोनों ही मूल लाइब्रेरी को कॉल करने के लिए एक ही इंटरफ़ेस का उपयोग करता है? मैं पूछता हूं, क्योंकि यह अलग दिखता है। जैसे: win jna, linux jni का उपयोग करता है।
एसआर

जवाबों:


2

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


यह बहुत दिलचस्प लग रहा है! मैं दो कारणों से इस (अभी तक) जाने में थोड़ा संकोच कर रहा हूं: 1) सॉकेट के माध्यम से वस्तुओं को अनुक्रमित करने और भेजने के लिए एक प्रदर्शन ओवरहेड होगा; 2) अगर मैं सब कुछ क्रमबद्ध करना चाहता हूं, तो इसमें सभी निर्भरताएं शामिल हैं जो किसी कार्य में जुड़ी हुई हैं - कोड को फिर से लिखना थोड़ा काम होगा - फिर भी, उपयोगी लिंक (ओं) के लिए धन्यवाद।
Nils

मैं आपकी चिंताओं को पूरी तरह से साझा करता हूं और कोड को फिर से डिजाइन करना कुछ प्रयास होंगे। ग्राफ़ को ट्रेस करते समय आपको थ्रेड्स की संख्या के लिए एक थ्रेशोल्ड प्रस्तुत करना होगा जब यह एक नई उप प्रक्रिया में काम को विभाजित करने का समय हो। 2 पता करने के लिए) जावा मेमोरी-मैप्ड फ़ाइल (java.nio.MappedByteBuffer) पर एक नज़र डालें, इससे आप प्रभावी रूप से प्रक्रियाओं के बीच डेटा साझा कर सकते हैं, उदाहरण के लिए आपका ग्राफ डेटा। गोडस्पीड :)
गेरी

0

लगता है जैसे विंडोज़ कुछ समय के लिए पेजफ़ाइल को कुछ मेमोरी कैशिंग कर रहा है, कुछ समय के लिए अछूता रहने के बाद, और यही कारण है कि सीपीयू डिस्क की गति से अड़चन है

आप इसे प्रोसेस एक्सप्लोरर के साथ सत्यापित कर सकते हैं और जांच सकते हैं कि मेमोरी कितनी कैश है


आपको लगता है? पर्याप्त मुक्त स्मृति है। विंडोज की अदला-बदली क्यों शुरू होगी? वैसे भी, धन्यवाद।
निल्स

कम से कम मेरे लैपटॉप खिड़कियों पर कभी-कभी कम से कम अनुप्रयोगों की अदला-बदली होती है, यहां तक ​​कि पर्याप्त स्मृति के साथ
यहूदी

0

मुझे लगता है कि यह प्रदर्शन अंतर इस कारण से है कि ओएस थ्रेड्स का प्रबंधन कैसे करता है। JVM सभी OS अंतर छिपाते हैं। कई साइटें हैं जहां आप इसके बारे में पढ़ सकते हैं , उदाहरण के लिए, इस तरह । लेकिन इसका मतलब यह नहीं है कि अंतर गायब हो जाता है।

मुझे लगता है कि आप जावा 8+ जेवीएम पर चल रहे हैं। इस तथ्य के कारण, मैं आपको स्ट्रीम और कार्यात्मक प्रोग्रामिंग सुविधाओं का उपयोग करने का प्रयास करने का सुझाव देता हूं। कार्यात्मक प्रोग्रामिंग बहुत उपयोगी है जब आपके पास कई छोटी स्वतंत्र समस्याएं हैं और आप आसानी से अनुक्रमिक से समानांतर निष्पादन पर स्विच करना चाहते हैं। अच्छी खबर यह है कि आपको यह निर्धारित करने के लिए कोई नीति निर्धारित करने की आवश्यकता नहीं है कि आपको कितने धागे का प्रबंधन करना है (जैसे कि एक्सेप्टर सर्विस)। सिर्फ उदाहरण के लिए ( यहाँ से लिया गया ):

package com.mkyong.java8;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.IntStream;
import java.util.stream.Stream;

public class ParallelExample4 {

    public static void main(String[] args) {

        long count = Stream.iterate(0, n -> n + 1)
                .limit(1_000_000)
                //.parallel()   with this 23s, without this 1m 10s
                .filter(ParallelExample4::isPrime)
                .peek(x -> System.out.format("%s\t", x))
                .count();

        System.out.println("\nTotal: " + count);

    }

    public static boolean isPrime(int number) {
        if (number <= 1) return false;
        return !IntStream.rangeClosed(2, number / 2).anyMatch(i -> number % i == 0);
    }

}

परिणाम:

सामान्य धाराओं के लिए, 1 मिनट 10 सेकंड लगते हैं। समानांतर धाराओं के लिए, 23 सेकंड लगते हैं। PS का परीक्षण i7-7700, 16G RAM, WIndows 10 के साथ किया गया

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


मैं सॉफ्टवेयर के अन्य हिस्सों में धाराओं का उपयोग करता हूं, लेकिन इस मामले में एक ग्राफ को पार करते समय कार्य बनाए जाते हैं। मुझे यह नहीं पता होगा कि धाराओं का उपयोग करके इसे कैसे लपेटना है।
निल्स

क्या आप ग्राफ को आगे बढ़ा सकते हैं, एक सूची बना सकते हैं और फिर धाराओं का उपयोग कर सकते हैं?
xcesco

एक ForkJoinPool के लिए समानांतर धाराएँ केवल वाक्यगत चीनी हैं। मैंने कोशिश की है (ऊपर @KarolDowbecki टिप्पणी देखें)।
निल्स

0

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

जब आप CPU उपयोग के 25% कह रहे हैं, तो क्या आपका मतलब केवल कुछ कोर उसी समय काम करने में व्यस्त हैं? (यह हो सकता है कि सभी कोर समय-समय पर काम करते हैं, लेकिन एक साथ नहीं।) क्या आप जांच करेंगे कि सिस्टम में वास्तव में कितने धागे (या प्रक्रियाएं) बनाए गए हैं? क्या संख्या हमेशा कोर की संख्या से बड़ी होती है?

यदि पर्याप्त थ्रेड्स हैं, तो क्या उनमें से कई बेकार हैं जो किसी चीज की प्रतीक्षा कर रहे हैं? यदि सही है, तो आप यह देखने के लिए कि वे किस चीज का इंतजार कर रहे हैं (या डिबगर संलग्न कर सकते हैं) को बाधित करने का प्रयास कर सकते हैं।


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

@ निल्स मुझे संदेह है कि आपके पास वास्तव में सभी धागे एक ही समय में व्यस्त नहीं हैं , लेकिन वास्तव में उनमें से केवल 9 - 10 हैं। वे सभी कोर में यादृच्छिक रूप से अनुसूचित हैं, इसलिए आपके पास औसतन 9/44 = 20% उपयोग है। क्या आप अंतर देखने के लिए ExecutorService के बजाय सीधे जावा थ्रेड्स का उपयोग कर सकते हैं? 44 थ्रेड्स बनाना मुश्किल नहीं है, और प्रत्येक में टास्क पूल / कतार से रननेबल / कॉलेबल को पकड़ा जाता है। (हालांकि विजुअल वीएम दिखाता है कि सभी जावा थ्रेड्स व्यस्त हैं, वास्तविकता यह हो सकती है कि 44 धागे जल्दी से निर्धारित किए जाते हैं ताकि सभी को विजुअलवीएम के नमूने की अवधि में चलने का मौका मिले।)
जिओ-फेंग ली

यह एक विचार और कुछ है जो मैंने वास्तव में किसी बिंदु पर किया है। मेरे कार्यान्वयन में, मैंने यह भी सुनिश्चित किया कि देशी पहुंच प्रत्येक थ्रेड के लिए स्थानीय हो, लेकिन इससे कोई फर्क नहीं पड़ा।
निल्स
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.