2016-01-07 16 views
9

Mam problem z foreach, którego po prostu nie mogę rozgryźć. nie działa na dwóch komputerach z systemem Windows próbowałem, ale udaje się na trzech komputerach Linux, wszystkie uruchomione te same wersje R i doParallel:doParallel "foreach" niekonsekwentnie dziedziczy obiekty ze środowiska nadrzędnego: "Błąd w {: zadanie 1 nie powiodło się -" nie może znaleźć funkcji ... "

library("doParallel") 
registerDoParallel(cl=2,cores=2) 

f <- function(){return(10)} 
g <- function(){ 
    r = foreach(x = 1:4) %dopar% { 
     return(x + f()) 
    } 
    return(r) 
} 
g() 

na tych dwóch komputerów z systemem Windows, następujące zwracany jest błąd:

Error in { : task 1 failed - "could not find function "f"" 

Jednak działa to dobrze na komputerach z systemem Linux, a także działa dobrze z% do% zamiast% dopar% i działa dobrze dla zwykłej pętli for.

To samo dotyczy zmiennych, np. Ustawienie i <- 10 i zastępując return(x + f()) z return(x + i)

Dla innych osób z tego samego problemu, dwa sposoby rozwiązania to:

1) wyraźnie import potrzebne funkcje i zmienne z .export:

r = foreach(x=1:4, .export="f") %dopar% 

2) importowanie wszystkich obiekty globalne:

r = foreach(x=1:4, .export=ls(.GlobalEnv)) %dopar% 

Problem z tymi obejściami polega na tym, że nie są one Najbardziej stabilny dla dużego, aktywnie rozwijającego się pakietu. W każdym razie foreach ma się zachowywać jak za.

Jakieś pomysły na to, co to powoduje i czy jest jakiś problem?


Informacje o wersji z komputerem, że funkcja działa na:

R version 3.2.2 (2015-08-14) 
Platform: x86_64-pc-linux-gnu (64-bit) 
Running under: CentOS release 6.5 (Final) 

other attached packages: 
[1] doParallel_1.0.10 iterators_1.0.8 foreach_1.4.3 

Komputer funkcja nie działa na:

R version 3.2.2 (2015-08-14) 
Platform: x86_64-w64-mingw32/x64 (64-bit) 
Running under: Windows 7 x64 (build 7601) Service Pack 1 

other attached packages: 
[1] doParallel_1.0.10 iterators_1.0.8 foreach_1.4.3 
+3

Gdzie jest funkcja 'f()' w twoim przykładowym kodzie? W zależności od tego, co podałeś, wydaje się, że komputer z systemem Windows podaje odpowiedni błąd, ponieważ 'f' nie jest funkcją, lecz liczbą. – brittenb

+0

Od doParallel winiety: "Aby użyć funkcji podobnej do wielordzeniowej, określilibyśmy liczbę rdzeni do wykorzystania (ale zauważ, że w systemie Windows próba użycia więcej niż jednego rdzenia z równoległym skutkuje błędem)" Tj: windows does not nie zaimplementuj czegoś takiego jak 'fork' używanego przez doParallel, obejście polega na rozpoczynaniu całej nowej sesji R, aby umieścić pracę, IIRC kopiuje środowisko macierzyste, tutaj funkcja' g' env, a nie globalna. – Tensibai

+0

@brittenb Przepraszamy, wprowadziłem niepełną zmianę; Zamierzałem zrobić f = function() {return (10)}. Edytowanie oryginału. – sssheridan

Odpowiedz

9

@Tensibai ma rację. Podczas próby użycia doParallel w systemie Windows musisz "wyeksportować" funkcje, których chcesz użyć, które nie są w bieżącym zakresie. Z mojego doświadczenia wynika, że ​​sposób, w jaki wykonałem tę pracę, jest następujący (zredagowany) przykład.

format_number <- function(data) { 
    # do stuff that requires stringr 
} 

format_date_time <- function(data) { 
    # do stuff that requires stringr 
} 

add_direction_data <- function(data) { 
    # do stuff that requires dplyr 
} 

parse_data <- function(data) { 
    voice_start <- # vector of values 
    voice_end <- # vector of values 
    target_phone_numbers <- # vector of values 
    parse_voice_block <- function(block_start, block_end, number) { 
    # do stuff 
    } 

    number_of_cores <- parallel::detectCores() - 1 
    clusters <- parallel::makeCluster(number_of_cores) 
    doParallel::registerDoParallel(clusters) 
    data_list <- foreach(i = 1:length(voice_start), .combine=list, 
         .multicombine=TRUE, 
         .export = c("format_number", "format_date_time", "add_direction_data"), 
         .packages = c("dplyr", "stringr")) %dopar% 
         parse_voice_block(voice_start[i], voice_end[i], target_phone_numbers[i]) 
    doParallel::stopCluster(clusters) 
    output <- plyr::rbind.fill(data_list) 
} 

