So beschleunigen Sie die Arbeit mit der R-Sprach-API mithilfe von Parallel Computing mithilfe des Beispiels der Yandex.Direct-API (Teil 2)

Im letzten Artikel habe ich über Multithreading gesprochen und Beispiele für seine Implementierung in der R-Sprache bei der Arbeit mit der Yandex.Direct-API unter Verwendung der doParallel doSNOW , doParallel und foreach Konstrukt gegeben.


Dieser Artikel ist eine Fortsetzung, kann aber als autonomer Leitfaden für Multithreading in R angesehen werden. Ich wurde aufgefordert, ihn durch die im ersten Teil eingegangenen Kommentare (hier ein besonderer Dank an Alexey_mosc , SatCat , Ananiev_Genrih ) zu schreiben , in denen mir eine Reihe von Paketen gegeben wurden, die einen moderneren Ansatz darstellen Implementierungen von Multithreading in R, wir werden später darüber sprechen.


Multithreading


Inhalt



Herausforderung


Als Beispiel nehmen wir das in einer früheren Veröffentlichung betrachtete Problem, d.h. Sammeln Sie im Multithread-Modus eine Liste mit Keywords aus 4 Yandex.Direct-Werbekonten.


Um mit der Yandex.Direct-API zu arbeiten, verwenden wir das ryandexdirect Paket. Die offizielle Dokumentation dazu befindet sich auf dem Link , aber für die Implementierung der beschriebenen Aufgabe benötigen wir nur 2 Funktionen:


  • yadirAuth - Autorisierung in der Yandex.Direct-API;
  • yadirGetKeyWords - Laden Sie eine Liste mit Keywords von Anzeigenkonten herunter.

Es ist nicht nur so, dass ich mich für das Herunterladen von Schlüsselwörtern entschieden habe, sondern dass dies eine der langwierigsten Vorgänge in der Yandex.Direct-API ist. Zweitens ist in allen Konten die Anzahl der Schlüsselwörter unterschiedlich, daher ist die Zeit zum Ausführen dieses Vorgangs für jedes Konto sehr unterschiedlich, in unserem Fall von 1 bis 20 Sekunden.


Vorbereitung


Zunächst müssen Sie alle in diesem Artikel beschriebenen Pakete installieren. Dazu können Sie den folgenden Code verwenden.


Code 1: Installieren von Paketen
 #    install.packages("ryandexdirect") install.packages("tictoc") install.packages("rbenchmark") install.packages("dplyr") install.packages("purrr") install.packages("future") install.packages("promises") install.packages("furrr") install.packages("future.apply") 

Damit Ihnen Paketfunktionen zur Verfügung stehen, müssen Sie sie mit dem Befehl library . Der Einfachheit halber werde ich alle erforderlichen Pakete in jedem gegebenen Codebeispiel separat verbinden.


Wir erstellen einen Vektor, der aus Yandex.Direct-Anmeldungen besteht, von denen wir später Schlüsselwörter anfordern werden:


Code 2: Erstellen eines Anmeldevektors
 logins <- c("login1", "login2", "login3", "login4") 

Um mit der Yandex.Direct-API arbeiten zu können, müssen Sie zunächst die Autorisierung für jedes Konto durchlaufen. Dazu können Sie das folgende Design verwenden:


Code 3: Autorisierung in der Yandex.Direct-API
 lapply(logins, function(l) { yadirAuth(Login = l)}) 

Nach dem Ausführen des obigen Codes wird unter jedem Konto ein Browser zur Autorisierung geöffnet. Sie bestätigen die Erlaubnis von ryandexdirect , auf Ihre Werbematerialien zuzugreifen. Sie werden zu der Seite weitergeleitet, auf der Sie den Bestätigungscode kopieren müssen. Schließen Sie den Autorisierungsprozess ab, indem Sie es in die R-Konsole eingeben. Dieser Vorgang wird für jede Anmeldung wiederholt, die Sie beim Erstellen der Vektoranmeldungen angegeben haben.


