مخصص Partitioners PLINQ و TPL
لعملية في مصدر بيانات، وإحدى الخطوات الأساسية parallelize هو إلى قسم المصدر إلى مقاطع متعددة يمكن الوصول بشكل متزامن بمؤشرات ترابط متعددة. توفر PLINQ ومهمة متوازى مكتبة (TPL) partitioners الافتراضية التي تعمل بشكل واضح عند الكتابة استعلام متوازي أو ForEachتكرار حلقي. ل المزيد متقدمة سيناريو، يمكنك توصيل في partitioner الخاص بك.
أنواع من تقسيم
هناك العديد من الطرق إلى تقسيم مصدر بيانات. التعاون في الطرق الأكثر كفاءة، مؤشرات ترابط متعددة إلى عملية التسلسل المصدر الأصلي، بدلاً من فعلياً تقسيم المصدر subsequences متعددة. للصفائف و غير ذلك فهرسة المصادر مثل IListمجموعات بحيث يعرف الطول مقدما، تقسيم نطاق هو النوع الأبسط من التقسيم. يتلقى كل مؤشر ترابط تبدأ وتنتهي فهارس فريدة حيث يمكن عملية به نطاق المصدر دون الكتابة فوق أو يتم تجاوزها من قبل أي مؤشر ترابط آخر. الوحيد حمولة جزءا من نطاق التقسيم هو العمل الأولى لإنشاء النطاقات؛ لا يوجد تزامن إضافى هو المطلوبة بعد ذلك. ولذلك، يمكن أن توفر أداء جيدا كما طويلة كالحمل هو مقسماً بالتساوي. dهوadvantage من تقسيم النطاق هو أنه عند مؤشر ترابط واحد finهوhes مبكرا، فإنه لا يساعد finهوh عمليات جزئية الأخرى عملهم.
لربط lهوts أو مجموعات غير ذلك طوله هو غير معروفة، فيمكنك استخدام المجموعة تقسيم . في تقسيم قطعة كل مؤشر ترابط أو المهام في متوازى تكرار حلقي أو استعلام يستهلك بعض عدد العناصر المصدر في مجموعة واحدة بمعالجة هذه وثم عودة إلى استرداد عناصر إضافى. partitioner يضمن أن يتم توزيع الجميع عناصر و وجود تكرارات. قد يكون قطعة أي الحجم. ل مثال، partitioner التي يتم هو موضح في كيفية القيام بما يلي: أقسام حيوية لتنفيذإنشاء النصوص التي تحتوي على عنصر واحد فقط. طالما أن النصوص ليست قطر أيمن متوسط إلى أ، هذا النوع من التقسيم هو مثل موازنة تحميل لأن التعيين عناصر إلى لم يتم pre-determined عمليات جزئية. على الرغم من ذلك، partitioner تتطلب مقدار الحمل المزامنة في كل مرة يحتاج مؤشر ترابط إلى الحصول على مجموعة أخرى. مقدار المزامنة الذي تم في هذه الحالات هو inversely نسبة إلى الحجم النصوص.
بشكل عام، يبدأ التقسيم هو عند فقط أسرع وقت تنفيذ المفوض هو مربع متوسط إلى متوسط، والمصدر يحتوي على عدد كبير من العناصر، والعمل الإجمالي لكل قسم من أقسام هو يعادل تقريبا. تقسيم قطعة هو لذلك عادة أسرع في معظم الحالات. تشغيل المصادر مع مربع متوسط عدد من العناصر أو أوقات تنفيذ أطول للمفوض، فستكون أداء المجموعة وتقسيم النطاق حول المساواة.
partitioners TPL أيضا دعم عدد الأقسام حيوية. هذا يعني أنه يمكنك إنشاء أقسام تشغيل--حركة، ل مثال، عندما ForEachspawns تكرار حلقي مهمة جديدة. تمكن هذه الميزة partitioner لقياس مع تكرار حلقي نفسه. تكون partitioners ديناميكي أيضا مثل موازنة تحميل. عندما تقوم بإنشاء partitioner مخصص، يجب أن يدعم التقسيم الحيوي ليكون القابلة للاستهلاك من ForEachتكرار حلقي.
تكوين موازنة التحميل Partitioners ل PLINQ
بعض التحميلات الزائدة من Partitioner.Createطريقة تتيح لك إنشاء partitioner عن صفيفة أو IListالمصدر وقم بتحديد ما إذا كان يجب محاولة إلى تحميلاً بين عمليات جزئية. عند partitioner هو تكوين موازنة تحميل، chunk تقسيم هو الاستخدام، والعناصر التي يتم تسليمها إيقاف لكل قسم في مجموعات صغيرة كما تم طلبها. Th هو هذا الأسلوب يساعد على ضمان أن يكون لالجميع الأقسام عناصر لمعالجة حتى الحلقة بأكملها أو الاستعلام هو مكتمل. يمكن استخدام حالات تحميل إضافى إلى توفر موازنة تحميل تقسيم أي IEnumerableالمصدر.
وبشكل عام، يتطلب موازنة تحميل الأقسام إلى طلب عناصر نسبيا عادة من partitioner. وعلى النقيض، partitioner يقوم بتقسيم ثابتة يمكن تعيين العناصر إلى partitioner كل دفعة واحدة باستخدام نطاق أو تقسيم قطعة. ويتطلب ذلك زائد عن الحد أقل من موازنة الزائد عن الحد، ولكن قد تستغرق وقتاً أطول إلى تنفيذ إذا مؤشر ترابط واحد ينتهي العمل أكثر بكثير من الآخرين. بشكل افتراضي عند ذلك هو التي تم تمريرها ILهوt أو صفيفة، PLINQ يستخدم دائماً تقسيم النطاق دون موازنة تحميل. إلى تمكين موازنة ل PLINQ تحميل، استخدم Partitioner.Createالطريقة، كما هو مبين في المثال التالي.
' Static number of partitions requires indexable source.
Dim nums = Enumerable.Range(0, 100000000).ToArray()
' Create a load-balancing partitioner. Or specify false For Shared partitioning.
Dim customPartitioner = Partitioner.Create(nums, True)
' The partitioner is the query's data source.
Dim q = From x In customPartitioner.AsParallel()
Select x * Math.PI
q.ForAll(Sub(x) ProcessData(x))
// Static partitioning requires indexable source. Load balancing
// can use any IEnumerable.
var nums = Enumerable.Range(0, 100000000).ToArray();
// Create a load-balancing partitioner. Or specify false for static partitioning.
Partitioner<int> customPartitioner = Partitioner.Create(nums, true);
// The partitioner is the query's data source.
var q = from x in customPartitioner.AsParallel()
select x * Math.PI;
q.ForAll((x) =>
{
ProcessData(x);
});
أفضل طريقة إلى تحديد ما إذا كان إلى موازنة في أي سيناريو الموفر تحميل الاستخدام إلى التجربة وقياس طول المدة التي تستغرقها العمليات إلى كاملة ضمن التحميلات تمثيلية وتكوينات الكمبيوتر. تشغيل سبيل المثال، تقسيم الثابتة قد يوفر زيادةالسرعة هامة تشغيل جهاز multi-core أساسيات قليلة فقط، ولكن قد يؤدي البطء تشغيل أجهزة الكمبيوتر التي تحتوي تشغيل أساسيات كثيرة نسبيا.
يسرد الجدول التالي التحميلات الزائدة متوفرة في Createالأسلوب. لا تقتصر هذه partitioners للاستخدام فقط مع PLINQ أو ForEach. يمكن أيضا استخدامها مع أي تركيبة متوازي المخصصة.
التحميل الزائد |
يستخدم موازنة التحميل |
---|---|
دومًا |
|
عند وسيطة المنطقية هو المعينة على أنها صحيحة |
|
عند وسيطة المنطقية هو المعينة على أنها صحيحة |
|
أبدًا |
|
أبدًا |
|
أبدًا |
|
أبدًا |
تكوين نطاق ثابت Partitioners ل متوازى.ForEach
في Forتكرار نص بالتكرار حلقي هو توفير الأسلوب كمفوض. تكلفة استدعاء هذا المفوض هو حول نفس استدعاء أسلوب ظاهري. في بعض وحدات السيناريو، النص متوازى قد تكون تكرار حلقي صغيرة يكفي أن يصبح تكلفة استدعاء المفوض في كل تكرار تكرار حلقي هامة. في مثل هذه الحالات، يمكنك استخدام واحد Createالتحميلات الزائدة إلى إنشاء IEnumerable<T>أقسام نطاق عبر عناصر المصدر. وبعد ذلك، يمكنك تمرير هذه المجموعة من نطاقات على ForEachالأسلوب الأساسي الذي يتكون من العادية forتكرار حلقي. فوائد هذا الأسلوب هو أن المفوض الاستدعاء يتم التعرض للتكلفة مرة واحدة فقط في كل نطاق، بدلاً من مرة واحدة في كل عنصر. يلي مثال يوضح النمط الأساسي.
' Source must be array or IList.
Dim source = Enumerable.Range(0, 100000).ToArray()
' Partition the entire source array.
' Let the partitioner size the ranges.
Dim rangePartitioner = Partitioner.Create(0, source.Length)
Dim results(source.Length - 1) As Double
' Loop over the partitions in parallel. The Sub is invoked
' once per partition.
Parallel.ForEach(rangePartitioner, Sub(range, loopState)
' Loop over each range element without a delegate invocation.
For i As Integer = range.Item1 To range.Item2 - 1
results(i) = source(i) * Math.PI
Next
End Sub)
// Source must be array or IList.
var source = Enumerable.Range(0, 100000).ToArray();
// Partition the entire source array.
var rangePartitioner = Partitioner.Create(0, source.Length);
double[] results = new double[source.Length];
// Loop over the partitions in parallel.
Parallel.ForEach(rangePartitioner, (range, loopState) =>
{
// Loop over each range element without a delegate invocation.
for (int i = range.Item1; i < range.Item2; i++)
{
results[i] = source[i] * Math.PI;
}
});
كل مؤشر ترابط في تكرار حلقي يتلقى الخاص به Tuple<T1, T2>التي تحتوي على قيم الفهرس البداية والنهاية في منخفض-range المحدد. الداخلي forتكرار يستخدم fromInclusiveو toExclusiveقيم إلى تكرار حلقي عبر الصفيف أو IListمباشرة.
واحد Createالتحميلات الزائدة يسمح لك بتعيين الحجم الأقسام، و رقم من الأقسام. Th هو حالات التحميل التي يمكن استخدامها في وحدات سيناريو الموقع العمل كل عنصر هو منخفضة الموقع أن استدعاء أسلوب ظاهري واحد حتى كل عنصر له تأثير يمكن ملاحظته تشغيل الأداء.
Partitioners مخصص
في بعض وحدات السيناريو، قد يكون worthwhile أو حتى المطلوبة إلى تنفيذ partitioner الخاصة بك. ل مثال، قد يكون لديك فئة مجموعة مخصصة يمكنك تقسيم بفعالية أكبر أكثر من يمكن partitioners الافتراضية، استناداً إلى معرفتك بالبنية الداخلية للفئة. أو، قد تحتاج إلى إنشاء partitiتشغيلs نطاق أحجام متباينة تعتمد تشغيل معرفتك بكيفية lتشغيلg مدة عناصر العملية في locatiتشغيلs مختلفة في collecti مصدر تشغيل.
لإنشاء partitioner مخصصة أساسية، اشتقاق فئة من System.Collections.Concurrent.Partitioner<TSource>وتجاوز ظاهري الطرق، كما هو موضح في الجدول التالي.
يتم استدعاء هذا الأسلوب مرة واحدة عن طريق عملية جزئية الرئيسية وإرجاع IList(IEnumerator(TSource)). يمكن استدعاء كل مؤشر ترابط العامل في تكرار حلقي أو الاستعلام GetEnumeratorتشغيل القائمة إلى استرداد IEnumerator<T>تشغيل قسم مختلف. |
|
trueفي حالة تطبيق GetDynamicPartitions، وإلا، بإرجاع false. |
|
إذا SupportsDynamicPartitionsهو true, th هو يمكن استدعاء الأسلوب اختيارياً بدلاً من GetPartitions. |
إذا كان يجب أن تكون نتائج sortable أو تتطلب مفهرسة للوصول إلى العناصر، ينحدر من System.Collections.Concurrent.OrderablePartitioner<TSource>وتجاوز الأساليب الظاهرية الخاصة به كـ هو موضح في الجدول التالي.
Th هو أسلوب هو استدعاؤها مرة واحدة بواسطة مؤشر ترابط الرئيسي ويرجع IList(IEnumerator(TSource)). يمكن استدعاء كل مؤشر ترابط العامل في تكرار حلقي أو الاستعلام GetEnumeratorتشغيل القائمة إلى استرداد IEnumerator<T>تشغيل قسم مختلف. |
|
إرجاع trueفي حالة تطبيق GetDynamicPartitions؛ وإلا، خطأ. |
|
بشكل عام، وهذا يستدعي فقط GetOrderableDynamicPartitions. |
|
إذا SupportsDynamicPartitionsهو true, th هو يمكن استدعاء الأسلوب اختيارياً بدلاً من GetPartitions. |
يوفر الجدول التالي لمزيد من التفاصيل حول ثلاثة أنواع من تنفيذ partitioners OrderablePartitioner<TSource>فئة. موازنة تحميل
أسلوب/الخاصية |
IList/صفيفة بدون موازنة تحميل |
IList/صفيفة مع موازنة تحميل |
IEnumerable |
---|---|---|---|
يستخدم نطاق التقسيم |
يستخدم تقسيم القطعة الأمثل لقوائم ل partitionCount المحدد |
يستخدم تقسيم المجموعة بإنشاء ثابت رقم أقسام. |
|
استثناء throws معتمدة على عدم |
يستخدم تقسيم المجموعة لقوائم و الأقسام ديناميكي |
يستخدم تقسيم المجموعة بإنشاء حيوي رقم أقسام. |
|
يرجع true. |
يرجع true. |
يرجع true. |
|
يرجع true. |
يرجع false. |
يرجع false. |
|
يرجع true. |
يرجع true. |
يرجع true. |
|
يرجع false. |
يرجع true. |
يرجع true. |
ديناميكي أقسام
إذا كنت ترغب في partitioner إلى يمكن استخدامها في ForEachالأسلوب، فيجب أن تكون قادراً على إلى بإرجاع عدد حيوية من الأقسام. وهذا يعني أن partitioner يمكنه تزويد أوراق العداد لجديد قسم عند طلب في أي وقت أثناء تنفيذ تكرار حلقي. بشكل أساسي، كلما الحلقة بإضافة مهمة جديدة بالتوازي، فإنه يطلب قسم جديد لهذه المهمة. إذا كنت تطلب بيانات التي سيتم orderable، ينحدر من System.Collections.Concurrent.OrderablePartitioner<TSource>حيث أن كل عنصر في كل قسم هو تعيين فهرس فريد.
للحصول على مزيد من المعلومات، و سبيل المثال، راجع كيفية القيام بما يلي: أقسام حيوية لتنفيذ.
عقد ل Partitioners
عندما تقوم بتطبيق cusإلىm partitioner، اتبع هذه الإرشادات إلى المساعدة على التأكد من صحة تفاعل مع PLINQ و ForEachفي TPL:
إذا GetPartitionsالمسماة بوسيطة صفر أو أقل من أجل partitionsCount، رمى ArgumentOutOfRangeException. على الرغم من عدم سيتم تمرير PLINQ و TPL في partitionCountتساوي 0، وبالرغم من ذلك نوصي حماية ضد احتمال.
GetPartitionsوGetOrderablePartitionsيجب أن تعود دائماًpartitionsCountعدد الأقسام. إذا partitioner نفدت بيانات و لا يمكن إنشاء العديد من الأقسام كما هو مطلوب، ثم يجب إرجاع الأسلوب العداد فارغ لكل من الأقسام المتبقية. قم بخلاف ذلك، TPL وبصورة PLINQ سيتم طرح InvalidOperationException.
GetPartitions,GetOrderablePartitions,GetDynamicPartitions، وGetOrderableDynamicPartitionsلا يجب أن ترجعnull (Nothing في Visual أساسى). إذا كانت، PLINQ/سوف TPL رمى InvalidOperationException.
الأساليب التي تقوم بإرجاع الأقسام يجب دوماً بإرجاع الأقسام التي يمكن تماما وفريد تعداد بيانات المصدر. يجب أن يكون هناك لا التكرار في مصدر بيانات أو عناصر المتخطاة إلا إذا كان المطلوب بشكل خاص بواسطة تصميم partitioner. إذا كان ترتيب هو قاعدة هو لا يتبع، ثم قد يكون ترتيب الإخراج مشفر.
يجب أن getters المنطقية التالية بإرجاع قيم التالية دائماً بشكل دقيق حيث ترتيب الإخراج هو غير مشفر:
KeysOrderedInEachPartition: كل قسم بإرجاع عناصر متزايدة للمفتاح الفهارس.
KeysOrderedAcrossPartitions: ل الجميع الأقسام التي يتم إرجاعها، الفهارس الأساسية في القسمi أعلى من الفهارس الأساسية في قسم i -1.
KeysNormalized: monotonically يتم زيادة الجميع الفهارس المفاتيح بدون فراغات، بدءاً من الصفر.
يجب أن تكون الجميع الفهارس فريدة. قد لا تتوفر الفهارس مكررة. إذا كان ترتيب هو قاعدة هو لا يتبع، ثم قد يكون ترتيب الإخراج مشفر.
يجب أن تكون الجميع الفهارس غير سالب. إذا كان ترتيب هو قاعدة هو لا يتبع، ثم PLINQ/TPL قد يقوم بطرح استثناء.
راجع أيضًا:
المبادئ
البرمجة المتوازية في .NET Framework