مفهوم PLINQ در Net.

در این مقاله ما قصد معرفی تکنولوژی PLINQ داریم . در ادامه این مقاله با ارائه یک مثال ، مقایسه ای بر LINQ و PLINQ خواهیم داشت و تفاوت ها ، مزیت ها و معایب آن را بررسی خواهیم کرد .

مفهوم PLINQ در Net.

معرفی :
اکثرا توسعه دهندگان Net. با LINQ آشنا میباشند،تکنولوژی ای که طرح و نقشه برنامه نویسی عملی
(functional programming ) را به محیط شی گرایی می‌آورد. Parallel LINQ یا PLINQ, با اضافه کردن قابلیت های موازی بصری بر روی یک فریمورک قدرتمند، LINQ را یک مرحله بالا میبرد . 

PLINQ یک موتور اجرای پرس و جو (query) است ،هر پرس و جوی LINQ-to-Objects یا LINQ-to-XML را می‌پذیرد و بصورت خودکار از چندین پردازشگر یا هسته که برای اجرا در دسترس هستند ، استفاده میکند .

استفاده از PLINQ دقیقا همانند استفاده از LINQ-to-Objects و LINQ-to-XML است . شما از تمام Operatorهایی که در  3.0 #C یا کلاس 
System.Linq.Enumerable ، شامل OrderByJoinSelect,Where هستند ، میتوانید استفاده کنید . 

کوئری های LINQ-to-SQL و LINQ-to-Entities بوسیله پایگاه داده های خاص و query providerها ، قابل اجرا خواهند بود . بنابراین ، PLINQ راهی برای توازی این دسته از کوئری ها پیشنهاد نمیدهد .


استفاده از متد 
AsParallel :
 
متد AsParallel  ، درگاهی است به PLINQ .رشته ای از داده ها را به ParallelQuery تبدیل می‌کند . موتور LINQ استفاده از ParallelQuery در queryها را تشخیص میدهد و بصورت خودکار آن را به PLINQ تبدیل میکند . شما در هر بار استفاده از PLINQ به احتمال زیاد باید از متد AsParallel هم استفاده کنید . 

Sequential LINQ execution :

 

var customers = new[] {
	new Customer { ID = 1,  FirstName = "Sandeep"  , LastName = "Ramani" },
	new Customer { ID = 2,  FirstName = "Dharmik"  , LastName = "Chotaliya" },
	new Customer { ID = 3,  FirstName = "Nisar"    ,  LastName = "Kalia" } ,
	new Customer { ID = 4,  FirstName = "Ravi"     , LastName = "Mapara" } ,
	new Customer { ID = 5,  FirstName = "Hardik"   , LastName = "Mistry" }
	new Customer { ID = 6,  FirstName = "Sandy"    , LastName = "Ramani" },
	new Customer { ID = 7,  FirstName = "Jigar"    , LastName = "Shah" },
	new Customer { ID = 8,  FirstName = "Kaushal"  , LastName = "Parik" } ,
	new Customer { ID = 9,  FirstName = "Abhishek" , LastName = "Swarnker" } ,
	new Customer { ID = 10, FirstName = "Sanket"   , LastName = "Patel" }
	new Customer { ID = 11, FirstName = "Dinesh"   , LastName = "Prajapati" },
	new Customer { ID = 12, FirstName = "Jayesh"   , LastName = "Patel" },
	new Customer { ID = 13, FirstName = "Nimesh"   , LastName = "Mishra" } ,
	new Customer { ID = 14, FirstName = "Shiva"    , LastName = "Reddy" } ,
	new Customer { ID = 15, FirstName = "Jasmin"   , LastName = "Malviya" }
	new Customer { ID = 16, FirstName = "Haresh"   , LastName = "Bhanderi" },
	new Customer { ID = 17, FirstName = "Ankit"    , LastName = "Ramani" },
	new Customer { ID = 18, FirstName = "Sanket"   , LastName = "Shah" } ,
	new Customer { ID = 19, FirstName = "Amit"     , LastName = "Shah" } ,
	new Customer { ID = 20, FirstName = "Nilesh"   , LastName = "Soni" }       };

var results = from c in customers
	      where c.FirstName.StartsWith("San")
	      select c;


Parallel LINQ execution :

 

var customers = new[] {
	new Customer { ID = 1,  FirstName = "Sandeep"  , LastName = "Ramani" },
	new Customer { ID = 2,  FirstName = "Dharmik"  , LastName = "Chotaliya" },
	new Customer { ID = 3,  FirstName = "Nisar"    ,  LastName = "Kalia" } ,
	new Customer { ID = 4,  FirstName = "Ravi"     , LastName = "Mapara" } ,
	new Customer { ID = 5,  FirstName = "Hardik"   , LastName = "Mistry" }
	new Customer { ID = 6,  FirstName = "Sandy"    , LastName = "Ramani" },
	new Customer { ID = 7,  FirstName = "Jigar"    , LastName = "Shah" },
	new Customer { ID = 8,  FirstName = "Kaushal"  , LastName = "Parik" } ,
	new Customer { ID = 9,  FirstName = "Abhishek" , LastName = "Swarnker" } ,
	new Customer { ID = 10, FirstName = "Sanket"   , LastName = "Patel" }
	new Customer { ID = 11, FirstName = "Dinesh"   , LastName = "Prajapati" },
	new Customer { ID = 12, FirstName = "Jayesh"   , LastName = "Patel" },
	new Customer { ID = 13, FirstName = "Nimesh"   , LastName = "Mishra" } ,
	new Customer { ID = 14, FirstName = "Shiva"    , LastName = "Reddy" } ,
	new Customer { ID = 15, FirstName = "Jasmin"   , LastName = "Malviya" }
	new Customer { ID = 16, FirstName = "Haresh"   , LastName = "Bhanderi" },
	new Customer { ID = 17, FirstName = "Ankit"    , LastName = "Ramani" },
	new Customer { ID = 18, FirstName = "Sanket"   , LastName = "Shah" } ,
	new Customer { ID = 19, FirstName = "Amit"     , LastName = "Shah" } ,
	new Customer { ID = 20, FirstName = "Nilesh"   , LastName = "Soni" }       };