Einige Benutzer sind während des Autorisierungsprozesses möglicherweise durch die Umleitung zu einer Ressource eines Drittanbieters verwirrt. Es besteht jedoch keine Gefahr für Ihr Konto. Ich habe dieses Thema im Artikel "Wie sicher es ist, R-Pakete für die Arbeit mit APIs für Werbesysteme zu verwenden" ausführlicher beschrieben.


Als nächstes werden wir einige Beispiele für die Implementierung der beschriebenen Aufgabe betrachten. Jedes davon beginnt mit einem Beispielcode und seiner weiteren Erklärung. Ich denke, diese Option ist für die Wahrnehmung am bequemsten.


Beispiel für eine serielle Verarbeitungslösung, Sapply-Funktion und Purrr-Paket



Im letzten Artikel habe ich eine Lösung am Beispiel der for Schleife angeführt. Da wir Multithreading mit dem foreach Paket in Betracht gezogen haben, dessen Syntax Schleifen ähnelt, war dieses Beispiel dort angemessen, obwohl die Verwendung von Schleifen von Benutzern von R nicht begrüßt wird.


Die Pakete, die wir in diesem Artikel betrachten werden, erinnern eher an die Funktionen der Apply-Familie in der Syntax. Daher werde ich ein Beispiel für eine Lösung im seriellen Modus geben, die sie verwendet.


sapply Funktion


Um die Ausführungszeit von Befehlen abzuschätzen, verwenden wir in jedem der betrachteten Ansätze das tictoc Paket.

Code 4: Beispiellösung im sequentiellen Modus mit Sapply-Funktion
 library(tictoc) library(dplyr) tic() #   kw.sapply <- sapply( logins, #  ,     function(x) #        #     { yadirGetKeyWords(Login = x) %>% mutate(login = x) }, simplify = FALSE #     ) toc() #   #       result.sapply <- do.call("rbind", kw.sapply) 

39.36 sec elapsed : 39.36 sec elapsed


Zunächst ist die Syntax der Funktionen der apply Familie nicht so einfach zu lesen wie die Syntax von Schleifen, aber tatsächlich ist alles recht einfach.


sapply(X, FUN)


Wo:


  • X - Ein Objekt, dessen Elemente wir bei jeder Iteration durchlaufen und nacheinander verwenden werden. In einer for Schleife sah es folgendermaßen aus: for(i in X) ;
  • FUN - Eine Funktion, bei der wir nacheinander jedes Element des Objekts X ersetzen. Wenn wir eine Analogie mit for , ist dies der Körper der Schleife.

In Codebeispiel 4 wird der zuvor erstellte Anmeldevektor an das X- Argument übergeben. Jedes Element des Anmeldevektors wird nacheinander als einziges Argument an die anonyme Funktionsfunktion function(x) { yadirGetKeyWords(Login = x) %>% mutate(login = x) } , die an das FUN- Argument übergeben wurde.


Das heißt, sapply führt die in FUN angegebene Funktion viermal aus, ersetzt sie nacheinander und gibt das Ergebnis in Form einer Liste (Objekt der Klassenliste) zurück, die aus vier Elementen besteht. Jedes Element ist eine Tabelle mit einer Liste von Schlüsselwörtern, die bei jeder Iteration vom Konto empfangen werden.


  1. yadirGetKeyWords(Login = "login1") %>% mutate(login = "login1")
  2. yadirGetKeyWords(Login = "login2") %>% mutate(login = "login2")
  3. yadirGetKeyWords(Login = "login3") %>% mutate(login = "login3")
  4. yadirGetKeyWords(Login = "login4") %>% mutate(login = "login4")

Das mit sapply erhaltene sapply hat folgende Struktur:


 summary(kw.sapply) 

  Length Class Mode login1 19 data.frame list login2 19 data.frame list login3 19 data.frame list login4 19 data.frame list 

