মাল্টিপ্রসেসিং প্রক্রিয়াগুলির মধ্যে বৃহত্তর, কেবল পঠনযোগ্য নম্পি অ্যারে ভাগ করুন


90

আমার কাছে একটি 60 গিগাবাইট সাইপাই অ্যারে (ম্যাট্রিক্স) আছে আমাকে অবশ্যই 5+ multiprocessing Processঅবজেক্টের মধ্যে ভাগ করতে হবে । আমি ন্যুপি-শেয়ার্ডমেম দেখেছি এবং এই আলোচনাটি সায়পাই তালিকায় পড়েছি । দুটি পন্থা বলে মনে হচ্ছে - numpy-sharedmemএবং একটি multiprocessing.RawArray()এবং NumPy dtypeগুলিকে ম্যাপিং ব্যবহার করে ctype। এখন, numpy-sharedmemযাওয়ার উপায় মনে হচ্ছে, তবে আমি এখনও একটি ভাল রেফারেন্স উদাহরণ দেখতে পাইনি। আমার কোনও ধরণের তালার দরকার নেই, যেহেতু অ্যারে (আসলে একটি ম্যাট্রিক্স) কেবল পঠনযোগ্য হবে। এখন, এর আকারের কারণে, আমি একটি অনুলিপি এড়াতে চাই। এটা তোলে শোনাচ্ছে মত সঠিক পদ্ধতি তৈরি করা শুধুমাত্র হিসেবে অ্যারের কপি sharedmemঅ্যারে, এবং তারপর এটি পাস Processবস্তু? কয়েকটি নির্দিষ্ট প্রশ্ন:

  1. শেয়ারডেম্ম হ্যান্ডলগুলি সাব-এসগুলিতে পাস করার সর্বোত্তম উপায় কী Process()? আমার চারপাশে একটি অ্যারে পাস করার জন্য কি আমার কিউ দরকার? একটি পাইপ ভাল হতে পারে? আমি কি কেবল এটিকে Process()সাবক্লাসের সূচনার আর্গুমেন্ট হিসাবে পাস করতে পারি (যেখানে আমি ধরেছি এটি সংকটযুক্ত)?

  2. উপরে উল্লিখিত আলোচনায় numpy-sharedmem64৪ বিট-নিরাপদ না থাকার কথা রয়েছে ? আমি অবশ্যই এমন কিছু কাঠামো ব্যবহার করছি যা 32-বিট ঠিকানার মতো নয়।

  3. ট্রেডঅফের RawArray()পদ্ধতির কী আছে? ধীর, বুগিয়ার?

  4. নম্পি-শেয়ারডেম পদ্ধতিতে আমার কি কোনও টাইপ-টু-ডাইপ ম্যাপিং দরকার?

  5. কারও কি এমন কিছু ওপেনসোর্স কোড করার উদাহরণ রয়েছে? আমি খুব শিখেছি এবং কোনও ধরণের ভাল উদাহরণ না দেখে এ কাজ করা শক্ত।

অন্যদের জন্য এটি পরিষ্কার করতে সহায়তা করতে আমি সরবরাহ করতে পারে এমন কোনও অতিরিক্ত তথ্য যদি থাকে তবে দয়া করে মন্তব্য করুন এবং আমি যুক্ত করব। ধন্যবাদ!

এই উবুন্টু লিনাক্স এবং চালানোর জন্য প্রয়োজন হতে পারে ম্যাক OS কিন্তু বহনযোগ্যতা বিপুল উদ্বেগ নয়।


4
যদি বিভিন্ন প্রক্রিয়া সেই অ্যারেতে লিখতে চলেছে, multiprocessingপ্রতিটি প্রক্রিয়াটির জন্য পুরো জিনিসটির একটি অনুলিপি তৈরির প্রত্যাশা করুন ।
টিয়াগো

4
@ টিয়াগো: "আমার কোনও ধরণের তালার দরকার নেই, যেহেতু অ্যারে (প্রকৃতপক্ষে একটি ম্যাট্রিক্স) কেবল পঠনযোগ্য হবে"
ডাঃ জান-ফিলিপ গেহর্ককে

