2011-09-01 7 views
22

यह पैटर्न बहुत कुछ आता है लेकिन मुझे सीधा जवाब नहीं मिल रहा है।पाइथन के समय का व्यवहार। नींद (0) लिनक्स के तहत - क्या यह संदर्भ स्विच का कारण बनता है?

एक गैर महत्वपूर्ण, संयुक्त राष्ट्र के अनुकूल कार्यक्रम अन्य प्रौद्योगिकियों और प्लेटफार्मों का उपयोग

while(True): 
    # do some work 

कर सकते हैं, अगर आप इस कार्यक्रम गर्म चलाने (जितना संभव हो उतना CPU चक्र का उपयोग), लेकिन विनम्र होना करने के लिए अनुमति देना चाहते हैं

while(True): 
    #do some work 
    time.sleep(0) 

के बारे में मैं बाद दृष्टिकोण करना होगा कि क्या परस्पर विरोधी जानकारी पढ़ा है कि मैं क्या अजगर पर आशा करना चाहते हैं, पर चल रहा है: - अन्य कार्यक्रमों जो गर्म चल रहे हैं प्रभावी ढंग से मुझे धीमा करने के लिए आप अक्सर लिखते हैं अनुमति देते हैं, एक लिनक्स बॉक्स। क्या यह संदर्भ स्विच का कारण बनता है, जिसके परिणामस्वरूप ऊपर वर्णित व्यवहार होता है?

संपादित करें: इसके लायक होने के लिए, हमने ऐप्पल ओएसएक्स में थोड़ा प्रयोग करने की कोशिश की (लिनक्स बॉक्स आसान नहीं था)। इस बॉक्स को 4 कोर प्लस हाइपरथ्रेडिंग हम साथ 8 प्रोग्राम काता है तो बस एक

while(True): 
    i += 1 

जैसी उम्मीद थी, गतिविधि मॉनिटर लेने वाली 95% से अधिक सीपीयू (जाहिरा तौर पर 4 कोर और हाइपरथ्रेडिंग आपको मिल के साथ के रूप में 8 प्रक्रियाओं में से प्रत्येक से पता चलता 800% कुल)। फिर हम नौवें ऐसे कार्यक्रम को बढ़ाते हैं। अब सभी 9 85% के आसपास चलाते हैं। अब नौवें पुरुष को मारने और

while(True): 
    i += 1 
    time.sleep(0) 

मुझे उम्मीद थी कि इस प्रक्रिया को 0% के करीब का प्रयोग करेंगे और अन्य 8 95% चल पाएंगे के साथ एक कार्यक्रम को स्पिन। लेकिन इसके बजाय, सभी नौ 85% के आसपास दौड़ते हैं। तो ऐप्पल ओएसएक्स पर, नींद (0) का कोई असर नहीं पड़ता है।

उत्तर

19

मैं इस बारे में कभी नहीं सोचा था, इसलिए मैं इस पटकथा लिखी: बस एक परीक्षण के रूप

import time 

while True: 
    print "loop" 
    time.sleep(0.5) 

strace -o isacontextswitch.strace -s512 python test.py के साथ इस रनिंग आप पाश पर इस उत्पादन देता है:

write(1, "loop\n", 5)     = 5 
select(0, NULL, NULL, NULL, {0, 500000}) = 0 (Timeout) 
write(1, "loop\n", 5)     = 5 
select(0, NULL, NULL, NULL, {0, 500000}) = 0 (Timeout) 
write(1, "loop\n", 5)     = 5 
select(0, NULL, NULL, NULL, {0, 500000}) = 0 (Timeout) 
write(1, "loop\n", 5)     = 5 
select(0, NULL, NULL, NULL, {0, 500000}) = 0 (Timeout) 
write(1, "loop\n", 5) 

