বৃহত্তর পান্ডাস ডাটাফ্রেমগুলি অতিক্রম করার জন্য সারি ব্যবহার করে মাল্টিপ্রসেসিংয়ের একটি উপায় সেট আপ করার চেষ্টা করার সময় আমার স্ট্যাক ওভারফ্লো এবং ওয়েব জুড়ে একাধিক উত্তর ছিল। আমার কাছে মনে হয়েছিল যে প্রতিটি উত্তর একই ধরণের সমাধানগুলিকে পুনরায় পুনরায় পুনরায় পুনরায় পুনরায় পুনরায় পুনরায় পুনরায় পুনরুক্ত করে চলেছে এবং এ জাতীয় গণনা স্থাপনের সময় অবশ্যই কোনও সমস্যা অবশ্যই আসবে। সমস্যাটি হ'ল একই সময়ে খেলতে প্রচুর জিনিস রয়েছে। কার্য সংখ্যা, কর্মীর সংখ্যা, প্রতিটি কার্যের সময়কাল এবং কার্য সম্পাদনের সময় সম্ভাব্য ব্যতিক্রম। এগুলি সমস্তই সিঙ্ক্রোনাইজেশনকে জটিল করে তোলে এবং বেশিরভাগ উত্তরগুলি কীভাবে আপনি এটি সম্পর্কে যেতে পারেন তা সম্বোধন করে না। তাই কয়েক ঘন্টার জন্য ঘোরাফেরা করার পরে এটি আমার গ্রহণ, আশা করি বেশিরভাগ লোকেরা এটির দরকারী হিসাবে খুঁজে পেতে পারে gener
কোনও কোডিং উদাহরণের আগে কিছু চিন্তা। যেহেতু queue.Empty
বা queue.qsize()
বা অন্য কোনও অনুরূপ পদ্ধতি প্রবাহ নিয়ন্ত্রণের জন্য অবিশ্বাস্য, এর মতো যে কোনও কোড
while True:
try:
task = pending_queue.get_nowait()
except queue.Empty:
break
বোগাস এটি মিলিসেকেন্ড পরে অন্য কোনও কাজ সারিতে সরে গেলেও শ্রমিককে হত্যা করবে। শ্রমিকটি পুনরুদ্ধার করতে পারবেন না এবং কিছুক্ষণ পরে সমস্ত শ্রমিক অদৃশ্য হয়ে যাবে কারণ তারা এলোমেলোভাবে ক্রমটি মুহূর্তের জন্য খালি পেয়েছে। শেষ ফলাফলটি হ'ল মূল মাল্টিপ্রসেসিং ফাংশন (প্রসেসগুলিতে জয়েন () সহ একটি) সমস্ত কাজ সমাপ্ত না করে ফিরে আসবে। ভাল লাগল সৌভাগ্যক্রমে ডিবাগিং এর মাধ্যমে যদি আপনার কয়েক হাজার কাজ থাকে এবং কয়েকটি অনুপস্থিত থাকে।
অন্য সমস্যাটি হ'ল সেন্ডিনেল মান ব্যবহার। অনেক লোক কাতারটির শেষে পতাকাটি প্রেরণ করতে কাতারে একটি সেন্ডিনেল মান যুক্ত করার পরামর্শ দিয়েছে। তবে একে ঠিক পতাকা কার কাছে? যদি সেখানে এন কর্মী থাকে, ধরে নেওয়া হয় যে এন উপলব্ধ এবং নেওয়া উপলব্ধ কোরগুলির সংখ্যা, তবে একটি একক সেন্ডিনেল মান কেবলমাত্র এক শ্রমিকের সারিটির সমাপ্তি পতাকাঙ্কিত করবে। বাকী সমস্ত কর্মী যখন আরও কিছু না রাখেন তখন আরও কাজের জন্য অপেক্ষা করতে বসবেন। সাধারণ উদাহরণগুলি আমি দেখেছি
while True:
task = pending_queue.get()
if task == SOME_SENTINEL_VALUE:
break
একজন কর্মী সেন্ডিনেল মান পাবেন এবং বাকিরা অনির্দিষ্টকালের জন্য অপেক্ষা করবেন। আমি যে পোস্টটি দেখতে পাইনি তা উল্লেখ করেই বলেছি যে আপনার যত শ্রমিক রয়েছে তার চেয়ে কম সময়ে যতবার কাতারে সেন্ডিনেল মান জমা করতে হবে যাতে সেগুলির সমস্ত এটি পায়।
অন্য সমস্যাটি হ'ল কার্য সম্পাদনের সময় ব্যতিক্রমগুলি পরিচালনা করা। আবার এগুলি ধরা এবং পরিচালনা করা উচিত। তদুপরি, আপনার যদি একটি completed_tasks
সারি থাকে তবে কাজটি সম্পন্ন করার সিদ্ধান্ত নেওয়ার আগে আপনার স্বাধীনভাবে একটি নির্বিচার পদ্ধতিতে গণনা করা উচিত যে কয়টি আইটেম সারিতে রয়েছে। আবার সারি আকারের উপর নির্ভর করা ব্যর্থ হতে বাধ্য এবং অপ্রত্যাশিত ফলাফলগুলি ফেরত দেয়।
নীচের উদাহরণে, par_proc()
ফাংশনটি কোনও কার্যকরী তালিকাভুক্ত করবে যা কার্যবিধির সাথে কোনও নামযুক্ত যুক্তি এবং মানগুলির পাশাপাশি এই কার্যগুলি সম্পাদন করা উচিত।
import multiprocessing as mp
import dill as pickle
import queue
import time
import psutil
SENTINEL = None
def do_work(tasks_pending, tasks_completed):
worker_name = mp.current_process().name
while True:
try:
task = tasks_pending.get_nowait()
except queue.Empty:
print(worker_name + ' found an empty queue. Sleeping for a while before checking again...')
time.sleep(0.01)
else:
try:
if task == SENTINEL:
print(worker_name + ' no more work left to be done. Exiting...')
break
print(worker_name + ' received some work... ')
time_start = time.perf_counter()
work_func = pickle.loads(task['func'])
result = work_func(**task['task'])
tasks_completed.put({work_func.__name__: result})
time_end = time.perf_counter() - time_start
print(worker_name + ' done in {} seconds'.format(round(time_end, 5)))
except Exception as e:
print(worker_name + ' task failed. ' + str(e))
tasks_completed.put({work_func.__name__: None})
def par_proc(job_list, num_cpus=None):
if not num_cpus:
num_cpus = psutil.cpu_count(logical=False)
print('* Parallel processing')
print('* Running on {} cores'.format(num_cpus))
tasks_pending = mp.Queue()
tasks_completed = mp.Queue()
processes = []
results = []
num_tasks = 0
for job in job_list:
for task in job['tasks']:
expanded_job = {}
num_tasks = num_tasks + 1
expanded_job.update({'func': pickle.dumps(job['func'])})
expanded_job.update({'task': task})
tasks_pending.put(expanded_job)
num_workers = num_cpus
for c in range(num_workers):
tasks_pending.put(SENTINEL)
print('* Number of tasks: {}'.format(num_tasks))
for c in range(num_workers):
p = mp.Process(target=do_work, args=(tasks_pending, tasks_completed))
p.name = 'worker' + str(c)
processes.append(p)
p.start()
completed_tasks_counter = 0
while completed_tasks_counter < num_tasks:
results.append(tasks_completed.get())
completed_tasks_counter = completed_tasks_counter + 1
for p in processes:
p.join()
return results
এবং এখানে উপরের কোডটির বিরুদ্ধে চালানোর জন্য একটি পরীক্ষা দেওয়া হচ্ছে
def test_parallel_processing():
def heavy_duty1(arg1, arg2, arg3):
return arg1 + arg2 + arg3
def heavy_duty2(arg1, arg2, arg3):
return arg1 * arg2 * arg3
task_list = [
{'func': heavy_duty1, 'tasks': [{'arg1': 1, 'arg2': 2, 'arg3': 3}, {'arg1': 1, 'arg2': 3, 'arg3': 5}]},
{'func': heavy_duty2, 'tasks': [{'arg1': 1, 'arg2': 2, 'arg3': 3}, {'arg1': 1, 'arg2': 3, 'arg3': 5}]},
]
results = par_proc(task_list)
job1 = sum([y for x in results if 'heavy_duty1' in x.keys() for y in list(x.values())])
job2 = sum([y for x in results if 'heavy_duty2' in x.keys() for y in list(x.values())])
assert job1 == 15
assert job2 == 21
কিছু ব্যতিক্রম সহ আরও একটি one
def test_parallel_processing_exceptions():
def heavy_duty1_raises(arg1, arg2, arg3):
raise ValueError('Exception raised')
return arg1 + arg2 + arg3
def heavy_duty2(arg1, arg2, arg3):
return arg1 * arg2 * arg3
task_list = [
{'func': heavy_duty1_raises, 'tasks': [{'arg1': 1, 'arg2': 2, 'arg3': 3}, {'arg1': 1, 'arg2': 3, 'arg3': 5}]},
{'func': heavy_duty2, 'tasks': [{'arg1': 1, 'arg2': 2, 'arg3': 3}, {'arg1': 1, 'arg2': 3, 'arg3': 5}]},
]
results = par_proc(task_list)
job1 = sum([y for x in results if 'heavy_duty1' in x.keys() for y in list(x.values())])
job2 = sum([y for x in results if 'heavy_duty2' in x.keys() for y in list(x.values())])
assert not job1
assert job2 == 21
আশা করি এটি সহায়ক।