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.

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.
yadirGetKeyWords(Login = "login1") %>% mutate(login = "login1")
yadirGetKeyWords(Login = "login2") %>% mutate(login = "login2")
yadirGetKeyWords(Login = "login3") %>% mutate(login = "login3")
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

, parallel
, . furrr
, future
future.apply
.
1 , , . , API . .
, 4 , .
Fazit
R, API.
, API . " R , 1" .
:
- doSNOW / doParallel + foreach
- future + promises
- future.apply / furrr
- parallel
, , .
, R .