Aracılığıyla paylaş


Nasıl yapılır: Basit bir Parallel.For Döngüsü Yazma

Bu konu, yöntemini gösteren Parallel.For iki örnek içerir. birincisi yöntem aşırı yüklemesini Parallel.For(Int64, Int64, Action<Int64>) , ikincisi ise yöntemin Parallel.For(Int32, Int32, Action<Int32>) en basit iki aşırı yüklemesini Parallel.For kullanır. Döngü iptal Parallel.For etmeniz, döngü yinelemelerini ayırmanız veya herhangi bir iş parçacığı yerel durumunu korumanız gerekmediğinde yönteminin bu iki aşırı yüklemesini kullanabilirsiniz.

Not

TPL'de temsilciler tanımlamak için bu belgede lambda ifadeleri kullanılır. C# veya Visual Basic'teki lambda ifadelerini bilmiyorsanız bkz . PLINQ ve TPL'de Lambda İfadeleri.

İlk örnek, tek bir dizindeki dosyaların boyutunu hesaplar. İkincisi iki matrisin çarpımını hesaplar.

Dizin boyutu örneği

Bu örnek, bir dizindeki dosyaların toplam boyutunu hesaplayan basit bir komut satırı yardımcı programıdır. Bağımsız değişken olarak tek bir dizin yolu bekler ve bu dizindeki dosyaların sayısını ve toplam boyutunu bildirir. Dizinin var olduğunu doğruladıktan sonra, dizindeki Parallel.For dosyaları listelemek ve dosya boyutlarını belirlemek için yöntemini kullanır. Ardından her dosya boyutu değişkenine totalSize eklenir. Eklemenin, öğesini çağırarak Interlocked.Add gerçekleştirildiğini ve böylece toplamanın atomik bir işlem olarak gerçekleştirildiğini unutmayın. Aksi takdirde, birden çok görev değişkeni aynı anda güncelleştirmeyi totalSize deneyebilir.

using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;

public class Example
{
   public static void Main(string[] args)
   {
      long totalSize = 0;
      
      if (args.Length == 0) {
         Console.WriteLine("There are no command line arguments.");
         return;
      }
      if (! Directory.Exists(args[0])) {
         Console.WriteLine("The directory does not exist.");
         return;
      }

      String[] files = Directory.GetFiles(args[0]);
      Parallel.For(0, files.Length,
                   index => { FileInfo fi = new FileInfo(files[index]);
                              long size = fi.Length;
                              Interlocked.Add(ref totalSize, size);
                   } );
      Console.WriteLine("Directory '{0}':", args[0]);
      Console.WriteLine("{0:N0} files, {1:N0} bytes", files.Length, totalSize);
   }
}
// The example displaysoutput like the following:
//       Directory 'c:\windows\':
//       32 files, 6,587,222 bytes
Imports System.IO
Imports System.Threading
Imports System.Threading.Tasks

Module Example
    Public Sub Main()
        Dim totalSize As Long = 0

        Dim args() As String = Environment.GetCommandLineArgs()
        If args.Length = 1 Then
            Console.WriteLine("There are no command line arguments.")
            Return
        End If
        If Not Directory.Exists(args(1))
            Console.WriteLine("The directory does not exist.")
            Return
        End If

        Dim files() As String = Directory.GetFiles(args(1))
        Parallel.For(0, files.Length,
                     Sub(index As Integer)
                         Dim fi As New FileInfo(files(index))
                         Dim size As Long = fi.Length
                         Interlocked.Add(totalSize, size)
                     End Sub)
        Console.WriteLine("Directory '{0}':", args(1))
        Console.WriteLine("{0:N0} files, {1:N0} bytes", files.Length, totalSize)
    End Sub
End Module
' The example displays output like the following:
'       Directory 'c:\windows\':
'       32 files, 6,587,222 bytes

Matris ve kronometre örneği

Bu örnek, iki matrisin çarpımını hesaplamak için yöntemini kullanır Parallel.For . Ayrıca paralel döngünün System.Diagnostics.Stopwatch performansını paralel olmayan bir döngüyle karşılaştırmak için sınıfının nasıl kullanılacağını gösterir. Büyük bir çıkış hacmi oluşturabildiğinden, örneğin çıkışın bir dosyaya yeniden yönlendirilmesine izin verdiğine dikkat edin.