var results = from c in customers.AsParallel()
	      where c.FirstName.StartsWith("San")
	      select c;


با اضافه کردن متد ()AsParallel ،زمان اجرای Net.  توازی (parallelize ) عملیات ها در سراسر هسته های چندگانه به طور خودکار انجام می‌شود . در حقیقت ، PLINQ مسئولیت قسمت بندی کردن داده ها، برای اینکه قادر به پردازش آنها بصورت موازی باشد را ، تماما بر عهده می‌گیرد . 

زمانی که شما کوئری های ساده بالا را اجرا می‌کنید ،خروجی یکسانی دریافت خواهید کرد اما احتمالا در جهت های مختلف . تکه کد اول یک مثال از Sequential LINQ execution ، و تکه کد دوم یک مثال از Parallel LINQ execution میباشد . 

محدودیت ها : 
1. PLINQ فقط بر روی مجموعه های local کار میکند . این بدان معناست که اگر شما از LINQ Providerهایی ، همانند LINQ to SQL یا ADO.NET Entity Framework استفاده می‌کنید ، شما شانسی در این زمینه برای استفاده از این نسخه ندارید . 

2. زمانی که داده هارا قسمت بندی میکنید و آنها را بصورت موازی اجرا میکنید ، خروجیِ شما دقیقا همانند خروجی اجرای کدها بصورت سریالی نمیشود . 


اگرچه ، شما قادر به استفاده از آن با استفاده از متد  ()AsOrdered در کوئری های خود خواهید بود ، که دستورِ خاصی را بصورت اجباری بر روی نتیجه شما اعمال می‌کند، به یاد داشته باشید که ،  متد ()AsOrdered
برای مجموعه های بزرگ روی کارایی تاثیر دارد . 


حفظ نظم نتایج جستجوی کوئری PLINQ  با استفاده از متد AsOdered :

 

var results = from c in customers.AsParallel().AsOrdered()
	      where c.FirstName.StartsWith("San")
	      select c;


کنترل توازی :

1. اجبار اجرای موازی :
در اینگونه موارد ، PLINQ ممکن است تصمیم بگیرد که با کوئری شما بهتر است بصورت ترتیبی رفتار شود . شما این را می‌توانید با استفاده از متد WithExecutionMode کنترل کنید ، که به نوع ParallelQuery اعمال می‌شود . متد 
WithExecutionMode  یک مقدار از ParallelExecutionMode enumeration دریافت میکند . در اینجا دو نوع مقدار وجود دارد : defualt (اجازه می‌دهد PLINQ برای اینکه چه کاری انجام شود تصمیم بگیرد ) و ForceParallelism .


در زیر تکه کدی وجود دارد که استفاده از متد را نمایش می‌دهد :

 

var results = from c in customers.AsParallel().WithExecutionMode
			(ParallelExecutionMode.ForceParallelism)
	      where c.FirstName.StartsWith("San")
	      select c;


2. محدود کردن درجه توازی :

شما می‌توانید با استفاده از متد WithDegreeofParallelism که روی ParallelQuery کار میکند   ، درخواست دهید که PLINQ تعداد قسمت هایی که قرار است با هم پردازش شوند را محدود کند . این متد ، یک مقدار int دریافت میکند که نشان دهنده ماکزیمم تعداد قسمت بندی هایی است که با ید با هم پردازش شوند . این را تحت عنوان درجه توازی میشناسند . تنظیم درجه توازی PLINQ را مجبور به استفاده از آن نمی‌کند . فقط سقف درجه توازی را تعیین می‌کند . ممکن است PLINQ تصمیم به استفاده از مقدار کمتری از آن چه شما مشخص کرده اید ، بگیرد . اگر شما از متد WithExecutionMode استفاده نمی‌کنید ، ممکن است تصمیم به اجرای queryها بصورت ترتیبی بکنید . 

در زیر تکه کدی آمده است که استفاده از این متد را نمایش می‌دهد :

 

var results = from c in customers.AsParallel().WithDegreeOfParallelism(2)
	      where c.FirstName.StartsWith("San")
	      select c;


3. تولید و استفاده از توالی موازی (Parallel Sequence) :
 

IEnumerable<int> evens
	= ((ParallelQuery<int>) ParallelEnumerable.Range(0, 50000))
		.Where(i => i % 2 == 0)
		.Select(i => i);

کد بالا از متد Range برای ایجاد 50000 عدد integer که از 0 شروع میشود ،  استفاده کرده است . اولین آرگومان دریافتی اندیس آغازین آن است و دومین آرگومان تعداد مقادیری که شما نیاز دارید . توجه داشته باشید که ما مقدار را از متد Range به Cast ، ParallelQuery کردیم . اگر این کار را نکنیم ، LINQ پشتیبانی از توالی بصورت موازی را تشخیص نمی‌دهد و کوئری ها را به صورت متوالی اجرا خواهد کرد . 

4. تولید و استفاده از تکرار توالی (Repeating Sequnce) :

 

int sum = ParallelEnumerable.Repeat(1, 50000)
		.Select(i => i)
		.Sum();

متد Static Repeat یک شئ و یک مقدار را دریافت می‌کند و توالی ای را ایجاد می‌کند که در آن اشیا به تعداد مقداری که متد دریافت کرده است تکرار می‌شوند .