4
@ টিয়াগো: এছাড়াও, মাল্টিপ্রসেসিং যতক্ষণ না স্পষ্টভাবে বলা হয়েছে (তত যুক্তি দিয়ে target_function) একটি অনুলিপি তৈরি করছে না । অপারেটিং সিস্টেমটি কেবলমাত্র পরিবর্তনের পরে পিতামাতার মেমরির কিছু অংশ সন্তানের মেমরি স্পেসে অনুলিপি করতে চলেছে।
ডাঃ জানু-ফিলিপ গেহর্কেকে


আমি এর আগে কিছু প্রশ্ন জিজ্ঞাসা করেছি । আমার সমাধানটি এখানে পাওয়া যাবে: github.com/david-hoffman/peaks/blob/… (দুঃখিত কোডটি একটি দুর্যোগ)।
ডেভিড হফম্যান

উত্তর:


30

@ ওয়েলিমির ম্লেকার একটি দুর্দান্ত উত্তর দিয়েছেন। আমি ভাবলাম আমি কিছু বিট মন্তব্য এবং একটি ছোট উদাহরণ যুক্ত করতে পারি।

(আমি শেয়ারডেমে খুব বেশি ডকুমেন্টেশন খুঁজে পাইনি - এগুলি আমার নিজস্ব পরীক্ষার ফলাফল))

  1. সাব-প্রসেসটি শুরু হওয়ার পরে, বা এটি শুরু হওয়ার পরে আপনার কি হ্যান্ডলগুলি পাস করতে হবে? যদি এটি কেবল প্রাক্তন হয় তবে আপনি এর জন্য যুক্তি targetএবং argsতর্কগুলি ব্যবহার করতে পারেন Process। এটি গ্লোবাল ভেরিয়েবল ব্যবহারের চেয়ে সম্ভাব্যরূপে ভাল।
  2. আপনার লিঙ্ক করা আলোচনার পৃষ্ঠা থেকে মনে হচ্ছে কিছুক্ষণ আগে shared৪-বিট লিনাক্সের জন্য শেয়ারডমেমে যোগ দেওয়া হয়েছিল, সুতরাং এটি কোনও সমস্যাবিহীন হতে পারে।
  3. আমি এই সম্পর্কে জানি না।
  4. না। নীচের উদাহরণ দেখুন।

উদাহরণ

#!/usr/bin/env python
from multiprocessing import Process
import sharedmem
import numpy

def do_work(data, start):
    data[start] = 0;

def split_work(num):
    n = 20
    width  = n/num
    shared = sharedmem.empty(n)
    shared[:] = numpy.random.rand(1, n)[0]
    print "values are %s" % shared

    processes = [Process(target=do_work, args=(shared, i*width)) for i in xrange(num)]
    for p in processes:
        p.start()
    for p in processes:
        p.join()

    print "values are %s" % shared
    print "type is %s" % type(shared[0])

if __name__ == '__main__':
    split_work(4)

আউটপুট

values are [ 0.81397784  0.59667692  0.10761908  0.6736734   0.46349645  0.98340718
  0.44056863  0.10701816  0.67167752  0.29158274  0.22242552  0.14273156
  0.34912309  0.43812636  0.58484507  0.81697513  0.57758441  0.4284959
  0.7292129   0.06063283]
values are [ 0.          0.59667692  0.10761908  0.6736734   0.46349645  0.
  0.44056863  0.10701816  0.67167752  0.29158274  0.          0.14273156
  0.34912309  0.43812636  0.58484507  0.          0.57758441  0.4284959
  0.7292129   0.06063283]
type is <type 'numpy.float64'>

এই সম্পর্কিত প্রশ্ন দরকারী হতে পারে।


37

আপনি যদি লিনাক্সে (বা কোনও পসিক্স-কমপ্লায়েন্ট সিস্টেম) থাকেন তবে আপনি এই অ্যারেটিকে বিশ্বব্যাপী পরিবর্তনশীল হিসাবে সংজ্ঞায়িত করতে পারেন। এটি একটি নতুন শিশু প্রক্রিয়া শুরু করার সময় লিনাক্সে multiprocessingব্যবহার fork()করছে। একটি নতুন প্রজ্বলিত শিশু প্রক্রিয়া যতক্ষণ না এটি পরিবর্তন করে না ততক্ষণ তার পিতামাতার সাথে মেমরিটি ভাগ করে দেয় ( অনুলিপি-অনুলিপি প্রক্রিয়া)।