using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Threading.Tasks;

class MultiplyMatrices
{
    #region Sequential_Loop
    static void MultiplyMatricesSequential(double[,] matA, double[,] matB,
                                            double[,] result)
    {
        int matACols = matA.GetLength(1);
        int matBCols = matB.GetLength(1);
        int matARows = matA.GetLength(0);

        for (int i = 0; i < matARows; i++)
        {
            for (int j = 0; j < matBCols; j++)
            {
                double temp = 0;
                for (int k = 0; k < matACols; k++)
                {
                    temp += matA[i, k] * matB[k, j];
                }
                result[i, j] += temp;
            }
        }
    }
    #endregion

    #region Parallel_Loop
    static void MultiplyMatricesParallel(double[,] matA, double[,] matB, double[,] result)
    {
        int matACols = matA.GetLength(1);
        int matBCols = matB.GetLength(1);
        int matARows = matA.GetLength(0);

        // A basic matrix multiplication.
        // Parallelize the outer loop to partition the source array by rows.
        Parallel.For(0, matARows, i =>
        {
            for (int j = 0; j < matBCols; j++)
            {
                double temp = 0;
                for (int k = 0; k < matACols; k++)
                {
                    temp += matA[i, k] * matB[k, j];
                }
                result[i, j] = temp;
            }
        }); // Parallel.For
    }
    #endregion

    #region Main
    static void Main(string[] args)
    {
        // Set up matrices. Use small values to better view
        // result matrix. Increase the counts to see greater
        // speedup in the parallel loop vs. the sequential loop.
        int colCount = 180;
        int rowCount = 2000;
        int colCount2 = 270;
        double[,] m1 = InitializeMatrix(rowCount, colCount);
        double[,] m2 = InitializeMatrix(colCount, colCount2);
        double[,] result = new double[rowCount, colCount2];

        // First do the sequential version.
        Console.Error.WriteLine("Executing sequential loop...");
        Stopwatch stopwatch = new Stopwatch();
        stopwatch.Start();

        MultiplyMatricesSequential(m1, m2, result);
        stopwatch.Stop();
        Console.Error.WriteLine("Sequential loop time in milliseconds: {0}",
                                stopwatch.ElapsedMilliseconds);

        // For the skeptics.
        OfferToPrint(rowCount, colCount2, result);

        // Reset timer and results matrix.
        stopwatch.Reset();
        result = new double[rowCount, colCount2];

        // Do the parallel loop.
        Console.Error.WriteLine("Executing parallel loop...");
        stopwatch.Start();
        MultiplyMatricesParallel(m1, m2, result);
        stopwatch.Stop();
        Console.Error.WriteLine("Parallel loop time in milliseconds: {0}",
                                stopwatch.ElapsedMilliseconds);
        OfferToPrint(rowCount, colCount2, result);

        // Keep the console window open in debug mode.
        Console.Error.WriteLine("Press any key to exit.");
        Console.ReadKey();
    }
    #endregion

    #region Helper_Methods
    static double[,] InitializeMatrix(int rows, int cols)
    {
        double[,] matrix = new double[rows, cols];

        Random r = new Random();
        for (int i = 0; i < rows; i++)
        {
            for (int j = 0; j < cols; j++)
            {
                matrix[i, j] = r.Next(100);
            }
        }
        return matrix;
    }

    private static void OfferToPrint(int rowCount, int colCount, double[,] matrix)
    {
        Console.Error.Write("Computation complete. Print results (y/n)? ");
        char c = Console.ReadKey(true).KeyChar;
        Console.Error.WriteLine(c);
        if (Char.ToUpperInvariant(c) == 'Y')
        {
            if (!Console.IsOutputRedirected &&
                RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
            {
                Console.WindowWidth = 180;
            }

            Console.WriteLine();
            for (int x = 0; x < rowCount; x++)
            {
                Console.WriteLine("ROW {0}: ", x);
                for (int y = 0; y < colCount; y++)
                {
                    Console.Write("{0:#.##} ", matrix[x, y]);
                }
                Console.WriteLine();
            }
        }
    }
    #endregion
}
Imports System.Diagnostics
Imports System.Runtime.InteropServices
Imports System.Threading.Tasks

Module MultiplyMatrices
#Region "Sequential_Loop"
    Sub MultiplyMatricesSequential(ByVal matA As Double(,), ByVal matB As Double(,), ByVal result As Double(,))
        Dim matACols As Integer = matA.GetLength(1)
        Dim matBCols As Integer = matB.GetLength(1)
        Dim matARows As Integer = matA.GetLength(0)