Am Ende dieses Beispiels result.sapply <- do.call("rbind", kw.sapply) der Befehl result.sapply <- do.call("rbind", kw.sapply) alle 4 Elemente der kw.sapply- Liste in einem einzigen result.sapply- Frame.


 # A tibble: 6,804 x 1 result.sapply$Id $Keyword $AdGroupId $CampaignId $ServingStatus $State <dbl> <fct> <dbl> <int> <fct> <fct> 1 15164230566 ~ 3597453985 39351725 ELIGIBLE ON 2 15164230567  ~ 3597453985 39351725 ELIGIBLE ON 3 15164230568  ~ 3597453985 39351725 ELIGIBLE ON 4 15164230569 ~ 3597453985 39351725 ELIGIBLE ON 5 15164230570 ~ 3597453985 39351725 ELIGIBLE ON 6 15164230571  ~ 3597453985 39351725 ELIGIBLE ON 7 15164230572 ~ 3597453985 39351725 ELIGIBLE ON 8 15164230573  ~ 3597453985 39351725 ELIGIBLE ON 9 15164230574 ~ 3597453985 39351725 ELIGIBLE ON 10 15164230575 ~ 3597453985 39351725 ELIGIBLE ON # ... with 6,794 more rows, and 13 more variables: $Status <fct>, # $StrategyPriority <fct>, $StatisticsSearchImpressions <int>, # $StatisticsSearchClicks <int>, $StatisticsNetworkImpressions <int>, # $StatisticsNetworkClicks <lgl>, $UserParam1 <chr>, $UserParam2 <chr>, # $ProductivityValue <lgl>, $ProductivityReferences <lgl>, $Bid <dbl>, # $ContextBid <dbl>, $login <chr> 

Zusätzlich zu sapply umfasst die Funktionsfamilie *apply : apply , lapply , vapply , mapply und andere.


purrr Paket


Code 5: Beispiellösung mit den Funktionen des Purrr-Pakets
 library(purrr) library(dplyr) library(tictoc) tic() #   result.purrr <- map_df( logins, #  ,     ~ #   function(.x) { yadirGetKeyWords(Login = .x) %>% mutate(login = .x) } ) toc() #   

35.46 sec elapsed : 35.46 sec elapsed


Das purrr Paket ist Teil des Kerns der von Headley Wickham verfassten tidyverse Bibliothek.


In Bezug auf Bedeutung und Syntax sind die Hauptfunktionen des Pakets sapply sehr ähnlich. sapply Hauptvorteil ist folgender:


  • Die Funktionen sind in Familienkarten, map2 , pmap , walk usw. unterteilt. Separate Funktionen derselben Familie geben das Ergebnis in verschiedenen Formaten zurück: chr , dbl , int , df usw.;
  • Mit den Funktionen der map2 Familie map2 Sie Elemente (iterieren) von zwei Objekten gleichzeitig map2 .
  • Mit den Funktionen der pmap Familie pmap Sie gleichzeitig Elemente einer beliebigen Anzahl von Objekten pmap . Sie können eine Tabelle an die Eingabe an das Argument .l übergeben (ein Analogon des X-Arguments in sapply) , dessen jede Spalte die Werte enthält, mit denen Sie iterieren, und die wiederum durch die Argumente derselben Funktion ersetzt werden, die in .f (dem FUN-Analogon von) übergeben wird sapply) .

In welcher Situation müssen wir Elemente mehrerer Objekte durchlaufen. Sie arbeiten beispielsweise mit mehreren Agentenkonten, und Werbekonten, von denen Sie eine Liste mit Schlüsselwörtern erhalten möchten, sind zwischen diesen verteilt. In diesem Fall können Sie aus den Namen der Agentenkonten einen Vektor erstellen und diesen durchlaufen, parallel dazu, wie Sie die Anmeldungen der Werbekonten sortieren.


Code 6: Beispiel für die Arbeit mit mehreren Agentenkonten
 library(purrr) #      agencies <- c("agency1", NA, "agency2", "agency1") #      #         result.pmap2 <- map2_df(.x = logins, .y = agencies, ~ { yadirGetKeyWords(Login = .x, AgencyAccount = .y) %>% mutate(login = .x) }) 

Stellen Sie sich nun die Situation vor, dass Sie, wenn Sie sich unter verschiedenen Konten angemeldet haben, die Datei mit den Anmeldeinformationen in verschiedenen Ordnern gespeichert haben und dann sofort drei Objekte durchlaufen müssen: Anmeldungen von Werbekonten, Anmeldungen von Agentenkonten, den Pfad, in dem die Datei mit den Anmeldeinformationen gespeichert ist. Dies kann mit Hilfe erfolgen. pmap Familienfunktionen.


