2014-06-25 2 views
1

Je tente de "mapper" une fonction sur un tableau. Cependant, lorsque vous essayez des fonctions simples et complexes, la version parallèle est toujours plus lente que la version sérielle. Comment puis-je améliorer les performances d'un calcul parallèle dans R?Comment puis-je effectuer une opération parallèle plus rapidement que la version série?

Exemple simple parallèle:

library(parallel) 

# Number of elements 
arrayLength = 100 
# Create data 
input = 1:arrayLength 

# A simple computation 
foo = function(x, y) x^y - x^(y-1) 

# Add complexity 
iterations = 5 * 1000 * 1000 

# Perform complex computation on each element 
compute = function (x) { 
    y = x 
    for (i in 1:iterations) { 
    x = foo(x, y) 
    } 
    return(x) 
} 

# Parallelized compute 
computeParallel = function(x) { 
    # Create a cluster with 1 fewer cores than are available. 
    cl <- makeCluster(detectCores() - 1) # 8-1 cores 
    # Send static vars & funcs to all cores 
    clusterExport(cl, c('foo', 'iterations')) 
    # Map 
    out = parSapply(cl, x, compute) 
    # Clean up 
    stopCluster(cl) 
    return(out) 
} 

system.time(out <- compute(input)) # 12 seconds using 25% of cpu 
system.time(out <- computeParallel(input)) # 160 seconds using 100% of cpu 
+1

L'initialisation des clusters prend du temps. Avez-vous essayé d'initialiser votre fonction? –

+0

Ce n'est pas une comparaison équitable, mais je l'ai juste essayé. Pas de changement substantiel. Je serais surpris si l'initialisation pouvait expliquer la différence d'ordre de grandeur. – sharoz

+0

Je n'utilise pas beaucoup ces paquets parallèles, mais littéralement à chaque fois il y a une question sur SO pourquoi la parallélisation est plus lente que la série car la surcharge de la tâche domine la complexité de la tâche elle-même. – joran

Répondre

1

Le problème est que vous troqués tous de la vectorisation pour la parallélisation, et qui est un mauvais métier. Vous devez garder autant de vectorisation que possible pour avoir l'espoir d'obtenir une amélioration avec la parallélisation pour ce genre de problème.

La fonction pvec dans le paquet parallèle peut être une bonne solution à ce genre de problème, mais elle n'est pas supportée en parallèle sur Windows. Une solution plus générale qui fonctionne sous Windows consiste à utiliser foreach avec le paquetage itertools qui contient des fonctions utiles pour parcourir plusieurs objets. Voici un exemple qui utilise la fonction « isplitVector » pour créer un sous-vecteur pour chaque travailleur:

library(doParallel) 
library(itertools) 
cl <- makeCluster(detectCores() - 1) 
registerDoParallel(cl) 
computeChunk <- function(x) { 
    foreach(xc=isplitVector(x, chunks=getDoParWorkers()), 
      .export=c('foo', 'iterations', 'compute'), 
      .combine='c') %dopar% { 
    compute(xc) 
    } 
} 

Cela peut toujours pas comparer très bien à la version pure vecteur, mais il devrait mieux que la valeur des « itérations » augmente. Cela peut en fait aider à diminuer le nombre de travailleurs à moins que la valeur des «itérations» soit très grande.

+0

Je suppose que j'ai supposé que le découpage était fait automatiquement par 'parSapply'. Cela a fait chuter le temps en dessous de l'heure de série. – sharoz

0

parSapply se déroulera la fonction de chaque élément de input séparément, ce qui signifie que vous renoncez à la vitesse que vous avez gagné de l'écriture foo et compute de façon vectorisé.

pvec exécutera une fonction vectorisée sur plusieurs cœurs par morceaux. Essayez ceci:

system.time(out <- pvec(input, compute, mc.cores=4)) 
+0

Malheureusement, cette fonction n'est pas supportée sous Windows pour mc.cores> 1 – sharoz

Questions connexes