রেপা অ্যারেগুলিতে সমান্তরাল মানচিত্র


90

আমার সাম্প্রতিক কাজের সাথে Gibbs samplingআমি এর দুর্দান্ত ব্যবহার করছি RVarযা আমার দৃষ্টিতে এলোমেলো সংখ্যা জেনারেশনের জন্য একটি আদর্শ ইন্টারফেস সরবরাহ করে। দুঃখের বিষয়, মানচিত্রে monadic ক্রিয়া ব্যবহার করতে অক্ষমতার কারণে আমি রেপা ব্যবহার করতে অক্ষম।

যদিও স্পষ্টত মনাদিক মানচিত্রগুলি সাধারণভাবে সমান্তরাল করা যায় না, এটি আমার কাছে মনে হয় যে RVarকমপক্ষে একটি মনডের উদাহরণ যেখানে প্রভাবগুলি নিরাপদে সমান্তরাল করা যেতে পারে (অন্তত নীতিগতভাবে; আমি এর অভ্যন্তরীণ কাজের সাথে ভয়ঙ্করভাবে পরিচিত নই RVar) । যথা, আমি নীচের মতো কিছু লিখতে চাই,

drawClass :: Sample -> RVar Class
drawClass = ...

drawClasses :: Array U DIM1 Sample -> RVar (Array U DIM1 Class)
drawClasses samples = A.mapM drawClass samples

যেখানে A.mapMদেখতে কিছু হবে,

mapM :: ParallelMonad m => (a -> m b) -> Array r sh a -> m (Array r sh b)

এটি কীভাবে কাজ করবে তা স্পষ্টভাবে বাস্তবায়নের RVarএবং এর অন্তর্নিহিততার উপর নির্ভর করে RandomSource, নীতিগতভাবে কেউ মনে করবেন যে এটি প্রতিটি থ্রেডের জন্য একটি নতুন এলোমেলো বীজ আঁকানো এবং যথারীতি অগ্রসর হওয়া জড়িত।

স্বজ্ঞাতসুলভভাবে, মনে হয় যে এই একই ধারণাটি অন্য কিছু মনদেও সাধারণীকরণ হতে পারে।

সুতরাং, আমার প্রশ্নটি হল: কেউ এমন এক শ্রেণির ParallelMonadমান্ড তৈরি করতে পারে যার জন্য প্রভাবগুলি নিরাপদে সমান্তরাল করা যায় (সম্ভবত কমপক্ষে, এর দ্বারা বাস করা যায় RVar)?

এটি দেখতে কেমন হতে পারে? এই শ্রেণিতে অন্য কোন মনদেহ বাস করতে পারে? অন্যেরা কীভাবে এটি রেপাতে কাজ করতে পারে তার সম্ভাবনা বিবেচনা করেছেন?

অবশেষে, যদি সমান্তরাল মনাদিক ক্রিয়াকলাপের এই ধারণাটি সাধারণীকরণ করা না যায়, তবে কেউ নির্দিষ্ট কাজ RVar(যেখানে এটি খুব কার্যকর হবে) এর ক্ষেত্রে এই কাজটি করার কোনও দুর্দান্ত উপায় দেখেন ? RVarসমান্তরালতার জন্য ছেড়ে দেওয়া খুব কঠিন বাণিজ্য বন্ধ is


4
আমার অনুমান যে স্টিকিং পয়েন্টটি হ'ল প্রতিটি থ্রেডের জন্য একটি নতুন এলোমেলো বীজ আঁকা "- এই পদক্ষেপটি কীভাবে কাজ করা উচিত এবং সমস্ত থ্রেড ফিরে আসার পরে কীভাবে বীজগুলি আবার একত্রিত করা উচিত?
ড্যানিয়েল ওয়াগনার

4
আরভিআর ইন্টারফেসটি একটি নির্দিষ্ট বীজের সাথে একটি নতুন জেনারেটর তৈরি করার জন্য প্রায় অবশ্যই কিছু সংযোজন প্রয়োজন। স্বীকার করা, এটি অস্পষ্ট যে কীভাবে এই কাজের মেকানিক্স এবং এটি বেশ RandomSourceসুনির্দিষ্ট মনে হয় । বীজ আঁকার ক্ষেত্রে আমার নির্বিচার প্রচেষ্টাটি হ'ল সহজ এবং সম্ভবত খুব ভুল কিছু করা যেমন উপাদানগুলির ভেক্টর আঁকার (ক্ষেত্রে mwc-random) এবং প্রথম কর্মীর জন্য একটি বীজ উত্পাদন করতে প্রতিটি উপাদানকে 1 যোগ করা, দ্বিতীয়টির জন্য 2 যোগ করা কর্মী, ইত্যাদি। আপনার যদি ক্রিপ্টোগ্রাফিক মানের মানের এনট্রপি দরকার হয়; আশা করি ঠিক আপনার যদি এলোমেলো হাঁটার দরকার হয়।
বিগামারী