Code 7: Beispiel für eine pmap-Funktion
 library(purrr) #  ,       #      TokenPath <- c("C:\\proj1\\tokens", "C:\\yandex\\token", "C:\\yandex\\token", "C:\\my_yandex_acoount") #   pmap.result <- pmap_df(list(Login = logins, AgencyAccount = agencies, TokenPath = TokenPath), yadirGetKeyWords) 

Dementsprechend ist das Ergebnis der Ausführung der Funktionen map_df , map2_df und pmap_df der pmap_df , und bei deren Verwendung ist der letzte Schritt aus dem Beispiel mit sapply ( do.call("rbind", kw.sapply) ) nicht erforderlich.


Der Code ist kompakter geworden und wird etwas schneller ausgeführt. purrr sammeln beide beschriebenen Ansätze, sapply und purrr , nacheinander Schlüsselwörter von jedem Konto. Daher ist die Gesamtausführungszeit dieser Operation gleich der Summe der Dauer der Datenerfassung von allen vier Konten.


Zeit [gesamt] = Zeit [login1] + Zeit [login2] + Zeit [login3] + Zeit [login4]


Multithread-Optionen zum Lösen der Aufgabe, Schlüsselwörter von Yandex.Direct zu sammeln



Wenn Sie also bereits den ersten Artikel gelesen haben, wissen Sie, dass der Multithread-Betriebsmodus mehrere Funktionen hat:


  • Jeder Thread startet in einer separaten R-Sitzung mit einer sauberen Arbeitsumgebung.
  • Aus dem gleichen Grund werden in einem separaten laufenden Prozess zuvor verbundene Pakete nicht standardmäßig übertragen.

Das Exportieren von Objekten, die in einer Arbeitsumgebung erstellt wurden, und das Verbinden von Paketen in jedem Ansatz wird unterschiedlich implementiert. Anschließend werden wir sie genauer betrachten.


parallel Paket


Dieses Paket wurde erstmals in Version 2.14.0 in das R-Paket aufgenommen und wird bis heute mit R selbst geliefert.


Code 8: Beispiellösung für das Problem durch das Parallelpaket
 library(parallel) library(tictoc) #   cl <- makeCluster(4) #      clusterExport(cl = cl, varlist = "logins") #  ,      #  ,       ryandexdirect clusterEvalQ(cl = cl, { library(ryandexdirect) library(dplyr) } ) tic() #   parallel.kw <- parSapplyLB(cl = cl, #   X = logins, # ,     FUN = function(x) { #      #      X yadirGetKeyWords(Login = x) %>% mutate(login = x) }, simplify = F) #    toc() #     #   stopCluster(cl) #      result.parallel <- dplyr::bind_rows(parallel.kw) 

16.75 sec elapsed : 16.75 sec elapsed


Versuchen wir, Code 8 zu analysieren. Die Funktion makeCluster erstellt einen Cluster von 4 Prozessen. Mit der Funktion clusterExport können wir Objekte aus unserer Hauptarbeitsumgebung in den erstellten Cluster clusterExport Dazu müssen wir die folgenden Argumente verwenden:


  • cl - Cluster, in den wir Objekte exportieren
  • varlist - Ein Textvektor , der die Namen der Objekte enthält, die in jeden Clusterprozess exportiert werden sollen.

Eine Möglichkeit, die richtigen Pakete auf jedem Clusterknoten zu verbinden, besteht in der Verwendung der Funktion clusterEvalQ . In unserem Beispiel verwenden wir es, um Pakete zu verbinden. Sie können jedoch einen beliebigen R-Code in clusterEvalQ schreiben. Der Code wird am Anfang jedes Clusterknotens gestartet. Die Argumente für diese Funktion sind ziemlich offensichtlich. Sie müssen den Cluster und die Befehle angeben, die darin ausgeführt werden.


parSapplyLB ist eine parallele Version der sapply Funktion mit Lastausgleich zwischen den Clusterknoten. Sie wird ebenfalls verwendet. Sie müssen den Cluster jedoch mit dem Argument cl angeben.


parallel gibt es auch andere parallelisierte Versionen der Funktionen der *apply Familie: parLapply , parSapply , parApply usw.


