কিউ এবং পাইপের মধ্যে মৌলিক পার্থক্যগুলি কী পাইথনের মাল্টিপ্রসেসিং প্যাকেজে ?
কোন পরিস্থিতিতে একটির অপরটির থেকে একটি বেছে নেওয়া উচিত? কখন এটি ব্যবহার করা সুবিধাজনক Pipe()
? কখন এটি ব্যবহার করা সুবিধাজনক Queue()
?
কিউ এবং পাইপের মধ্যে মৌলিক পার্থক্যগুলি কী পাইথনের মাল্টিপ্রসেসিং প্যাকেজে ?
কোন পরিস্থিতিতে একটির অপরটির থেকে একটি বেছে নেওয়া উচিত? কখন এটি ব্যবহার করা সুবিধাজনক Pipe()
? কখন এটি ব্যবহার করা সুবিধাজনক Queue()
?
উত্তর:
এগুলি কখন ব্যবহার করবেন
যোগাযোগের জন্য যদি আপনার আরও দুটি পয়েন্টের প্রয়োজন হয় তবে a ব্যবহার করুন Queue()
।
আপনি পরম কর্মক্ষমতা প্রয়োজন হলে, একটি Pipe()
অনেক দ্রুত কারণ Queue()
উপরে নির্মিত হয় Pipe()
।
পারফরম্যান্স বেঞ্চমার্কিং
ধরে নেওয়া যাক আপনি দুটি প্রক্রিয়া চালিত করতে চান এবং যত তাড়াতাড়ি সম্ভব তাদের মধ্যে বার্তা প্রেরণ করতে চান। এটি একইরকম পরীক্ষাগুলির ব্যবহার Pipe()
এবং Queue()
... এর মধ্যে টেনে আনার রেসের সময়সীমার ফলাফল Thisউবুন্টু ১১.১০ এবং পাইথন ২.7.২ চলছে এমন একটি থিঙ্কপ্যাডটি on১ এর উপর।
এফওয়াইআই, আমি JoinableQueue()
বোনাস হিসাবে ফলাফল ছুঁড়েছি ; JoinableQueue()
যখন queue.task_done()
ডাকা হয় তখন কাজের জন্য অ্যাকাউন্টগুলি (এটি নির্দিষ্ট টাস্ক সম্পর্কে এমনকি জানে না, এটি কেবল সারিতে অসম্পূর্ণ কাজগুলি গণনা করে), যাতেqueue.join()
কাজটি শেষ হয়ে যায় knows
এই উত্তরের নীচে প্রতিটিটির জন্য কোড ...
mpenning@mpenning-T61:~$ python multi_pipe.py
Sending 10000 numbers to Pipe() took 0.0369849205017 seconds
Sending 100000 numbers to Pipe() took 0.328398942947 seconds
Sending 1000000 numbers to Pipe() took 3.17266988754 seconds
mpenning@mpenning-T61:~$ python multi_queue.py
Sending 10000 numbers to Queue() took 0.105256080627 seconds
Sending 100000 numbers to Queue() took 0.980564117432 seconds
Sending 1000000 numbers to Queue() took 10.1611330509 seconds
mpnening@mpenning-T61:~$ python multi_joinablequeue.py
Sending 10000 numbers to JoinableQueue() took 0.172781944275 seconds
Sending 100000 numbers to JoinableQueue() took 1.5714070797 seconds
Sending 1000000 numbers to JoinableQueue() took 15.8527247906 seconds
mpenning@mpenning-T61:~$
সংক্ষিপ্তসার Pipe()
এ এর থেকে প্রায় তিনগুণ দ্রুত Queue()
। এমনকি JoinableQueue()
যদি না আপনার সত্যিকারের সুবিধাগুলি না থাকে তবে তা সম্পর্কেও ভাববেন না ।
বোনাস উপাদান 2
মাল্টিপ্রসেসিং তথ্য প্রবাহে সূক্ষ্ম পরিবর্তনগুলি উপস্থাপন করে যা ডিবাগিংকে শক্ত করে তোলে যদি না আপনি কিছু শর্টকাট জানেন না। উদাহরণস্বরূপ, আপনার কাছে এমন একটি স্ক্রিপ্ট থাকতে পারে যা ডিকশনারিটির মাধ্যমে অনেক শর্তে ইনডেক্স করার সময় সূক্ষ্মভাবে কাজ করে, তবে নির্দিষ্ট ইনপুটগুলিতে খুব কমই ব্যর্থ হয়।
পুরো অজগর প্রক্রিয়াটি ক্র্যাশ হয়ে গেলে সাধারণত আমরা ব্যর্থতার সূত্র পাই; তবে মাল্টিপ্রসেসিং ফাংশন ক্র্যাশ হয়ে গেলে আপনি কনসোল-এ মুদ্রিত অযৌক্তিক ক্র্যাশ ট্র্যাসব্যাকগুলি পাবেন না। অজানা মাল্টিপ্রসেসিং ক্র্যাশগুলি সন্ধান করা প্রক্রিয়াটি কীভাবে বিধ্বস্ত হয়েছিল তার কোনও ইঙ্গিত ছাড়াই শক্ত।
মাল্টিপ্রসেসিং ক্র্যাশ ইনফরমিটিটনটি ট্র্যাক করার সহজ উপায়টি আমি খুঁজে পেয়েছি try
/ except
এবং ব্যবহারে সম্পূর্ণ মাল্টিপ্রসেসিং ফাংশনটি মোড়ানো traceback.print_exc()
:
import traceback
def run(self, args):
try:
# Insert stuff to be multiprocessed here
return args[0]['that']
except:
print "FATAL: reader({0}) exited while multiprocessing".format(args)
traceback.print_exc()
এখন, আপনি যখন ক্র্যাশ পান তখন আপনি এমন কিছু দেখতে পান:
FATAL: reader([{'crash': 'this'}]) exited while multiprocessing
Traceback (most recent call last):
File "foo.py", line 19, in __init__
self.run(args)
File "foo.py", line 46, in run
KeyError: 'that'
সোর্স কোড:
"""
multi_pipe.py
"""
from multiprocessing import Process, Pipe
import time
def reader_proc(pipe):
## Read from the pipe; this will be spawned as a separate Process
p_output, p_input = pipe
p_input.close() # We are only reading
while True:
msg = p_output.recv() # Read from the output pipe and do nothing
if msg=='DONE':
break
def writer(count, p_input):
for ii in xrange(0, count):
p_input.send(ii) # Write 'count' numbers into the input pipe
p_input.send('DONE')
if __name__=='__main__':
for count in [10**4, 10**5, 10**6]:
# Pipes are unidirectional with two endpoints: p_input ------> p_output
p_output, p_input = Pipe() # writer() writes to p_input from _this_ process
reader_p = Process(target=reader_proc, args=((p_output, p_input),))
reader_p.daemon = True
reader_p.start() # Launch the reader process
p_output.close() # We no longer need this part of the Pipe()
_start = time.time()
writer(count, p_input) # Send a lot of stuff to reader_proc()
p_input.close()
reader_p.join()
print("Sending {0} numbers to Pipe() took {1} seconds".format(count,
(time.time() - _start)))
"""
multi_queue.py
"""
from multiprocessing import Process, Queue
import time
import sys
def reader_proc(queue):
## Read from the queue; this will be spawned as a separate Process
while True:
msg = queue.get() # Read from the queue and do nothing
if (msg == 'DONE'):
break
def writer(count, queue):
## Write to the queue
for ii in range(0, count):
queue.put(ii) # Write 'count' numbers into the queue
queue.put('DONE')
if __name__=='__main__':
pqueue = Queue() # writer() writes to pqueue from _this_ process
for count in [10**4, 10**5, 10**6]:
### reader_proc() reads from pqueue as a separate process
reader_p = Process(target=reader_proc, args=((pqueue),))
reader_p.daemon = True
reader_p.start() # Launch reader_proc() as a separate python process
_start = time.time()
writer(count, pqueue) # Send a lot of stuff to reader()
reader_p.join() # Wait for the reader to finish
print("Sending {0} numbers to Queue() took {1} seconds".format(count,
(time.time() - _start)))
"""
multi_joinablequeue.py
"""
from multiprocessing import Process, JoinableQueue
import time
def reader_proc(queue):
## Read from the queue; this will be spawned as a separate Process
while True:
msg = queue.get() # Read from the queue and do nothing
queue.task_done()
def writer(count, queue):
for ii in xrange(0, count):
queue.put(ii) # Write 'count' numbers into the queue
if __name__=='__main__':
for count in [10**4, 10**5, 10**6]:
jqueue = JoinableQueue() # writer() writes to jqueue from _this_ process
# reader_proc() reads from jqueue as a different process...
reader_p = Process(target=reader_proc, args=((jqueue),))
reader_p.daemon = True
reader_p.start() # Launch the reader process
_start = time.time()
writer(count, jqueue) # Send a lot of stuff to reader_proc() (in different process)
jqueue.join() # Wait for the reader to finish
print("Sending {0} numbers to JoinableQueue() took {1} seconds".format(count,
(time.time() - _start)))
Queue()
এটির লক্ষণীয় যেগুলির একটি অতিরিক্ত বৈশিষ্ট্য হ'ল ফিডার থ্রেড। এই বিভাগটি নোট করে "যখন কোনও প্রক্রিয়া প্রথমে কাতারে কোনও আইটেম রাখে তখন একটি ফিডার থ্রেড শুরু হয় যা বাফার থেকে পাইপগুলিতে বস্তু স্থানান্তর করে।" ব্লকিংয়ের জন্য Queue()
কোনও কল ছাড়াই অসীম সংখ্যক (বা সর্বোচ্চ) আইটেম intoোকানো যেতে পারে queue.put()
। Queue()
আপনার প্রোগ্রামটি প্রক্রিয়া করার জন্য প্রস্তুত না হওয়া পর্যন্ত এটি আপনাকে একটিতে একাধিক আইটেম সংরক্ষণ করতে দেয় ।
Pipe()
অন্যদিকে, আইটেমগুলির জন্য সীমাবদ্ধ পরিমাণ সঞ্চয়স্থান রয়েছে যা একটি সংযোগে প্রেরণ করা হয়েছে, তবে অন্য সংযোগ থেকে প্রাপ্ত হয়নি। এই স্টোরেজটি ব্যবহার করার পরে, connection.send()
পুরো আইটেমটি লেখার জায়গা না পাওয়া পর্যন্ত কলগুলি অবরুদ্ধ হবে। অন্য থ্রেডটি পাইপ থেকে পড়া না হওয়া পর্যন্ত এটি থ্রেডটি লেখার কাজটি স্টল করবে। Connection
বস্তুগুলি আপনাকে অন্তর্নিহিত ফাইল বর্ণনাকারীর অ্যাক্সেস দেয়। * নিক্স সিস্টেমে আপনি connection.send()
কলটি ব্লক করা থেকে ব্লক করা আটকাতে পারবেনos.set_blocking()
ফাংশনটি । যাইহোক, আপনি যদি পাইপ এর ফাইলের মধ্যে উপযুক্ত না এমন একক আইটেম প্রেরণের চেষ্টা করেন তবে এটি সমস্যার সৃষ্টি করবে। লিনাক্সের সাম্প্রতিক সংস্করণগুলি আপনাকে একটি ফাইলের আকার বাড়াতে দেয়, তবে অনুমতি দেওয়া সর্বাধিক আকার সিস্টেম কনফিগারেশনের উপর নির্ভর করে। সুতরাং আপনার কখনই Pipe()
বাফার ডেটার উপর নির্ভর করা উচিত নয় । কলconnection.send
অন্য কোথাও পাইপ থেকে ডেটা পড়া না পাওয়া অবরুদ্ধ হতে পারে।
উপসংহারে, যখন আপনাকে ডেটা বাফার করা দরকার তখন পাইপের চেয়ে ক্যু ভাল পছন্দ। এমনকি যখন আপনাকে কেবল দুটি পয়েন্টের মধ্যে যোগাযোগ করতে হবে।