Montag, 14. Dezember 2015

Akka.NET Adventskalender – Tür 14

Wenn Aktoren im Bündel auftreten 

Bislang hatten wir von jedem Aktor, den wir geschaffen haben, immer nur eine Instanz am Laufen. Damit konnten wir Aufgaben auf mehrere "Schultern" verteilt abarbeiten lassen anstelle alles von einem Aktor erledigen zu lassen. Jeder Aktor war dabei komplett für jeweils eine Teilaufgabe zuständig. Was passiert nun, wenn wir etwa eine CPU-intensive Aufgabe lösen wollen? Schaffen wir es die Arbeit ebenfalls unter mehreren Aktoren aufzuteilen? Damit werden wir uns diese Woche befassen.

Damit wir ein Gefühl für die Vorteile der parallelen Bearbeitung bekommen, sehen wir uns von einer Aufgabenstellung zunächst die iterative Version an und haben dann einen Ausgangspunkt für Verbesserungen.

Bei der Scala-Version von Akka habe ich ein nettes Tutorial gefunden, in welchem Pi berechnet wird. Das ist eine verständliche aber auch leicht parallelsierbare Aufgabe. Also legen wir los und sehen zu wie wir die Aufgabe mit konventionellen Mitteln vermutlich gelöst hätten:

using System;
using System.Diagnostics;

namespace PiIterative
{
    class MainClass
    {
        public static void Main(string[] args)
        {
            Console.WriteLine("Calculating PI -- please be patient...");

            const int million   = 1000 * 1000;
            const int rangeFrom = 0;
            const int rangeTo   = 200 * million;

            var stopWatch = Stopwatch.StartNew();

            double sum = 0;
            for (int n = rangeFrom; n <= rangeTo; n++)
                sum += Math.Pow(-1, n) / (2 * n + 1);

            stopWatch.Stop();

            Console.WriteLine("Pi = {0}, Elapsed: {1:F1}s", 
                4 * sum, 
                stopWatch.ElapsedMilliseconds / 1000.0);
        }
    }
}

Mein MacBook Air von 2011 braucht für diese Aufgabe unter Mono etwa 8 Sekunden. Wenn Deine Maschine schneller ist, erhöhe einfach die Anzahl der Iterationen, damit wir später auch Steigerungen erleben können...

Wir Du siehst, ist dieser Algorithmus relativ einfach und besteht lediglich aus ein paar Millionen einzelner Schritte, die es auszuführen gilt. Jeder einzelne Schleifendurchlauf ist sicher nicht der Rede Wert, aber bei 200 Millionen oder mehr Wiederholungen summiert sich die Rechenzeit eben doch.

Wollen wir diese Aufgabe parallelisieren, müssen wir einen geeigneten Kompromiss finden. 200 Millionen Aktoren einzusetzen wäre sicher unklug. Allein die Instantiierungs-Zeit dafür würde den Rechenaufwand eines Schleifendurchlaufs weit übersteigen. An den Speicherbedarf mag ich gar nicht denken. Also macht es vermutlich Sinn, mit relativ wenigen Aktoren zu arbeiten, die dann große (aber eben nicht die vollständige Menge) Arbeitspakete erhalten. Da wir nur den Anfangs- und Endwert der Schleifendurchläufe benötigen, sind die Aufgaben-Pakete einfach zu transportieren. Anschließend hat jeder Aktor ein wenig zu tun und liefert seinen Teil an der Gesamtsumme ab. Irgendwer muss dann noch die Teilsummen aufaddieren und Pi ist berechnet.

Im Detail sehen wir uns das morgen an.

Keine Kommentare:

Kommentar veröffentlichen