Ponieważ pierwsze trzy funkcje nie są wliczone w mojej obecnej sytuacji doParallel by je ignorować podczas rozpalania nowe instancje R, ale byłoby wiedzieć, gdzie znaleźć parse_voice_block ponieważ jest to w obecnym zakresie. Dodatkowo musisz określić, jakie pakiety powinny być załadowane w każdym nowym wystąpieniu R. Jak stwierdził Tensibai, dzieje się tak dlatego, że nie używasz rozwidlenia procesu, ale zamiast tego uruchamiasz wiele instancji R i jednocześnie uruchamiasz polecenia.

+1

Powinieneś użyć 'stopCluster (clusters)' zamiast 'stopImplicitCluster()', ponieważ jawnie wywołujesz 'makeCluster'. Ponadto, używając '.combine = list', otrzymasz listę wewnątrz list, jeśli masz więcej niż 100 zadań. –

+0

@SteveWeston Należycie zauważyłem, dziękuję za to! – brittenb

+0

@SteveWeston Dosłownie 6 dni po tym, jak mi to powiesz, uruchamiam kod z 'doParallel' i próbuję' plyr :: rbind.fill (data_list) 'na końcu i to się nie udaje. Nie mogę się domyślić, dlaczego tak jest przez godzinę, a potem w końcu zdaję sobie sprawę, że to z powodu tego komentarza. Jeszcze raz dzięki za heads-upy. – brittenb

5

To raczej niefortunne, że podczas rejestracji doParallel używając:

registerDoParallel(2) 

następnie doParallel wykorzystuje mclapply na Linux i Mac OS X, ale clusterApplyLB z niejawnie utworzonego obiektu klastra w systemie Windows.Często powoduje to, że kod działa w systemie Linux, ale kończy się niepowodzeniem w systemie Windows, ponieważ pracownicy są klonami systemu głównego, gdy używają mclapply ze względu na fork. Z tego powodu, zwykle przetestować mojego kodu przy użyciu:

cl <- makePSOCKcluster(2) 
registerDoParallel(cl) 

aby upewnić Ja ładuje wszystkie niezbędne pakiety i wywozu wszystkie niezbędne funkcje i zmienne, a następnie powrócić do registerDoParallel(2) aby uzyskać korzyści z mclapply na platformach które to wspierają.

Zauważ, że opcje .packages i .export są ignorowane podczas doParallel wykorzystuje mclapply, ale zawsze zalecamy ich użyciem przenośności.


Funkcja auto-eksport foreach nie działa już tak sprawnie, kiedy go używać wewnątrz funkcji foreach, ponieważ jest raczej konserwatywny, co do automatycznego eksportu. Wydaje się całkiem bezpieczne automatyczne eksportowanie zmiennych i funkcji zdefiniowanych w bieżącym środowisku, ale poza tym wydaje mi się to ryzykowne ze względu na złożoność reguł zakresu R.

I raczej się zgadza z Twojego komentarza, że ​​dwa obejścia nie są bardzo stabilne dla aktywnie rozwijany pakiet, ale jeśli fg i są zdefiniowane w pakiecie foo, to należy użyć foreach .package opcję, aby załadować pakować foo na pracowników:

g <- function(){ 
    r = foreach(x = 1:4, .packages='foo') %dopar% { 
     return(x + f()) 
    } 
    return(r) 
} 

Następnie f będzie w zakresie g mimo że nie jest ani pośrednio lub bezpośrednio eksportowane przez foreach. Wymaga to jednak, aby f była wyeksportowaną funkcją foo (w przeciwieństwie do funkcji wewnętrznej), ponieważ kod wykonywany przez pracowników nie jest zdefiniowany w foo, więc może uzyskać dostęp tylko do wyeksportowanych funkcji. (Przepraszam, że używam terminu "eksport" na dwa różne sposoby, ale trudno tego uniknąć.)

Zawsze jestem zainteresowany, aby usłyszeć komentarze takie jak twoje, ponieważ zawsze zastanawiam się, czy reguły automatycznego eksportu powinny być zmodyfikowany. W tym przypadku myślę, że jeśli pętla foreach jest wykonywana przez funkcję zdefiniowaną w pakiecie, pracownicy klastra powinni automatycznie załadować ten pakiet bez potrzeby stosowania opcji .packages. Spróbuję zajrzeć do tego i być może dodać to do następnej wersji doParallel i doSNOW.

Powiązane problemy