        For i As Integer = 0 To matARows - 1
            For j As Integer = 0 To matBCols - 1
                Dim temp As Double = 0
                For k As Integer = 0 To matACols - 1
                    temp += matA(i, k) * matB(k, j)
                Next
                result(i, j) += temp
            Next
        Next
    End Sub
#End Region

#Region "Parallel_Loop"
    Private Sub MultiplyMatricesParallel(ByVal matA As Double(,), ByVal matB As Double(,), ByVal result As Double(,))
        Dim matACols As Integer = matA.GetLength(1)
        Dim matBCols As Integer = matB.GetLength(1)
        Dim matARows As Integer = matA.GetLength(0)

        ' A basic matrix multiplication.
        ' Parallelize the outer loop to partition the source array by rows.
        Parallel.For(0, matARows, Sub(i)
                                      For j As Integer = 0 To matBCols - 1
                                          Dim temp As Double = 0
                                          For k As Integer = 0 To matACols - 1
                                              temp += matA(i, k) * matB(k, j)
                                          Next
                                          result(i, j) += temp
                                      Next
                                  End Sub)
    End Sub
#End Region

#Region "Main"
    Sub Main(ByVal args As String())
        ' Set up matrices. Use small values to better view 
        ' result matrix. Increase the counts to see greater 
        ' speedup in the parallel loop vs. the sequential loop.
        Dim colCount As Integer = 180
        Dim rowCount As Integer = 2000
        Dim colCount2 As Integer = 270
        Dim m1 As Double(,) = InitializeMatrix(rowCount, colCount)
        Dim m2 As Double(,) = InitializeMatrix(colCount, colCount2)
        Dim result As Double(,) = New Double(rowCount - 1, colCount2 - 1) {}

        ' First do the sequential version.
        Console.Error.WriteLine("Executing sequential loop...")
        Dim stopwatch As New Stopwatch()
        stopwatch.Start()

        MultiplyMatricesSequential(m1, m2, result)
        stopwatch.[Stop]()
        Console.Error.WriteLine("Sequential loop time in milliseconds: {0}", stopwatch.ElapsedMilliseconds)

        ' For the skeptics.
        OfferToPrint(rowCount, colCount2, result)

        ' Reset timer and results matrix. 
        stopwatch.Reset()
        result = New Double(rowCount - 1, colCount2 - 1) {}

        ' Do the parallel loop.
        Console.Error.WriteLine("Executing parallel loop...")
        stopwatch.Start()
        MultiplyMatricesParallel(m1, m2, result)
        stopwatch.[Stop]()
        Console.Error.WriteLine("Parallel loop time in milliseconds: {0}", stopwatch.ElapsedMilliseconds)
        OfferToPrint(rowCount, colCount2, result)

        ' Keep the console window open in debug mode.
        Console.Error.WriteLine("Press any key to exit.")
        Console.ReadKey()
    End Sub
#End Region

#Region "Helper_Methods"
    Function InitializeMatrix(ByVal rows As Integer, ByVal cols As Integer) As Double(,)
        Dim matrix As Double(,) = New Double(rows - 1, cols - 1) {}

        Dim r As New Random()
        For i As Integer = 0 To rows - 1
            For j As Integer = 0 To cols - 1
                matrix(i, j) = r.[Next](100)
            Next
        Next
        Return matrix
    End Function

    Sub OfferToPrint(ByVal rowCount As Integer, ByVal colCount As Integer, ByVal matrix As Double(,))
        Console.Error.Write("Computation complete. Display results (y/n)? ")
        Dim c As Char = Console.ReadKey(True).KeyChar
        Console.Error.WriteLine(c)
        If Char.ToUpperInvariant(c) = "Y"c Then
            If Not Console.IsOutputRedirected AndAlso
                RuntimeInformation.IsOSPlatform(OSPlatform.Windows) Then Console.WindowWidth = 168

