Einige Überlegungen zum gleichzeitigen Rechnen in R für "Unternehmens" -Aufgaben

Paralleles oder verteiltes Rechnen ist an sich keine sehr triviale Sache. Und die Entwicklungsumgebung sollte unterstützen, und der DS-Spezialist sollte über die Fähigkeiten verfügen, paralleles Rechnen durchzuführen, und die Aufgabe sollte auf eine Form reduziert werden, die in Teile unterteilt werden kann, falls vorhanden. Mit einem kompetenten Ansatz können Sie die Lösung des Problems mit einem Single-Threaded-R erheblich beschleunigen, wenn Sie mindestens einen Multicore-Prozessor haben (und fast jeder hat ihn jetzt), der an die theoretische Beschleunigungsgrenze angepasst ist, die durch das Amdalsche Gesetz bestimmt wird . In einigen Fällen kann es jedoch sogar umgangen werden.


Es ist eine Fortsetzung früherer Veröffentlichungen .


Typischer Ansatz


Wenn ein Analyst (DS-Spezialist, Entwickler oder wählen Sie einen geeigneten Namen für sich selbst) versucht, die Aufgabe innerhalb eines Computers zu beschleunigen, und in der Regel vom Single-Thread- in den Multithread-Modus übergeht, führt er dies auf eine Art und Weise durch. parApply , foreach\%dopar% usw. Sie können beispielsweise hier kompakt und verständlich sehen: „Parallelität in R“ . 3 Schritte:


  1. core-1 Thread machen
  2. mit foreach ausführen,
  3. Sammeln Sie die Antworten und erhalten Sie das Ergebnis.

Für typische Computeraufgaben, die 100% der CPU belegen und keine große Menge an Eingabeinformationen übertragen müssen, ist dies der richtige Ansatz. Der Hauptpunkt, der beachtet werden muss, ist die Bereitstellung der Protokollierung innerhalb der Threads, um den Prozess steuern zu können. Ohne Protokollierung geht der Flug auch ohne Instrumente.


Im Fall von "Unternehmens" -Aufgaben, wenn sie parallelisiert werden, treten viele zusätzliche methodische Schwierigkeiten auf, die die Wirkung des obigen einfachen Ansatzes erheblich verringern:


  • mögliche starke Unwucht der Last auf den Strömungen;
  • Die CPU-Leistungsanforderungen innerhalb einer einzelnen Aufgabe können mit nur wenigen scharfen Bursts gerissen werden.
  • Jede einzelne Berechnung kann eine erhebliche Menge an Speicher für die Eingabe und Ausgabe von Ergebnissen erfordern, die ebenfalls von beträchtlicher Größe sind.
  • Als Teil einer separaten Aufgabe kann es eine Mischung zwischen Rechnen, Arbeiten mit der Festplatte und Abfragen externer Systeme geben.

Dies ist ein völlig typisches Szenario, in dem Sie als Teil des Prozesses einen umfangreichen Auftrag als Eingabe abrufen, Daten von der Festplatte lesen, einen großen Teil aus der Datenbank abrufen, nach externen Systemen fragen und auf eine Antwort von ihnen warten müssen (klassische REST-API-Anforderung) und dann N an den übergeordneten Prozess zurückgeben müssen Megabyte als Ergebnis.


Map-reduce nach Benutzern, Standorten, Dokumenten, IP-Adressen, Daten usw. (selbst hinzufügen). In den traurigsten Fällen kann die parallele Ausführung länger sein als Single-Threaded. Es können auch Probleme mit zu wenig Speicher auftreten. Ist alles weg? Überhaupt nicht.


Alternativer Weg


Betrachten wir die These, wie die Situation radikal verbessert werden kann. Gleichzeitig vergessen wir nicht, dass wir im Rahmen eines vollen Zoos leben. Produktive Schaltung unter *nix , DS-Laptops unter Win * nix \ MacOS, aber es ist notwendig, dass sie überall einheitlich funktioniert.


  1. Eine Mikrotask: Empfing eine Benutzereingabe, forderte eine Datenbank an, forderte 2 externe ICs über REST an, lud ein Verzeichnis von einer Festplatte herunter und analysierte es, führte eine Berechnung durch und speicherte das Ergebnis auf Festplatte \ Datenbank. Benutzer zum Beispiel 10^6 .
  2. Wir wenden uns der Verwendung des future Pakets und des universellen doFuture Adapters zu.
  3. Wenn separate Aufgaben so doFuture sind, dass innerhalb separater Aufgaben Prozessorzeit in geringem Umfang benötigt wird (wir warten auf Antworten von Systemen von Drittanbietern), können Sie mit doFuture von der doFuture Aufteilung zur Aufteilung in separate Prozesse in einer Zeile doFuture (Sie können die Startparameter in *nix in htop ). .
  4. Diese Prozesse können viel mehr als nur Kerne erstellt werden. Es findet kein Clinchen statt, da sich einzelne Prozesse die meiste Zeit im Standby-Modus befinden. Es wird jedoch notwendig sein, die optimale Anzahl von Prozessen basierend auf dem Zyklogramm eines typischen Verarbeitungsprozesses experimentell auszuwählen.

Ergebnis - Die ursprüngliche Aufgabe ist um ein Vielfaches schneller. Die Beschleunigung kann sogar größer sein als die Anzahl der verfügbaren Kerne.
Es gibt keinen bewussten Code, da die Hauptaufgabe der Veröffentlichung darin besteht, den Ansatz und eine hervorragende Familie future Pakete zu teilen.


PS


Es gibt ein paar kleine Nuancen, die ebenfalls verfolgt werden müssen:


  • Jeder Prozess belegt Speicher, einschließlich der empfangenen und zurückgegebenen Daten. Eine Erhöhung der Anzahl der Prozesse vervielfacht die Anforderungen an den verfügbaren RAM.
  • doFuture verwendet "Magie", um die Zusammensetzung der an den Prozess übertragenen Variablen und Pakete automatisch zu bestimmen. Sie sollten jedoch nicht alles von selbst loslassen, sondern es besser überprüfen.
  • In Prozessen gc explizite gc Steuerung und das explizite Löschen von Variablen mit rm nicht. Dies ist kein Allheilmittel und funktioniert möglicherweise nicht , aber die explizite Angabe gelöschter Objekte hilft.
  • Rufen Sie nach Abschluss der Berechnung den plan(sequential) . Dadurch werden alle Prozesse geschlossen und der von ihnen belegte Speicher freigegeben.
  • Wenn Sie eine große Datenmenge in den Prozess übertragen müssen, sollten Sie einen externen Speicher (Festplatte, Datenbank) verwenden. Vergessen Sie nicht, dass die Deskriptoren nicht übertragen werden können. Die Quelle muss innerhalb des Prozesses selbst geöffnet werden.

Vorherige Veröffentlichung - „Geschäftsprozesse in Unternehmen: Spekulation und Realität. Wir bringen Licht mit R " .

Source: https://habr.com/ru/post/de462469/


All Articles