select(), एक सिस्टम कॉल है, तो हाँ, तो स्विच करने संदर्भ कर रहे हैं (ठीक तकनीकी रूप से एक संदर्भ स्विच वास्तव में आवश्यक है जब आप कर्नेल स्थान में कोई बदलाव नहीं है, लेकिन यदि आपके पास अन्य प्रक्रियाएं चल रही हैं, तो आप यहां क्या कह रहे हैं कि जब तक आपके पास फ़ाइल फ़ाइल डिस्क्रिप्टर पर पढ़ने के लिए डेटा तैयार नहीं होता है, तब तक अन्य प्रक्रियाएं कर्नेल में तब तक चल सकती हैं)। दिलचस्प बात यह है कि देरी stdin पर चयन करने में है। यह पाइथन को ctrl+c इनपुट जैसी घटनाओं पर आपके इनपुट को बाधित करने की अनुमति देता है, चाहे वे चाहते हैं कि कोड को समय-समय पर प्रतीक्षा न करें - जो मुझे लगता है कि काफी साफ है।

मुझे ध्यान रखना चाहिए कि यह time.sleep(0) पर लागू होता है सिवाय इसके कि {0,0} में पारित समय पैरामीटर लागू होता है। और वह स्पिन लॉकिंग किसी भी चीज़ के लिए वास्तव में आदर्श नहीं है लेकिन बहुत कम देरी - multiprocessing और threads ईवेंट ऑब्जेक्ट्स पर प्रतीक्षा करने की क्षमता प्रदान करती है।

संपादित करें: तो मुझे यह देखने का एक नजर आया कि वास्तव में लिनक्स क्या करता है। समाप्ति समय प्रदान की जाती है अगर,

if (end_time && !end_time->tv_sec && !end_time->tv_nsec) { 
    wait = NULL; 
timed_out = 1; 
} 

if (end_time && !timed_out) 
    slack = select_estimate_accuracy(end_time); 

दूसरे शब्दों में और दोनों मानकों शून्य हैं (0 = 1 और सी में सही का आकलन!) तो इंतजार सेट है: do_select (fs\select.c) में कार्यान्वयन इस जांच करता है नल के लिए और चयन का समय समाप्त माना जाता है।हालांकि, इसका मतलब यह नहीं है कि फ़ंक्शन आपको वापस लौटाता है; यह आपके पास मौजूद सभी फाइल डिस्क्रिप्टरों पर लूप करता है और cond_resched पर कॉल करता है, जिससे संभावित रूप से दूसरी प्रक्रिया को चलाने की इजाजत मिलती है। दूसरे शब्दों में, क्या होता है पूरी तरह से शेड्यूलर तक; यदि आपकी प्रक्रिया अन्य प्रक्रियाओं की तुलना में सीपीयू समय को घुमा रही है, संभावना है कि एक संदर्भ स्विच होगा। यदि नहीं, तो आप जिस कार्य में हैं (कर्नेल do_select फ़ंक्शन) तब तक पूरा हो सकता है जब तक कि यह पूरा न हो जाए।

हालांकि, मैं पुन: पुनरावृत्त करूंगा, हालांकि, अन्य प्रक्रियाओं के लिए बेहतर होने का सबसे अच्छा तरीका आमतौर पर स्पिन लॉक की तुलना में अन्य तंत्र का उपयोग करना शामिल है।

+0

यह सहायक है। मैं यह सुनिश्चित करना चाहता हूं कि मैं आपके निष्कर्ष को समझूं। Time.sleep (0) एक संदर्भ स्विच कर रहा है - है ना? लेकिन मुझे लगता है कि शायद मेरी धारणा गलत थी कि इससे मेरा कार्यक्रम अन्य प्रक्रियाओं के लिए अधिक अनुकूल हो जाएगा? –

+0

@ मैथ्यू दो चीजें हैं - एक संदर्भ स्विच है (एक अन्य प्रक्रिया में स्विच करने के रूप में स्विचिंग कार्य) और कर्नेल मोड में स्विचिंग - बस कर्नेल मोड में स्विच करने का मतलब यह नहीं है कि आप एक और प्रक्रिया सीपीयू टाइम भी देंगे। शायद देरी जोड़ना (अगर कुछ और चल रहा है) होगा। यह (नींद (0)) निश्चित रूप से आपको कर्नेल मोड में ले जाएगा; यह प्रश्न में कर्नेल पर निर्भर करता है कि क्या कोई देरी नहीं मांगती है, फिर से आपके प्रोग्राम को फिर से जगाता है, या यदि यह अन्य प्रक्रियाओं की तलाश करता है जो फाइल डिस्क्रिप्टर पर समय सीमा समाप्त होने के साथ सीपीयू समय की प्रतीक्षा कर रहे हैं। –

