2016-10-24 27 views
7

मेरे पास एक फ़ंक्शन foo है जो बहुत सारी मेमोरी का उपभोग करता है और जिसे मैं समानांतर में कई उदाहरण चला सकता हूं।मल्टीप्रोसेसिंग: केवल ** भौतिक कोर का उपयोग करें?

मान लीजिए मेरे पास 4 भौतिक कोर के साथ एक सीपीयू है, प्रत्येक में दो लॉजिकल कोर हैं।

मेरे सिस्टम में समानांतर में foo के 4 उदाहरणों को समायोजित करने के लिए पर्याप्त स्मृति है, लेकिन 8 नहीं। इसके अलावा, इनमें से 8 कोर में से 4 कोर लॉजिकल हैं, फिर भी मैं उम्मीद नहीं करता कि सभी 8 कोर ऊपर और उससे अधिक लाभ प्रदान करेंगे केवल 4 भौतिक लोगों का उपयोग करना।

तो मैं 4 भौतिक कोर केवल पर foo चलाने के लिए चाहता हूं। दूसरे शब्दों में, मैं यह सुनिश्चित करना चाहता हूं कि multiprocessing.Pool(4) (4 मेमोरी सीमाओं के कारण इस मशीन पर फ़ंक्शन के समवर्ती भाग की अधिकतम संख्या होने के कारण) चार भौतिक कोरों (और नहीं, उदाहरण के लिए, दो भौतिक कोरों और उनके दो तार्किक संतानों के एक कॉम्बो के लिए)।

पायथन में यह कैसे करें?

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

मैंने पहले एक कोड उदाहरण multiprocessing से इस्तेमाल किया है, लेकिन मैं पुस्तकालय नास्तिक हूँ, इसलिए भ्रम से बचने के, मुझे लगता है कि हटा दिया।

+0

@ GáborErdős लेकिन करता है कि पूल सभी * शारीरिक * कोर या सिर्फ पहले चार कोर? – user189035

+0

@ गैबरएर्डो: क्या आप निश्चित हैं? 'आयात psutils' 'psutil.cpu_count (तार्किक = झूठा) 'अंतर को जानना प्रतीत होता है। – user189035

+0

@ युगी: नहीं, मुझे नहीं लगता कि यह एक डुप्लिकेट है, हालांकि मेरा प्रश्न गलत तरीके से तैयार किया गया हो सकता है (उसमें 'सभी' भाग पर एक अनुचित जोर दिया गया था)। – user189035

उत्तर

2

मुझे एक समाधान मिला जिसमें पाइथन मॉड्यूल के स्रोत कोड को शामिल करने में शामिल नहीं है। यह here सुझाए गए दृष्टिकोण का उपयोग करता है।एक जाँच कर सकते हैं करने से है कि स्क्रिप्ट चलाने के बाद कि केवल शारीरिक कोर सक्रिय हैं:

lscpu 
बैश रिटर्न में

:

CPU(s):    8 
On-line CPU(s) list: 0,2,4,6 
Off-line CPU(s) list: 1,3,5,7 
Thread(s) per core: 1 

[एक python भीतर स्क्रिप्ट से ऊपर लिंक चला सकते हैं]। किसी भी मामले में, ऊपर स्क्रिप्ट चलाने, अजगर में इन आदेशों लिखने के बाद:

import multiprocessing 
multiprocessing.cpu_count() 

रिटर्न 4.

+1

साफ समाधान, +1 – Yugi

4

नोट: यह दृष्टिकोण विंडोज़ पर काम नहीं करता है और यह केवल लिनक्स पर परीक्षण किया जाता है।

multiprocessing.Process का उपयोग करना: जब Process() का उपयोग कर प्रत्येक प्रक्रिया के लिए एक भौतिक कोर नियत

काफी आसान है। आप taskset -p [mask] [pid] का उपयोग कर पाश कि दोहराता प्रत्येक कोर गर्त के लिए एक बना सकते हैं और नए कोर करने के लिए नई प्रक्रिया प्रदान करती है कर सकते हैं:

import multiprocessing 
import os 

def foo(): 
    return 

if __name__ == "__main__" : 
    for process_idx in range(multiprocessing.cpu_count()): 
     p = multiprocessing.Process(target=foo) 
     os.system("taskset -p -c %d %d" % (process_idx % multiprocessing.cpu_count(), os.getpid())) 
     p.start() 

मैं अपने कार्य केंद्र पर 32 कोर है इसलिए मैं यहाँ आंशिक परिणामों डाल देता हूँ:

pid 520811's current affinity list: 0-31 
pid 520811's new affinity list: 0 
pid 520811's current affinity list: 0 
pid 520811's new affinity list: 1 
pid 520811's current affinity list: 1 
pid 520811's new affinity list: 2 
pid 520811's current affinity list: 2 
pid 520811's new affinity list: 3 
pid 520811's current affinity list: 3 
pid 520811's new affinity list: 4 
pid 520811's current affinity list: 4 
pid 520811's new affinity list: 5 
... 

जैसा कि आप देखते हैं, यहां प्रत्येक प्रक्रिया के पिछले और नए संबंध हैं। पहला कोर सभी कोर (0-31) के लिए है और फिर कोर 0 को सौंपा गया है, दूसरी प्रक्रिया डिफ़ॉल्ट रूप से कोर 0 को सौंपा गया है और फिर इसका संबंध अगले कोर (1) में बदल दिया गया है, और बहुत आगे।

multiprocessing.Pool का उपयोग करना:

चेतावनी: इस तरीके pool.py मॉड्यूल में सुधार करने की जरूरत है के बाद से वहाँ कोई रास्ता नहीं है कि मैं इस बात का पता है कि तुम Pool() से पीआईडी ​​निकाल सकते हैं है। इसके अलावा इस परिवर्तन का परीक्षण python 2.7 और multiprocessing.__version__ = '0.70a1' पर किया गया है।

Pool.py में, उस रेखा को खोजें जहां _task_handler_start() विधि कहा जा रहा है। अगली पंक्ति में, आप प्रक्रिया पूल में एक "भौतिक" कोर के लिए प्रयोग कर दे सकते हैं (मैं import os यहाँ रखा ताकि पाठक यह आयात करने के लिए भूल जाते हैं नहीं है):

import os 
for worker in range(len(self._pool)): 
    p = self._pool[worker] 
    os.system("taskset -p -c %d %d" % (worker % cpu_count(), p.pid)) 

और आप कर रहे हैं किया हुआ।टेस्ट:

import multiprocessing 

def foo(i): 
    return 

if __name__ == "__main__" : 
    pool = multiprocessing.Pool(multiprocessing.cpu_count()) 
    pool.map(foo,'iterable here') 

परिणाम:

pid 524730's current affinity list: 0-31 
pid 524730's new affinity list: 0 
pid 524731's current affinity list: 0-31 
pid 524731's new affinity list: 1 
pid 524732's current affinity list: 0-31 
pid 524732's new affinity list: 2 
pid 524733's current affinity list: 0-31 
pid 524733's new affinity list: 3 
pid 524734's current affinity list: 0-31 
pid 524734's new affinity list: 4 
pid 524735's current affinity list: 0-31 
pid 524735's new affinity list: 5 
... 

ध्यान दें कि pool.py को यह संशोधन कोर को काम देना राउंड robinly। इसलिए यदि आप सीपीयू-कोर की तुलना में अधिक नौकरियां सौंपते हैं, तो आप उनमें से कई को एक ही कोर पर समाप्त कर देंगे।

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

क्या ओ पी की तलाश में है एक pool() कि विशिष्ट कोर पर पूल घूर करने में सक्षम है है। multiprocessing पर इस और अधिक बदलावों की आवश्यकता है (पहले उल्लिखित परिवर्तन पूर्ववत करें)।