            Console.WriteLine()
            For x As Integer = 0 To rowCount - 1
                Console.WriteLine("ROW {0}: ", x)
                For y As Integer = 0 To colCount - 1
                    Console.Write("{0:#.##} ", matrix(x, y))
                Next
                Console.WriteLine()
            Next
        End If
    End Sub
#End Region
End Module

Döngüler de dahil olmak üzere herhangi bir kodu paralelleştirirken önemli bir hedef, paralel işleme ek yükünün performans avantajlarını ortadan kaldırma noktasına fazla paralelleştirmeden işlemcileri mümkün olduğunca kullanmaktır. Bu özel örnekte, iç döngüde gerçekleştirilen çok fazla iş olmadığından yalnızca dış döngü paralelleştirilmiştir. Az miktarda çalışma ve istenmeyen önbellek efektlerinin birleşimi, iç içe paralel döngülerde performans düşüşüyle sonuçlanabilir. Bu nedenle, dış döngünün paralelleştirilmesi, çoğu sistemde eşzamanlılığın avantajlarını en üst düzeye çıkarmanın en iyi yoludur.

Temsilci

Bu aşırı yüklemenin For üçüncü parametresi, C# veya Action(Of Integer) Visual Basic'te türünde Action<int> bir temsilcidir. Sıfır Action , bir veya on altı tür parametresi olan bir temsilci her zaman void döndürür. Visual Basic'te bir öğesinin Action davranışı ile Subtanımlanır. Örnek, temsilciyi oluşturmak için lambda ifadesi kullanır, ancak temsilciyi başka şekillerde de oluşturabilirsiniz. Daha fazla bilgi için bkz . PLINQ ve TPL'de Lambda İfadeleri.

Yineleme Değeri

Temsilci, değeri geçerli yineleme olan tek bir giriş parametresi alır. Bu yineleme değeri çalışma zamanı tarafından sağlanır ve başlangıç değeri, geçerli iş parçacığında işlenen kaynağın segmentindeki (bölüm) ilk öğenin dizinidir.

Eşzamanlılık düzeyi üzerinde daha fazla denetime ihtiyacınız varsa, aşağıdaki gibi bir System.Threading.Tasks.ParallelOptions giriş parametresi alan aşırı yüklemelerden birini kullanın: Parallel.For(Int32, Int32, ParallelOptions, Action<Int32,ParallelLoopState>).

Dönüş Değeri ve Özel Durum İşleme

For döndürür, tüm iş parçacıkları tamamlandığında bir System.Threading.Tasks.ParallelLoopResult nesne kullanır. Döngü yinelemesini el ile durdurduğunuzda veya kırdığınızda bu dönüş değeri kullanışlıdır, çünkü ParallelLoopResult tamamlanmaya çalışan son yineleme gibi bilgileri depolar. İş parçacıklarından birinde bir veya daha fazla özel durum oluşursa, bir System.AggregateException oluşturulur.

Bu örnekteki kodda değerinin dönüş değeri For kullanılmaz.

Analiz ve Performans

Performans Sihirbazı'nı kullanarak bilgisayarınızda CPU kullanımını görüntüleyebilirsiniz. Deneme olarak, matrislerdeki sütun ve satır sayısını artırın. Matrisler ne kadar büyükse, hesaplamanın paralel ve sıralı sürümleri arasındaki performans farkı da o kadar artar. Matris küçük olduğunda, paralel döngünün ayarlanmasındaki ek yük nedeniyle sıralı sürüm daha hızlı çalışır.

Konsol veya Dosya Sistemi gibi paylaşılan kaynaklara yapılan zaman uyumlu çağrılar paralel döngünün performansını önemli ölçüde düşürür. Performansı ölçerken döngü içinde gibi Console.WriteLine çağrılardan kaçınmaya çalışın.

Kodu Derleme

Bu kodu kopyalayıp bir Visual Studio projesine yapıştırın.

Ayrıca bkz.