+0

वास्तव में चयन मैं stdin पर नहीं है। चुनने के लिए पहला तर्क() तीन सेटों में दायरस्क्रिप्टर्स की संख्या है ... इस मामले में 0. यह नैनोसेकंद आधारित शताब्दियों को लागू करने के लिए एक चाल है। –

1

time.sleep (0) कोई प्रभाव नहीं प्रतीत होता है, ऐसा लगता है कि यह छोड़ा गया है। यह देरी कम CPU उपयोग की एक छोटी राशि देते हुए 100% से 0% करने के लिए:

import time 
while True: 
    time.sleep(0.0001); 

नोट: सही/गलत अजगर में बड़े अक्षरों में।

+1

यह बिल्कुल प्रभाव पड़ता है। यह cpython में भी परीक्षण इकाई है: https://github.com/python/cpython/blob/6db7033192cd537ca987a65971acb01206c3ba82/Lib/test/test_import/__init__.py#L432। 'time.sleep (0) 'कॉलिंग थ्रेड के संदर्भ को छोड़ देता है, जो सवाल पूछ रहा है। – erikreed

8

मुझे लगता है कि आपके पास पहले से ही @Ninefingers का जवाब है, लेकिन इस जवाब में हम पाइथन स्रोत कोड में गोता लगाने की कोशिश करेंगे।

पहले पायथन time मॉड्यूल सी में लागू किया गया है और time.sleep फ़ंक्शन कार्यान्वयन को देखने के लिए आप Modules/timemodule.c पर एक नज़र डाल सकते हैं। जैसा कि आप देख सकते हैं (और सभी प्लेटफ़ॉर्म विशिष्ट विवरणों के बिना) यह फ़ंक्शन floatsleep फ़ंक्शन पर कॉल का प्रतिनिधि होगा।

अब floatsleep अलग मंच में काम करने के लिए बनाया गया है, लेकिन अभी भी व्यवहार जब भी यह संभव है समान होने के लिए डिजाइन किया गया था, लेकिन जैसा कि हम रुचि रखते हैं केवल यूनिक्स की तरह मंच में की जाँच करें that part only करेगा हम:

... 
Py_BEGIN_ALLOW_THREADS 
sleep((int)secs); 
Py_END_ALLOW_THREADS 

के रूप में आप देख सकते हैं floatsleepC नींद और sleep man page से बुला रहा है: ई से निलंबित किया जाना है

नींद() फ़ंक्शन बुला धागे का कारण होगा जब तक या तो वास्तविक समय सेकंड की संख्या निर्दिष्ट तर्क सेकंड से xecution गुजरे या ...

लेकिन एक मिनट हम जीआईएल बारे में भूल गया नहीं था इंतजार है?

खैर यह वह जगह है जहाँ Py_BEGIN_ALLOW_THREADS और Py_END_ALLOW_THREADS मैक्रो कार्रवाई (जाँच Include/ceval.h अगर आप इस दो मैक्रो की परिभाषा के बारे में रुचि रखते हैं), सी कोड ऊपर को यह दो मैक्रो का उपयोग कर अनुवाद किया जा सकता में आया:

Save the thread state in a local variable. 
Release the global interpreter lock. 
... Do some blocking I/O operation ... (call sleep in our case) 
Reacquire the global interpreter lock. 
Restore the thread state from the local variable. 

the c-api doc में इन दो मैक्रो के बारे में अधिक जानकारी मिल सकती है।

आशा है कि यह सहायक होगा।

7

आप मूल रूप से ओएस सीपीयू अनुसूचक की नौकरी को हड़पने की कोशिश कर रहे हैं। शेड्यूलर को सूचित करने के लिए os.nice(100) पर कॉल करने के लिए यह संभवतः बेहतर होगा कि आप बहुत कम प्राथमिकता रखते हैं ताकि यह ठीक से अपना काम कर सके।

+0

हाँ, आप सही हैं। –

+0

हम सिर्फ यह पता लगाने की कोशिश कर रहे थे कि यह कैसे काम करता है ... –

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