parSapply unterscheidet sich von parSapplyLB nur dadurch, dass es keinen Lastausgleich auf Clusterknoten gibt.


Mit der Funktion stopCluster wird der erstellte Cluster gestoppt.


Mit dem letzten Befehl, dplyr::bind_rows(parallel.kw) kombinieren wir das mit parSapplyLB erhaltene parallel.kw- Objekt in einer Tabelle.


Für Linux hat parallel separate Funktionen: mclapply , mcmapply , mcMap . In diesem Betriebssystem werden Befehle häufig schneller ausgeführt und der Code wird kompakter.


Code 9: Lösung mit mclapply für Linux
 library(parallel) library(tictic) library(dplyr) library(ryandexdirect) tic() mclapply.kw <- mclapply(logins, FUN = function(x) { #      #      X yadirGetKeyWords(Login = x) %>% mutate(login = x) }, mc.cores = 4) toc() 

Bei Verwendung dieser Funktionen muss der Cluster nicht mit dem makeCluster . Die Anzahl der Knoten, die Sie mit dem Argument mc.cores angeben . Es ist auch nicht erforderlich, Pakete zu verbinden und Objekte zu exportieren. Diese Vorgänge werden automatisch ausgeführt.


future Paket


Einer der modernsten Ansätze zur asynchronen Programmierung in R.


Ein Code, der parallel unser Problem mit Hilfe der future lösen wird, ist kompliziert genug, um verstanden zu werden. Lassen Sie uns daher die Arbeit an einem einfacheren Beispiel analysieren. Wir fordern eine Liste mit Schlüsselwörtern von einem Konto an.


Code 10: Das einfachste Beispiel für die Verwendung des zukünftigen Pakets
 library(future) #    plan(multiprocess) #      #    future.kw <- future({yadirGetKeyWords(Login = logins[4])}, packages = "ryandexdirect", globals = "logins") #     resolved(future.kw) #     future.result.1 <- value(future.kw) 

Versuchen wir, das Code 10- Beispiel herauszufinden. Mit der plan können Sie den Ausführungsmodus der angegebenen Ausdrücke festlegen und ändern. Hier sind die wichtigsten:


  • sequentiell - Dies ist die übliche R-Betriebsart. Befehle werden in der aktuellen Sitzung sequentiell ausgeführt.
  • Multisession - Im parallelen Modus werden die Befehle in den laufenden Sitzungen im Hintergrund auf dem aktuellen Computer ausgeführt, während Ihre Arbeitssitzung nicht blockiert wird.
  • Cluster - Paralleler Modus: Die Befehle werden auf dem aktuellen oder Remote-Computer ausgeführt, ähnlich wie sie im parallel Paket implementiert sind.

Das gesamte future Paket basiert auf der Ausführung von Befehlen in Hintergrundprozessen, ohne die aktuelle Sitzung zu blockieren. Das Ausführen der Ausführung von Befehlen folgt der gleichnamigen Funktion future . Wenn wir also den Befehl ausführen:


 future({yadirGetKeyWords(Login = logins[4])}, packages = "ryandexdirect", globals = "logins") 

Unsere aktuelle Sitzung in R ist nicht blockiert, und der Befehl wird im Hintergrund ausgeführt, wobei eine weitere R-Sitzung ausgeführt wird.


Mit der resolved Funktion können Sie den aktuellen Status des Ausführungsprozesses eines bestimmten Ausdrucks überprüfen. Schließlich wird die value verwendet, um das Ergebnis der future Ausführung zu erhalten. Wenn Sie die value früher als Ihre future Ausführung in einer parallel laufenden Sitzung ausführen, wird die aktuelle Arbeitssitzung blockiert, bis der Ausdruck für die parallele Sitzung abgeschlossen ist.


Das fortschrittlichste Arbeitsbeispiel ist die Verwendung von future Verbindung mit promises .


Code 11: Beispiel für das Teilen von Paketen mit "Zukunft" und "Versprechen"
 library(future) library(promises) #    plan(multiprocess) #      #    future.kw <- future({suppressMessages( yadirGetKeyWords(Login = logins[4]))}, packages = "ryandexdirect", globals = "logins") %...>% #     future, #      nrow() %...>% paste("words loaded") %...>% print() 

Das promises Paket bietet eine Reihe von Pipeline-Betreibern, die die future Funktionalität perfekt ergänzen.


Im Beispiel von Code 11 starten wir im Hintergrund den Prozess des Herunterladens von Keywords von einem Werbekonto. Außerdem wartet der Pipeline-Betreiber %...>% ohne die Arbeitssitzung zu blockieren, auf den Abschluss der future und führt die verbleibenden Vorgänge aus. Als Ergebnis der Codeausführung wird nach Abschluss future Arbeiten die Anzahl der Schlüsselwörter des angegebenen Kontos in der Konsole angezeigt:


 [1] "1855 words loaded" 

Am Ende des Artikels wird ein anschaulicheres Beispiel für eine Reihe von future und promises gezeigt.

Standardmäßig exportiert das future Paket selbst den gesamten Arbeitsbereich in jede parallel ausgeführte Sitzung. Sie können jedoch selbst eine Liste der zu exportierenden Objekte mithilfe des globalen Arguments angeben .


Um Pakete mit der future zu verbinden future sollten future einen Vektor mit ihren Namen an das Paketargument übergeben .


Zurück zu unserer Aufgabe: Im folgenden Codebeispiel im Parallelmodus wird eine Liste mit Schlüsselwörtern aus 4 Konten geladen:


Code 12: Ein Beispiel für die Lösung eines Problems mit dem zukünftigen Paket
 library(future) library(tictoc) #   plan("multisession", workers = 4) tic() #   futs <- lapply(logins, #      function(i) #        #   future({ yadirGetKeyWords(Login = i) %>% mutate(login = i) }, packages = c("ryandexdirect", "dplyr"))) completed <- sapply(futs, resolved) #    kw <- lapply(futs, value) #    toc() #    #     result.future <- dplyr::bind_rows(kw) 

Vorlaufzeit: 14.83 sec elapsed


Um eine Liste von Keywords im Multithread-Modus von allen in den Vektoranmeldungen aufgeführten Werbekonten herunterzuladen , müssen Sie im Hintergrund eine separate future ausführen. In lapply 12 implementieren wir dies mit der Funktion lapply .


Das Ergebnis von lapply ist eine Liste der future lapply . Sie können den Status jedes einzelnen mit dem sapply(futs, resolved) überprüfen, der einen logischen Vektor sapply(futs, resolved) wobei TRUE bedeutet, dass die future erfüllt ist, und FALSE, dass die future gerade ausgeführt wird.


Um Ergebnisse aus jeder future zu erhalten, verwenden wir nach Abschluss ihrer Arbeit den lapply(futs, value) .


: result.future <- dplyr::bind_rows(kw) .


future


, (
future ), .


future.apply

future.apply future , .


13: future.apply
 library(future.apply) library(tictoc) #    plan("multisession", workers = 4) tic() #   kw.future.apply <- future_sapply(logins, #    ,   function(x) { #     yadirGetKeyWords(Login = x) %>% mutate(login = x) }, simplify = FALSE, #    #   future.packages = c("ryandexdirect", "dplyr"), future.globals = TRUE ) toc() #    

: 17.28 sec elapsed


13 , future.apply future , .


4 : plan("multisession", workers = 4) .


future_sapply logins . Das heißt, , , sapply , .


future_sapply future.packages . future.globals . , .


furrr


future . purrr , furrr .


14: furrr
 library(furrr) library(tictoc) #   cl <- parallel::makeCluster(4) plan(cluster, workers = cl) tic() #   furrr.kw <- future_map(logins, ~ #   function(.x) yadirGetKeyWords(Login = .x) %>% mutate(login = .x), .options = future_options(packages = c("ryandexdirect", "dplyr"), globals = c())) toc() #    #      result.furrr <-dplyr::bind_rows(furrr.kw) 

: 15.45 sec elapsed


furrr purrr . purrr , .


.options . .options future_options , .


14 packages globals :


 .options = future_options(packages = c("ryandexdirect", "dplyr"), globals = c()) 


rbenchmark .


, future promises . .


, 20 4 () .


= (T[1] * 20) + (T[2] * 20) + (T[N] * 20)


15: future promises
 library(furrr) library(parallel) library(dplyr) library(future) library(ryandexdirect) library(tictoc) library(rbenchmark) #   logins <- c("login1", "login2", "login3", "login4") #        #        par par.furrr <- function(logins) { cl <- parallel::makeCluster(4) plan(cluster, workers = cl) furrr.kw <- future_map(logins, ~ yadirGetKeyWords(Login = .x) %>% mutate(login = .x), .options = future_options(packages = c("ryandexdirect", "dplyr"), globals = c())) result.furrr <-dplyr::bind_rows(furrr.kw) } par.future <- function(logins) { plan("multisession", workers = 4) futs <- lapply(logins, function(i) future({ yadirGetKeyWords(Login = i) %>% mutate(login = i) }, packages = c("ryandexdirect", "dplyr"))) completed <- sapply(futs, resolved) kw <- lapply(futs, value) result.future <- dplyr::bind_rows(kw) } par.future.apply <- function(logins) { plan("multisession", workers = 4) kw.future.apply <- future_sapply(logins, function(x) { yadirGetKeyWords(Login = x) %>% mutate(login = x) }, simplify = FALSE, future.packages = c("ryandexdirect", "dplyr"), future.globals = TRUE ) result.future.apply <- dplyr::bind_rows(kw.future.apply) } par.parallel <- function(logins) { cl <- parallel::makeCluster(4) clusterExport(cl = cl, varlist = "logins") clusterEvalQ(cl = cl, { library(ryandexdirect) library(dplyr) } ) parallel.kw <- parSapplyLB(cl = cl, X = logins, FUN = function(x) { yadirGetKeyWords(Login = x) %>% mutate(login = x) }, simplify = F) stopCluster(cl) result.parallel <- dplyr::bind_rows(parallel.kw) } #          seq seq.apply <- function(logins) { kw.sapply <- sapply( logins, function(x) { yadirGetKeyWords(Login = x) %>% mutate(login = x) }, simplify = FALSE ) result.sapply <- do.call("rbind", kw.sapply) } seq.purrr <- function(logins) { kw.purrr <- map_df( logins, ~ { yadirGetKeyWords(Login = .x) %>% mutate(login = .x) } ) result.purrr <- do.call("rbind", kw.purrr) } #       rbenchmark #   future + promises #  ,       #          plan(list(tweak(multisession, workers = 2), tweak(multisession, workers = 4))) tic() speed.test <- future({ #          within(benchmark(furrr = par.furrr(logins), future = par.future(logins), future.apply = par.future.apply(logins), parallel = par.parallel(logins), apply = seq.apply(logins), purrr = seq.purrr(logins), replications = c(20), columns = c('test', 'replications', 'elapsed'), order = c('elapsed', 'test')), { average = round(elapsed/replications, 2) }) }, packages = c("dplyr", "ryandexdirect", "rbenchmark", "parallel", "purrr", "future", "promises", "furrr", "future.apply"), globals = c("logins", "par.furrr", "par.future", "par.future.apply", "par.parallel", "seq.apply", "seq.purrr")) %...>% print() %...T>% toc() message("My Session is not blocked") 

3370 , .. .


. , future , promises , .


, . "My Session is not blocked", , , .. .


promises :


  • %...>%%>% , . Das heißt, , resolved , future , value . , print .
  • %...T>%%T>% , , . , , , .. .. print , , .
  • %...T!% — .

15 plan tweak ( plan(list(tweak(multisession, workers = 2), tweak(multisession, workers = 4))) ), , 2 , future 4 .


:


 My Session is not blocked test replications elapsed average 4 parallel 20 393.02 19.65 1 furrr 20 402.09 20.10 2 future 20 431.19 21.56 3 future.apply 20 432.29 21.61 5 apply 20 847.77 42.39 6 purrr 20 864.19 43.21 3370.55 sec elapsed 

Bild


, parallel , . furrr , future future.apply .


1 , , . , API . .


, 4 , .


Fazit


R, API.


, API . " R , 1" .


:


  • doSNOW / doParallel + foreach
  • future + promises
  • future.apply / furrr
  • parallel

, , .


, R .

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


All Articles