चेतावनी:

कॉपी-पेस्ट करने के लिए कार्यशील परिभाषाएँ और फ़ंक्शन कॉल कोशिश मत करो। केवल उस भाग को पेस्ट करें जिसे self._worker_handler.start() के बाद जोड़ा जाना चाहिए (आप इसे नीचे देखेंगे)। ध्यान दें कि मेरी multiprocessing.__version__ मुझसे कहता संस्करण '0.70a1' है, लेकिन यह बात जब तक तुम सिर्फ तुम क्या जोड़ने की जरूरत है जोड़ने के रूप में नहीं करता है:

multiprocessing के pool.py:

__init__() करने के लिए एक cores_idx = None तर्क जोड़ने परिभाषा।

def __init__(self, processes=None, initializer=None, initargs=(), 
      maxtasksperchild=None,cores_idx=None) 

भी आप self._worker_handler.start() के बाद निम्नलिखित कोड जोड़ने चाहिए:: मेरे संस्करण में यह यह जोड़ने के बाद इस तरह दिखता है

if not cores_idx is None: 
    import os 
    for worker in range(len(self._pool)): 
     p = self._pool[worker] 
     os.system("taskset -p -c %d %d" % (cores_idx[worker % (len(cores_idx))], p.pid)) 

multiprocessing के __init__.py:

एक cores_idx=None तर्क जोड़े रिटर्न भाग में Pool() के साथ-साथ अन्य Pool() फ़ंक्शन कॉल की परिभाषा के लिए। मेरे संस्करण में ऐसा लगता है:

def Pool(processes=None, initializer=None, initargs=(), maxtasksperchild=None,cores_idx=None): 
    ''' 
    Returns a process pool object 
    ''' 
    from multiprocessing.pool import Pool 
    return Pool(processes, initializer, initargs, maxtasksperchild,cores_idx) 

और आप कर चुके हैं। निम्न उदाहरण कोर 0 पर 5 कर्मचारियों की एक पूल और 2 चलाता है केवल:

import multiprocessing 


def foo(i): 
    return 

if __name__ == "__main__": 
    pool = multiprocessing.Pool(processes=5,cores_idx=[0,2]) 
    pool.map(foo,'iterable here') 

परिणाम:

pid 705235's current affinity list: 0-31 
pid 705235's new affinity list: 0 
pid 705236's current affinity list: 0-31 
pid 705236's new affinity list: 2 
pid 705237's current affinity list: 0-31 
pid 705237's new affinity list: 0 
pid 705238's current affinity list: 0-31 
pid 705238's new affinity list: 2 
pid 705239's current affinity list: 0-31 
pid 705239's new affinity list: 0 

बेशक आप अभी भी cores_idx को हटाने के द्वारा के रूप में अच्छी multiprocessing.Poll() के सामान्य कार्यक्षमता हो सकता है तर्क।

+1

@ user189035 क्या आपको इसे लागू करने के लिए किसी विशिष्ट समस्या का सामना करना पड़ा? क्योंकि यह मेरे उत्तर के 'multiprocessing.Process' भाग का उपयोग करके लागू करने के लिए काफी ठीक लगता है। जब तक मुझे कुछ याद नहीं आ रहा है – Yugi

+0

@ user189035 शायद मुझे कुछ याद आ रहा है क्योंकि अब मुझे लगता है कि इसे दोनों के संयोजन की आवश्यकता हो सकती है। लेकिन अगर आपको परेशानी हो रही है तो मुझे बताएं और मैं इस पर काम करूंगा – Yugi

+0

मुझे आपकी आखिरी टिप्पणी नहीं है। मुझे आपके उत्तर के माध्यम से काम करने में भी कठिनाइयां हैं। क्या आप अपना जवाब यह दिखाने के लिए जोड़ सकते हैं कि कैसे कोई यह देख सकता है कि foo का एक उदाहरण वास्तव में भौतिक कोर पर चल रहा है या नहीं। – user189035

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