4
একটি অনুরূপ সমস্যা সমাধানের চেষ্টা করার সময় আমি এই প্রশ্নটি জুড়ে এসেছি। আমি MonadRandom এবং System.Random ব্যবহার করছি সমান্তরালভাবে monadic র্যান্ডম গণনা জন্য। এটি কেবল সিস্টেম.র্যান্ডমের splitফাংশন দ্বারা সম্ভব । এটির বিভিন্ন ফলাফল আনার অসুবিধা রয়েছে (প্রকৃতির কারণে splitএটি কাজ করে। তবে, আমি এটি রেপা অ্যারেগুলিতে প্রসারিত করার চেষ্টা করছি এবং বেশি ভাগ্যবান নই। আপনি কি এটি নিয়ে কোনও অগ্রগতি করেছেন বা এটি একটি মৃত- শেষ?
টম সেভেজ

4
সিকোয়েন্সিং ছাড়াই মোনাড এবং গণনার মধ্যে নির্ভরতা আমার কাছে প্রযোজ্য বলে মনে হয়।
জন টাইারি

4
আমি দ্বিধায় আছি টম সেভেজ নোট হিসাবে, splitএকটি প্রয়োজনীয় ভিত্তি সরবরাহ করে, তবে কীভাবে splitপ্রয়োগ করা হয় তার উত্সের মন্তব্যটি নোট করুন : "- এটির জন্য কোনও পরিসংখ্যানিক ভিত্তি নেই!"! আমি ভাবতে আগ্রহী যে কোনও পিআরএনজি বিভক্ত করার যে কোনও পদ্ধতি তার শাখাগুলির মধ্যে শোষণমূলক সম্পর্ক রেখে যাবে, তবে এটি প্রমাণ করার জন্য পরিসংখ্যানগত পটভূমি নেই। সাধারণ প্রশ্ন সম্পর্কে, আমি নিশ্চিত নই যে
ইস্তুর্তি

উত্তর:


7

এই প্রশ্নটি জিজ্ঞাসা করার পরে 7 বছর হয়ে গেছে এবং এখনও মনে হচ্ছে কেউ এই সমস্যার ভাল সমাধান নিয়ে আসে নি। রেপাতে কোনও mapM/ traverseমত ফাংশন নেই, এমনও একটি যা সমান্তরালতা ছাড়াই চলতে পারে। তদ্ব্যতীত, গত কয়েক বছরে যে পরিমাণ অগ্রগতি হয়েছিল তা বিবেচনা করে তা সম্ভবত হয় না বলেই মনে হয়।

হাস্কেলের অনেক অ্যারে লাইব্রেরির বাসি অবস্থা এবং তাদের বৈশিষ্ট্যগুলির সাথে আমার সামগ্রিক অসন্তোষের কারণে আমি কয়েক বছরের কাজ একটি অ্যারে লাইব্রেরিতে রেখেছি massiv, যা রেপা থেকে কিছু ধারণা ধার নিয়েছে, তবে একে একে সম্পূর্ণ ভিন্ন স্তরে নিয়ে গেছে। ইন্ট্রো দিয়ে যথেষ্ট।

আজ এর আগে সেখানে ফাংশন মত তিন পরমাণুসদৃশ্য মানচিত্র ছিল massiv(ফাংশন মত সমার্থক গণনা না: imapM, forM। এট):

  • mapM- একটি স্বেচ্ছায় সাধারণ ম্যাপিং Monad। সুস্পষ্ট কারণে সমান্তরালযোগ্য নয় এবং কিছুটা ধীর ( mapMএকটি তালিকার ধীরে ধীরে ধীরে ধীরে)
  • traversePrim- এখানে আমরা সীমাবদ্ধ PrimMonad, যা তুলনায় উল্লেখযোগ্যভাবে দ্রুত mapM, তবে এর কারণ এই আলোচনার জন্য গুরুত্বপূর্ণ নয়।
  • mapIO- এই নামটি যেমন প্রস্তাব করে, তেমন সীমাবদ্ধ IO(বা বরং MonadUnliftIOএটি অপ্রাসঙ্গিক)। যেহেতু আমরা আছি তাই আমরা IOস্বয়ংক্রিয়ভাবে অনেকগুলি খণ্ডে অ্যারে বিভক্ত করতে পারি যেহেতু কোর রয়েছে এবং IOসেই অংশগুলির প্রতিটি উপাদানগুলির ক্রিয়াটি মানচিত্র করতে পৃথক কর্মী থ্রেড ব্যবহার করতে পারে । খাঁটি থেকে পৃথক fmap, যা সমান্তরালও, IOআমাদের ম্যাপিং ক্রিয়াটির পার্শ্ব প্রতিক্রিয়াগুলির সাথে মিলিত সময় নির্ধারণের অ-নির্ধারকতার কারণে আমাদের এখানে থাকতে হবে ।

সুতরাং, আমি একবার এই প্রশ্নটি পড়ার পরে, আমি নিজেকে ভেবেছিলাম যে সমস্যাটি কার্যত সমাধান হয়েছে massiv, তবে এত দ্রুত নয় no এলোমেলো সংখ্যা জেনারেটর, যেমন mwc-randomএবং অন্যান্যরা random-fuঅনেক থ্রেডে একই জেনারেটর ব্যবহার করতে পারে না। যার অর্থ, আমি যে ধাঁধাটি হারিয়েছিলাম তার একমাত্র অংশটি হ'ল: "প্রতিটি থ্রেডের জন্য একটি নতুন এলোমেলো বীজ অঙ্কন করা এবং যথারীতি অগ্রসর হওয়া"। অন্য কথায়, আমার দুটি জিনিস প্রয়োজন:

  • এমন একটি ফাংশন যা শ্রমিকের থ্রেড হিসাবে যত বেশি জেনারেটর সূচনা করবে
  • এবং এমন একটি বিমূর্ততা যা কোনও থ্রেডে ক্রিয়া চলছে তার উপর নির্ভর করে ম্যাপিং ফাংশনে নির্বিঘ্নে সঠিক জেনারেটরটি দেবে।

আমি ঠিক তাই করেছি।

প্রথমে আমি বিশেষভাবে তৈরি করা randomArrayWSএবং initWorkerStatesফাংশনগুলি ব্যবহার করে উদাহরণ দেব , কারণ তারা প্রশ্নের সাথে আরও প্রাসঙ্গিক এবং পরে আরও সাধারণ মোনাডিক মানচিত্রে চলে যায় move এখানে তাদের ধরণের স্বাক্ষর রয়েছে:

randomArrayWS ::
     (Mutable r ix e, MonadUnliftIO m, PrimMonad m)
  => WorkerStates g -- ^ Use `initWorkerStates` to initialize you per thread generators
  -> Sz ix -- ^ Resulting size of the array
  -> (g -> m e) -- ^ Generate the value using the per thread generator.
  -> m (Array r ix e)
initWorkerStates :: MonadIO m => Comp -> (WorkerId -> m s) -> m (WorkerStates s)

যাঁদের সাথে পরিচিত নন massiv, তাদের পক্ষে Compযুক্তিটি গণনার কৌশল, উল্লেখযোগ্য নির্মাতারা হলেন:

  • Seq - কোনও থ্রেড না দিয়েই ধারাবাহিকভাবে গণনা চালান
  • Par - যতটা দক্ষতা রয়েছে ততটা থ্রেড স্পিন করুন এবং সেগুলি কাজ করতে ব্যবহার করুন।

আমি mwc-randomপ্রথমে প্যাকেজটিকে উদাহরণ হিসাবে ব্যবহার করব এবং পরে এখানে স্থানান্তর করব RVarT:

λ> import Data.Massiv.Array
λ> import System.Random.MWC (createSystemRandom, uniformR)
λ> import System.Random.MWC.Distributions (standard)
λ> gens <- initWorkerStates Par (\_ -> createSystemRandom)

উপরে আমরা সিস্টেমের এলোমেলো ব্যবহার করে থ্রেডের জন্য পৃথক জেনারেটর সূচনা করেছি, তবে আমরা WorkerIdযুক্তির সাহায্যে থ্রেড বীজকে অনন্যভাবে ব্যবহার করতে পারলাম যা Intশ্রমিকের নিছক সূচক। এবং এখন আমরা সেই জেনারেটরগুলি এলোমেলো মান সহ একটি অ্যারে তৈরি করতে ব্যবহার করতে পারি:

λ> randomArrayWS gens (Sz2 2 3) standard :: IO (Array P Ix2 Double)
Array P Par (Sz (2 :. 3))
  [ [ -0.9066144845415213, 0.5264323240310042, -1.320943607597422 ]
  , [ -0.6837929005619592, -0.3041255565826211, 6.53353089112833e-2 ]
  ]

Parকৌশল ব্যবহার করে schedulerগ্রন্থাগারটি উপলব্ধ শ্রমিকদের মধ্যে প্রজন্মের কাজ সমানভাবে বিভক্ত হবে এবং প্রতিটি শ্রমিক তার নিজস্ব জেনারেটর ব্যবহার করবে, সুতরাং এটি থ্রেডটিকে নিরাপদ করে তুলবে। WorkerStatesএকসাথে সম্পন্ন না করা পর্যন্ত একই সালিশি সংখ্যার পুনরায় ব্যবহার থেকে আমাদের কোনও কিছুই বাধা দেয় না, যা অন্যথায় ব্যতিক্রম হতে পারে:

λ> randomArrayWS gens (Sz1 10) (uniformR (0, 9)) :: IO (Array P Ix1 Int)
Array P Par (Sz1 10)
  [ 3, 6, 1, 2, 1, 7, 6, 0, 8, 8 ]

এখন mwc-randomপাশে রেখে আমরা অন্যান্য সম্ভাব্য ব্যবহারের ক্ষেত্রে একই ধারণাটি পুনরায় ব্যবহার করতে পারি যেমন generateArrayWS:

generateArrayWS ::
     (Mutable r ix e, MonadUnliftIO m, PrimMonad m)
  => WorkerStates s
  -> Sz ix --  ^ size of new array
  -> (ix -> s -> m e) -- ^ element generating action
  -> m (Array r ix e)

এবং mapWS:

mapWS ::
     (Source r' ix a, Mutable r ix b, MonadUnliftIO m, PrimMonad m)
  => WorkerStates s
  -> (a -> s -> m b) -- ^ Mapping action
  -> Array r' ix a -- ^ Source array
  -> m (Array r ix b)

এখানে কিভাবে এই কার্যকারিতা ব্যবহার করার জন্য প্রতিশ্রুত উদাহরণ rvar, random-fuএবং mersenne-random-pure64লাইব্রেরি। আমরা randomArrayWSএখানেও ব্যবহার করতে পারতাম , তবে উদাহরণের জন্য বলে নেওয়া যাক যে আমাদের ইতিমধ্যে বিভিন্ন RVarTএস সহ একটি অ্যারে রয়েছে , এক্ষেত্রে আমাদের একটি দরকার mapWS:

λ> import Data.Massiv.Array
λ> import Control.Scheduler (WorkerId(..), initWorkerStates)
λ> import Data.IORef
λ> import System.Random.Mersenne.Pure64 as MT
λ> import Data.RVar as RVar
λ> import Data.Random as Fu
λ> rvarArray = makeArrayR D Par (Sz2 3 9) (\ (i :. j) -> Fu.uniformT i j)
λ> mtState <- initWorkerStates Par (newIORef . MT.pureMT . fromIntegral . getWorkerId)
λ> mapWS mtState RVar.runRVarT rvarArray :: IO (Array P Ix2 Int)
Array P Par (Sz (3 :. 9))
  [ [ 0, 1, 2, 2, 2, 4, 5, 0, 3 ]
  , [ 1, 1, 1, 2, 3, 2, 6, 6, 2 ]
  , [ 0, 1, 2, 3, 4, 4, 6, 7, 7 ]
  ]

এটি লক্ষণীয় গুরুত্বপূর্ণ যে উপরোক্ত উদাহরণে মার্সেন টুইস্টারকে খাঁটি প্রয়োগ করা হচ্ছে তবুও আমরা আইও থেকে বাঁচতে পারি না। এটি অ-নিরঙ্কুশিক সময়সূচির কারণে, যার অর্থ আমরা কখনই জানি না যে কোন শ্রমিক কোন অ্যারে পরিচালনা করবে এবং ফলস্বরূপ অ্যারের কোন অংশের জন্য কোন জেনারেটর ব্যবহার করা হবে। উপরের দিকে, যদি জেনারেটর বিশুদ্ধ এবং বিভাজনযোগ্য হয়, যেমন splitmix, তবে আমরা খাঁটি, নির্জনবাদী এবং সমান্তরাল প্রজন্মের ফাংশনটি ব্যবহার করতে পারি: randomArrayতবে এটি ইতিমধ্যে একটি পৃথক গল্প।


আপনি যদি কিছু মানদণ্ড দেখতে চান: alexey.kuleshevi.ch/blog/2019/12/21/random-benchmark
lehins

4

পিআরএনজির অন্তর্নিহিত অনুক্রমিক প্রকৃতির কারণে এটি করা সম্ভবত ভাল ধারণা নয়। পরিবর্তে, আপনি নিম্নলিখিত হিসাবে আপনার কোড স্থানান্তর করতে চাইতে পারেন:

  1. একটি আইও ফাংশন ঘোষণা করুন ( mainবা আপনার কী আছে)।
  2. আপনার প্রয়োজন যতগুলি এলোমেলো সংখ্যা পড়ুন।
  3. আপনার রেপা ফাংশনগুলিতে (এখন খাঁটি) নম্বরগুলি পাস করুন।

পরিসংখ্যানগত স্বাধীনতা তৈরি করতে প্রতিটি সমান্তরাল থ্রেডে প্রতিটি পিআরএনজি বার্ন-ইন করা সম্ভব হবে কি?
জে আব্রাহামসন

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