2011-11-17 5 views
4

[Question corrigée des réponses suivantes]Ecriture de grandes matrices dans une fonction - rapide vs lent

Merci pour les réponses. Je n'étais pas clair dans ma question, pour laquelle je m'excuse.

Je vais essayer de donner plus de détails sur notre situation. Nous avons c. 100 matrices que nous gardons dans un environnement. Chacun est très grand. Si possible, nous voulons éviter toute copie de ces matrices lorsque nous effectuons des mises à jour. Nous sommes souvent confrontés à la limite de mémoire de 2 Go, ce qui est très important pour nous.

Ainsi, nos deux exigences sont 1) éviter les copies et 2) adresser indirectement les matrices par leur nom. La vitesse, bien qu'importante, est un problème secondaire qui serait résolu en évitant la copie.

Il me semble que la solution de Tommy impliquait la création d'une copie (bien qu'elle ait entièrement répondu à ma question initiale, je suis donc fautive).

Le code ci-dessous est ce qui nous semble le plus évident, mais il crée clairement une copie (comme le montre l'augmentation de memory.size)

myenv <- new.env() 
myenv$testmat1 <- matrix(1.0, nrow=6000, ncol=200) 

testfnDirect <- function(paramEnv) { 
    print(memory.size()) 

    for (i in 1:300) { 
     temp <- paramEnv$testmat1[10,] 
     paramEnv$testmat1[10,] <- temp * 0 
    } 
    print(memory.size()) 
} 
system.time(testfnDirect(myenv)) 

En utilisant le mot-clé avec semble éviter cela, comme ci-dessous:

myenv <- new.env() 
myenv$testmat1 <- matrix(1.0, nrow=6000, ncol=200) 

testfnDirect <- function(paramEnv) { 
    print(gc()) 
    varname <- "testmat1" # unused, but see text 
    with (paramEnv, { 
     for (i in 1:300) { 
      temp <- testmat1[10,] 
      testmat1[10,] <- temp * 0 
     } 
    }) 
    print(gc()) 
} 
system.time(testfnDirect(myenv)) 

Toutefois, ce code fonctionne en adressant directement testmat1 par son nom. Notre problème est que nous devons nous y attaquer indirectement (nous ne savons pas à l'avance quelles matrices nous mettrons à jour).

Est-il possible de modifier testfnDirect telle que nous utilisons la variable nom_variable plutôt que hardcoding testmat

+0

FWIW: Cet exemple peut convenir, mais en général, il est bon d'éviter de produire des exemples où la mise en cache ou la mémoisation peuvent créer des accélérations évidentes. Dans de tels cas, il est difficile de faire la distinction entre les optimisations qui sont sous notre contrôle et celles qui sont effectuées par l'interpréteur ou le matériel. – Iterator

Répondre

1

Eh bien, ce serait bien si vous pouviez expliquer pourquoi la première solution n'est pas OK ... Il ressemble beaucoup plus propre et fonctionne plus vite.

Pour tenter de répondre aux questions suivantes:

  1. Une opération « de remplacement imbriqué » comme foo[bar][baz] <- 42 est très complexe, et est optimisé pour certains cas pour éviter la copie. Mais il est très probable que votre cas d'utilisation particulier ne soit pas optimisé. Cela conduirait à beaucoup de copies, et la perte de performance.

    Une façon de tester cette théorie est d'appeler gcinfo(TRUE) avant vos tests. Vous verrez alors que la première solution déclenche 2 garbage collects, et la seconde déclenche environ 160!

  2. Voici une variante de votre deuxième solution qui convertit l'environnement en liste, fait son truc et le convertit en environnement. C'est aussi rapide que votre première solution.

code:

testfnList <- function() { 
    mylist <- as.list(myenv, all.names=TRUE) 

    thisvar <- "testmat2" 
    for (i in 1:300) { 
     temp <- mylist[[thisvar]][10,] 
     mylist[[thisvar]][10,] <- temp * 0 
    } 

    myenv <<- as.environment(mylist) 
} 
system.time(testfnList()) # 0.02 secs 

... ce serait bien sûr plus propre si vous avez réussi myenv à la fonction comme argument. Une petite amélioration (si vous boucle beaucoup, pas seulement 300 fois) serait d'indexer par numéro au lieu de nom (ne fonctionne pas pour les environnements, mais pour les listes).Il suffit de changer thisvar:

thisvar <- match("testmat2", names(mylist)) 
2

Un changement relativement récent au paquet « data.table » a été spécialement pour éviter la copie lors de la modification des valeurs. Donc, si votre application peut gérer data.tables pour les autres opérations, cela pourrait être une solution. (Et ce serait rapide.)

Questions connexes