Pourquoi la fonction diag est-elle si lente?

Je regardais les points de référence dans cette réponse et je voulais les comparer avec diag (utilisé dans une réponse différente). Malheureusement, il semble que diag prend des années:

 nc <- 1e4 set.seed(1) m <- matrix(sample(letters,nc^2,replace=TRUE), ncol = nc) microbenchmark( diag = diag(m), cond = m[row(m)==col(m)], vec = m[(1:nc-1L)*nc+1:nc], mat = m[cbind(1:nc,1:nc)], times=10) 

Commentaires : J’ai testé ceux-ci avec identical . J’ai pris “cond” d’une des réponses à cette question de devoirs . Les résultats sont similaires avec une masortingce d’entiers, 1:26 au lieu de letters .

Résultats :

 Unit: microseconds expr min lq mean median uq max neval diag 604343.469 629819.260 710371.3320 706842.3890 793144.019 837115.504 10 cond 3862039.512 3985784.025 4175724.0390 4186317.5260 4312493.742 4617117.706 10 vec 317.088 329.017 432.9099 350.1005 629.460 651.376 10 mat 272.147 292.953 441.7045 345.9400 637.506 706.860 10 

C’est juste une opération de sous-masortingce, donc je ne sais pas pourquoi il y a tant de frais généraux. En regardant à l’intérieur de la fonction, je vois quelques vérifications et puis c(m)[v] , où v est le même vecteur utilisé dans le benchmark “vec”. Chronométrer ces deux …

 v <- (1:nc-1L)*nc+1:nc microbenchmark(diaglike=c(m)[v],vec=m[v]) # Unit: microseconds # expr min lq mean median uq max neval # diaglike 579224.436 664853.7450 720372.8105 712649.706 767281.5070 931976.707 100 # vec 334.843 339.8365 568.7808 646.799 663.5825 1445.067 100 

… il semble que j’ai trouvé mon coupable. Donc, la nouvelle variante de ma question est la suivante: pourquoi y-a-t-il un c apparemment inutile et très chronophage?

Résumé

A partir de la version 3.2.1 (World-Famous Astronaut), diag() a reçu une mise à jour. La discussion s’est déplacée vers r-devel où il a été noté que c() supprime les atsortingbuts non-nom et peut-être pourquoi il a été placé là. Alors que certaines personnes craignaient que la suppression de c() ne provoque des problèmes inconnus sur des objects de type masortingce, Peter Dalgaard a trouvé que: “Le seul cas où c() dans diag() a un effet est où M[i,j] != M[(i-1)*m+j] ET c(M) enchaînera M en ordre majeur de colonne, de sorte que M[i,j] == c(M)[(i-1)*m+j] . ”

Luke Tierney a testé la suppression de c() @Frank, en constatant que cela n’avait aucun effet sur CRAN ou BIOC et a donc été implémenté pour remplacer […] c (x) par x […] sur la ligne 27 . Cela conduit à des accélérations relativement importantes dans diag() . Vous trouverez ci-dessous un test de vitesse montrant l’amélioration avec la version R 3.2.1 de diag() .

 library(microbenchmark) nc <- 1e4 set.seed(1) m <- matrix(sample(letters,nc^2,replace=TRUE), ncol = nc) microbenchmark(diagOld(m),diag(m)) Unit: microseconds expr min lq mean median uq max neval diagOld(m) 451189.242 526622.2775 545116.5668 531905.5635 540008.704 682223.733 100 diag(m) 222.563 646.8675 644.7444 714.4575 740.701 1015.459 100