যেহেতু আপনি বলছেন "আমার কোনও ধরণের তালার দরকার নেই, যেহেতু অ্যারে (প্রকৃতপক্ষে একটি ম্যাট্রিক্স) কেবল পঠনযোগ্য হবে" এই আচরণের সুযোগ নেওয়া খুব সহজ এবং তবুও অত্যন্ত দক্ষ পদ্ধতি হবে: সমস্ত শিশু প্রক্রিয়া অ্যাক্সেস করবে শারীরিক মেমরির একই ডেটা এই বৃহত্তর ন্যাকা অ্যারে পড়ার সময়।

আপনার অ্যারেটি Process()কনস্ট্রাক্টরের হাতে দেবেন না , এটি আপনার সন্তানের কাছে ডেটা নির্দেশ multiprocessingদেবে pickle, যা আপনার ক্ষেত্রে অত্যন্ত অদক্ষ বা অসম্ভব হতে পারে। লিনাক্সে, ঠিক fork()একই শারীরিক মেমরি ব্যবহার করে সন্তানের পিতা-মাতার একটি সঠিক অনুলিপি, সুতরাং আপনাকে যা করতে হবে তা নিশ্চিত করা যে ম্যাট্রিক্সের সাথে পাইথন ভেরিয়েবলটি 'ম্যাট্রিক্স' রয়েছে targetতা আপনি যে ফাংশনটি হস্ত করেছেন তার মধ্যেই অ্যাক্সেসযোগ্য Process()। এটি আপনি সাধারণত একটি 'বৈশ্বিক' পরিবর্তনশীল দিয়ে অর্জন করতে পারেন।

উদাহরণ কোড:

from multiprocessing import Process
from numpy import random


global_array = random.random(10**4)


def child():
    print sum(global_array)


def main():
    processes = [Process(target=child) for _ in xrange(10)]
    for p in processes:
        p.start()
    for p in processes:
        p.join()


if __name__ == "__main__":
    main()

উইন্ডোজ - যা সমর্থন করে না fork()- multiprocessingউইন 32 এপিআই কলটি ব্যবহার করছে CreateProcess। এটি প্রদত্ত যে কোনও এক্সিকিউটেবল থেকে সম্পূর্ণ নতুন প্রক্রিয়া তৈরি করে। এজন্য পিতা-মাতার রানটাইম চলাকালীন সময়ে যদি এমন কোনও ডেটা প্রয়োজন হয় যা উইন্ডোজের একটিতে বাচ্চার কাছে ডেটা বাছাই করা প্রয়োজন।


4
অনুলিপি-অন লিখন পৃষ্ঠাটি রেফারেন্স কাউন্টার সহ কপি করবে (সুতরাং প্রতিটি ফর্কড পাইথনের নিজস্ব রেফারেন্স কাউন্টার থাকবে) তবে এটি পুরো ডেটা অ্যারে অনুলিপি করবে না।
ছিনতাই করুন

4
আমি যুক্ত করব যে আমি বৈশ্বিক ভেরিয়েবলের চেয়ে মডিউল স্তরের ভেরিয়েবলগুলির সাথে আরও সাফল্য পেয়েছি ... অর্থাৎ
কাঁটাচালার

