Shiny: Performance-Tuning mit future & promises – Die Theorie

Im vorangegangenen Artikel zum Thema Shiny teilten wir unsere Erfahrungen mit dem Load-Testing und der horizontalen Skalierung von Apps. Dabei zeigten wir die Gestaltung eines Prozesses vom Proof-of-Concept zur firmenweiten genutzten Applikation.

Im zweiten Teil der Blogserie wird der Fokus auf die R-Pakete future & promises gelegt, welche für Optimierungen innerhalb der App verwendet werden. Diese erleichtern die drastische Reduzierung von potenziellen Wartezeiten für den Nutzer.

Um diese Thematik so ausführlich wie möglich behandeln zu können, bezieht sich der erste Teil des Artikels auf die Theorie hinter der Arbeitsweise von Shiny und den Paketen future & promises. Außerdem wird die asynchrone Programmierung erklärt, auf deren Techniken die Paketfunktionen beruhen. Im zweiten Teil wird am Praxisbeispiel gezeigt, wie sich die Ideen zur Optimierung umsetzen lassen.

Shiny Workflow – Verbindungen und R-Prozesse

Die folgende Abbildung zeigt zunächst das Verfahren, mit dem der Nutzerzugriff auf eine Shiny-App durchgeführt wird:

Für jeden Nutzerzugriff auf eine Shiny-App entscheidet der Server, ob der Nutzer zu einem bestehendem R-Prozess hinzugefügt oder ein neuer R-Prozess gestartet wird, zu dem dieser verbunden wird. Wie genau der Server die Entscheidung handhabt, kann dabei für jede Applikation über gewisse Tuning-Parameter separat geregelt werden.

R ist single-threaded, d.h. alle Befehle innerhalb eines Prozesses werden sequenziell ausgeführt (nicht parallel). Aus diesem Grund kann der orange hinterlegte Prozess für Verzögerungen in der Ausführung der App sorgen. Dies ist jedoch abhängig von der Verwendung der anderen zum Prozess verbundenen Nutzern der Applikation. Folgende Grafik verdeutlicht diese Verwendung.

 

 

Da User 2 auf denselben R-Prozess verbunden wird wie User 1, muss er bis zum Abschluss des von User 1 gestarteten Tasks warten, um die App zu nutzen. Da es meistens unpraktikabel (bzw. unmöglich) ist, jedem Nutzer eine eigene R-Session für die Shiny-App zuzuweisen, führt dies zu den oben angesprochenen Möglichkeiten, welche mit future & promises realisiert und im Folgenden näher betrachten werden.

future & promises – Asynchrone Programmierung in R

Wer bereits Erfahrung mit anderen Programmiersprachen sammeln konnte, dem wird der Begriff der „asynchronen Programmierung“ vermutlich in der ein oder anderen Form schon einmal begegnet sein. Die Idee dahinter ist simpel: Aus einer Liste aus Aufgaben eines Prozesses wird eine Teilliste von aufwändigen Aufgaben (sog. „prozessblockende Aufgaben“), in beliebiger Form, ausgelagert, um den Ausgangsprozess reaktiv zu halten. In unserem Beispiel wäre eine dieser „Prozessblockenden Aufgaben“ bspw. der von User 1 gestartete Machine-Learning/Datenbank-Task. Die R-Pakete future & promises implementieren dieses Programmierparadigma für R, welches wie bereits erwähnt nativ nur die sequenzielle („synchrone“) Programmierung erlaubt. Der Workflow ist dabei in unterschiedliche Klassen (sog. „Pläne“) unterteilt:

Der Standardplan spiegelt die “normale” Arbeitsweise von R wieder. Prozesse führen die Aufgaben nacheinander aus, d.h. spätere Aufgaben müssen auf die Fertigstellung ihrer Vorgänger warten.

Die Aufgaben 1 & 2 aus den oben abgebildeten asynchronen Plänen werden jeweils auf Neben/Sub-Prozesse ausgelagert, sodass der Hauptprozess frei bleibt und Aufgabe 3 bearbeitet werden kann. Der Unterschied ist dabei, dass „Multisession“ zwei neue R-Prozesse startet, auf denen die Aufgaben ausgeführt werden. Multicore hingegen zweigt den Hauptprozess in zwei Sub-Prozesse ab, was nur unter Linux möglich ist. Weitere Pläne erlauben bspw. die Auslagerung auf verteilte Systeme.

 

 

Optimierung: Inter-Session vs. Intra-Session

Um die Schritte im folgenden Artikel zur Praxis besser nachvollziehen zu können, werden zwei Arten der Optimierung vorgestellt, in die sich der Begriff des InApp-Performance-Tunings unterteilen lässt. Diese beeinflussen maßgeblich den Optimierungsprozesses und sind daher für ein gemeinsames Verständnis essenziell.

Wie in der nachstehenden Grafik zu sehen ist, unterteilt sich das Performance Tuning in Inter-Session- und Intra-Session-Optimierung. Wird eine App in Hinblick auf Intra Session Performance optimiert, wird versucht die Wartezeit für den Nutzer zu verringern, der die aktuelle Applikation ausführt.

Die Pakete future & promises sind im Shiny-Kontext auf einer Inter-Session-Optimierung ausgelegt. Diese fokussiert sich darauf, die App für alle anderen Nutzer reaktiv zu halten, die zeitgleich auf die App zugreifen. Der Nutzer, der die Aufgabe startet, muss dennoch auf den Abschluss der Aufgabe warten, um die App weiter verwenden zu können.

Fazit & Ausblick

Die Theorie hinter der Arbeitsweise von Shiny und den asynchronen Programmierparadigmen stellt einen wichtigen Schritt zum Verständnis der Arbeitsweise von future & promises dar. Weiterhin schärfen tiefere Einblicke in die Architektur der Systeme den Blick hinsichtlich nötiger Optimierungsprozesse und an welchen Stellen hierfür angesetzt werden kann. Im folgenden Teil dieses Artikels werden wir sehen, wie sich die Fülle an Informationen über eine intuitive Syntax elegant in Form gießen lässt und wie dies die Entwicklung einer App beeinflussen kann.

Wir sind die Experten für die Entwicklung von Shiny-Apps und den Aufbau produktiver IT-Infrastrukturen im Data-Science-Kontext. Sie haben Fragen zu diesen Themen? Erfahren Sie mehr.