4
এই প্রশ্ন / উত্তর জুড়ে হোঁচট খাচ্ছে লোকদের কাছে একটি সতর্কতার শব্দ: আপনি যদি ওপেনবিএলএস-যুক্ত লিঙ্কযুক্ত নম্পিটিকে তার মাল্টিথ্রিডিয়াড অপারেশনের জন্য ব্যবহার করছেন, তার মাল্টিথ্রিডিং অক্ষম করার বিষয়টি নিশ্চিত করুন (ওপেনব্ল্যাস_এনএম_থ্রেডস = 1) ব্যবহার করার সময় multiprocessingবা শিশু প্রক্রিয়াগুলি ঝুলিয়ে শেষ হতে পারে ( সাধারণত একটি ভাগ করা বৈশ্বিক অ্যারে / ম্যাট্রিক্সে রৈখিক বীজগণিত ক্রিয়াকলাপ সম্পাদন করার পরে এন প্রসেসরের পরিবর্তে এক প্রসেসরের 1 / n ব্যবহার করে । OpenBLAS সাথে জ্ঞাত মাল্টি দ্বন্দ্ব পাইথন প্রসারিত বলে মনে হয়multiprocessing
Dologan

4
অজগর কেন কেবল সিরিয়ালকরণের পরিবর্তে forkপ্রদত্ত প্যারামিটারগুলি পাস করার জন্য ওএস ব্যবহার করবে না তা যে কেউ ব্যাখ্যা করতে পারেন Process? অর্থাত, forkপ্যারেন্ট প্রসেসে কল করার ঠিক আগে child কি প্রয়োগ করা যায় না , যাতে প্যারামিটারের মানটি ওএস থেকে পাওয়া যায়? এটি সিরিয়ালিকরণের চেয়ে আরও দক্ষ বলে মনে হচ্ছে?
সর্বাধিক

4
fork()উইন্ডোজে এটি উপলভ্য নয় সে সম্পর্কে আমরা সবাই সচেতন , এটি আমার উত্তরে এবং মন্তব্যে একাধিকবার বলা হয়েছে। আমি জানি যে এই আপনার প্রারম্ভিক প্রশ্ন ছিল, এবং আমি তা উপরে চার মন্তব্য বললেন এই : "আপস ডিফল্টরূপে উভয় প্ল্যাটফর্মের উপর পরামিতি স্থানান্তর, ভাল maintainability জন্য ও সমান আচরণ নিশ্চিত করার জন্য একই পদ্ধতি ব্যবহার করা হয়।"। উভয় উপায়েই তাদের সুবিধা এবং অসুবিধাগুলি রয়েছে, এজন্য পাইথন 3-এ ব্যবহারকারীর পক্ষে পদ্ধতিটি বেছে নেওয়ার ক্ষেত্রে আরও নমনীয়তা রয়েছে। এই আলোচনাটি উত্পাদনশীল ডাব্লু / ও টকিংয়ের বিশদ নয়, যা আমাদের এখানে করা উচিত নয়।
ডাঃ জ্যান-ফিলিপ গেহর্কেকে

24

আমি লিখেছি এমন একটি ছোট্ট কোডে আপনি আগ্রহী হতে পারেন: github.com/vmlaker/benchmark-sharedmem

আগ্রহের একমাত্র ফাইল main.py। এটি নম্পি-শেয়ারডেমের একটি মানদণ্ড - কোডটি পাইপের মাধ্যমে স্প্যানড প্রসেসগুলিতে অ্যারেগুলি (হয় numpyবা হয় sharedmem) কেবল পাস করে । শ্রমিকরা কেবল sum()ডেটা কল করে। আমি কেবলমাত্র দুটি বাস্তবায়নের মধ্যে ডেটা যোগাযোগের সময়ের তুলনা করতে আগ্রহী ছিলাম।

আমি আরও একটি জটিল কোড লিখেছি: github.com/vmlaker/sherlock

এখানে আমি ওপেনসিভি সহ রিয়েল-টাইম চিত্র প্রসেসিংয়ের জন্য নিম্পি-শেয়ারডেম মডিউলটি ব্যবহার করি - চিত্রগুলি ওপেনসিভির নতুন cv2এপিআই অনুযায়ী নুমপি অ্যারে হয় । চিত্রগুলি, প্রকৃতপক্ষে এর রেফারেন্সগুলি তৈরি করা ডিকশনারি অবজেক্টের মাধ্যমে প্রক্রিয়াগুলির মধ্যে ভাগ করা হয়েছে multiprocessing.Manager(কুই বা পাইপ ব্যবহারের বিপরীতে)) সরল নুমপি অ্যারে ব্যবহারের সাথে তুলনা করার সময় আমি দুর্দান্ত পারফরম্যান্সের উন্নতি পাচ্ছি।

পাইপ বনাম সারি :

আমার অভিজ্ঞতায় পাইপের সাথে আইপিসি কিউয়ের চেয়ে দ্রুত। এবং এটি উপলব্ধি করে, যেহেতু কুই এটিকে একাধিক উত্পাদক / গ্রাহকদের জন্য সুরক্ষিত করতে লকিং যুক্ত করেছে। পাইপ দেয় না। তবে যদি আপনার কেবল দুটি প্রক্রিয়া পিছনে পিছনে কথা হয় তবে পাইপ ব্যবহার করা নিরাপদ হবে বা ডকস যেমন পড়বে:

... একই সময়ে পাইপের বিভিন্ন প্রান্তগুলি ব্যবহার করে প্রক্রিয়াগুলি থেকে দুর্নীতির ঝুঁকি নেই।

sharedmemসুরক্ষা :

sharedmemমডিউল সহ প্রধান সমস্যা হ'ল অযৌক্তিক প্রোগ্রামের প্রস্থানের পরে মেমরি ফাঁস হওয়ার সম্ভাবনা। এটি এখানে দীর্ঘ আলোচনায় বর্ণিত হয়েছে । যদিও এপ্রিল 10, 2011-এ স্টারলা স্মৃতি ফাঁসির একটি সমাধানের কথা উল্লেখ করেছেন, তারপরেও আমি এখনও দু'টি রেপো ব্যবহার করে স্টুরলা মোলডেনের গিতহাবের ( github.com/sturlamolden/sharedmem-numpy ) এবং বিটবকেটে ক্রিস লি-মেসারের লিখিত অভিজ্ঞতা পেয়েছি ( বিটবুকিট.আর . / ক্লিমেসার / নম্পি- শেয়ার্ডেম )।


ধন্যবাদ, খুব তথ্যপূর্ণ। যদিও মেমরি ফুটো sharedmemবড় কথা বলে। কোন সমাধান যে নেতৃত্ব?
উইল

4
শুধু ফাঁসগুলি লক্ষ্য করার বাইরেও আমি কোডটিতে এটি সন্ধান করিনি। আমি উপরে "শেয়ারডেম সুরক্ষা" এর অধীনে আমার জবাবটিতে যোগ করেছি sharedmem, রেফারেন্সের জন্য মডিউলটির দুটি ওপেন সোর্স রেপোর রক্ষকগণ ।
ভেলিমির ম্লেকার

14

যদি আপনার অ্যারেটি বড় হয় তবে আপনি ব্যবহার করতে পারেন numpy.memmap। উদাহরণস্বরূপ, আপনার যদি ডিস্কে একটি অ্যারে সঞ্চয় থাকে তবে বলুন 'test.array', আপনি "লিখন" মোডে এমনকি এতে ডেটা অ্যাক্সেস করতে একযোগে প্রক্রিয়াগুলি ব্যবহার করতে পারেন তবে আপনার ক্ষেত্রে কেবল "পড়া" মোডের প্রয়োজন হওয়ায় আপনার ক্ষেত্রে সহজ is

অ্যারে তৈরি করা হচ্ছে:

a = np.memmap('test.array', dtype='float32', mode='w+', shape=(100000,1000))

তারপরে আপনি কোনও সাধারণ অ্যারে দিয়ে যেমন করেন তেমনভাবে এই অ্যারেটি পূরণ করতে পারেন। উদাহরণ স্বরূপ:

a[:10,:100]=1.
a[10:,100:]=2.

আপনি যখন ভেরিয়েবলটি মুছবেন তখন ডেটা ডিস্কে সঞ্চিত থাকে a

পরে আপনি একাধিক প্রক্রিয়া ব্যবহার করতে পারেন যা এতে ডেটা অ্যাক্সেস করতে পারে test.array:

# read-only mode
b = np.memmap('test.array', dtype='float32', mode='r', shape=(100000,1000))

# read and writing mode
c = np.memmap('test.array', dtype='float32', mode='r+', shape=(100000,1000))

সম্পর্কিত উত্তর:


3

পাইওরো সম্পর্কিত ডকুমেন্টেশনগুলি একবার দেখে নেওয়া আপনার পক্ষে দরকারী হতে পারে যেমন আপনি নিজের কাজটি যথাযথভাবে পার্টিশন করতে পারেন তবে আপনি এটি বিভিন্ন মেশিনে একই বিভাগে একই মেশিনের বিভিন্ন কোরে প্রয়োগ করতে পারেন।


0

মাল্টিথ্রেডিং কেন ব্যবহার করবেন না? মূল প্রক্রিয়াটির সংস্থানগুলি মূলত তার থ্রেডগুলির মাধ্যমে ভাগ করা যায়, সুতরাং মূল প্রক্রিয়াটির মালিকানাধীন অবজেক্টগুলি ভাগ করার পক্ষে মাল্টিথ্রেডিং স্পষ্টতই একটি ভাল উপায়।

আপনি যদি অজগরটির জিআইএল প্রক্রিয়া সম্পর্কে উদ্বিগ্ন হন, তবে আপনি সম্ভবত অবলম্বন করতে পারেন nogil এর numba

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.