Chapitre 4 Profiler son code

On parle de profiling en anglais. Il s’agit de dĂ©terminer ce qui prend du temps dans un code. Le but Ă©tant, une fois trouvĂ© le bloc de code qui prend le plus de temps dans l’exĂ©cution, d’optimiser uniquement cette brique.

Pour obtenir un profiling du code ci-dessous, sĂ©lectionner les lignes de code d’intĂ©rĂȘt et aller dans le menu “Profile” puis “Profile Selected Lines”. Cela utilise en fait la fonction profvis() du package profvis.

n <- 10e4
pdfval <- mvnpdf(x=matrix(1.96, nrow = 2, ncol = n), Log=FALSE)

OK, we get it ! ConcatĂ©ner un vecteur au fur et Ă  mesure dans une boucle n’est vraiment pas une bonne idĂ©e.

4.1 Comparaison avec une version plus habile de mnvpdf()

ConsidĂ©rons une nouvelle version de mvnpdf(), appelĂ©e mvnpdfsmart(). TĂ©lĂ©charger le fichier puis l’inclure dans votre package.

Profiler la commande suivante :

n <- 10e4
pdfval <- mvnpdfsmart(x=matrix(1.96, nrow = 2, ncol = n), Log=FALSE)

On a effectivement résolu le problÚme et on apprend maintenant de maniÚre plus fine ce qui prend du temps dans notre fonction.

Pour confirmer que mvnpdfsmart() est effectivement bien plus rapide que mvnpdf() on peut re-faire une comparaison avec microbenchmark() :

n <- 1000
mb <- microbenchmark(mvnpdf(x=matrix(1.96, nrow = 2, ncol = n), Log=FALSE),
                     mvnpdfsmart(x=matrix(1.96, nrow = 2, ncol = n), Log=FALSE),
                     times=100L)
mb
## Unit: milliseconds
##                                                            expr      min
##       mvnpdf(x = matrix(1.96, nrow = 2, ncol = n), Log = FALSE) 3.138591
##  mvnpdfsmart(x = matrix(1.96, nrow = 2, ncol = n), Log = FALSE) 2.330481
##        lq     mean   median       uq      max neval cld
##  3.322681 3.778820 3.437030 3.580571 8.138008   100  a 
##  2.360309 2.447065 2.381362 2.425847 6.387636   100   b

Et on peut également voir si on devient compétitif avec dmvnorm() :

n <- 1000
mb <- microbenchmark(mvtnorm::dmvnorm(matrix(1.96, nrow = n, ncol = 2)),
                     mvnpdf(x=matrix(1.96, nrow = 2, ncol = n), Log=FALSE),
                     mvnpdfsmart(x=matrix(1.96, nrow = 2, ncol = n), Log=FALSE),
                     times=100L)
mb
## Unit: microseconds
##                                                            expr      min
##              mvtnorm::dmvnorm(matrix(1.96, nrow = n, ncol = 2))   43.747
##       mvnpdf(x = matrix(1.96, nrow = 2, ncol = n), Log = FALSE) 3147.857
##  mvnpdfsmart(x = matrix(1.96, nrow = 2, ncol = n), Log = FALSE) 2308.546
##         lq       mean   median       uq       max neval cld
##    51.8445   73.05421   74.251   89.749   117.424   100 a  
##  3295.1085 3715.51266 3416.387 3522.740  8054.737   100  b 
##  2348.0085 2504.66540 2379.497 2428.163 10087.189   100   c

Il y a encore du travail


4.2 Comparaison avec une version optimisée dans

Boris est arrivé, aprÚs de longues recherches et plusieurs tests, à une version optimisée avec les outils de .

Inclure la fonction mvnpdfoptim() dans le package, puis profiler cette fonction :

n <- 10e4
profvis::profvis(mvnpdfoptim(x=matrix(1.96, nrow = 2, ncol = n), Log=FALSE))

Et un petit microbenchmark() :

n <- 1000
mb <- microbenchmark(mvtnorm::dmvnorm(matrix(1.96, nrow = n, ncol = 2)),
                     mvnpdf(x=matrix(1.96, nrow = 2, ncol = n), Log=FALSE),
                     mvnpdfsmart(x=matrix(1.96, nrow = 2, ncol = n), Log=FALSE),
                     mvnpdfoptim(x=matrix(1.96, nrow = 2, ncol = n), Log=FALSE),
                     times=100L)
mb
## Unit: microseconds
##                                                            expr      min
##              mvtnorm::dmvnorm(matrix(1.96, nrow = n, ncol = 2))   43.337
##       mvnpdf(x = matrix(1.96, nrow = 2, ncol = n), Log = FALSE) 3154.499
##  mvnpdfsmart(x = matrix(1.96, nrow = 2, ncol = n), Log = FALSE) 2314.450
##  mvnpdfoptim(x = matrix(1.96, nrow = 2, ncol = n), Log = FALSE) 1747.871
##         lq       mean   median        uq      max neval  cld
##    53.6075   73.60197   75.235   90.3025  142.680   100 a   
##  3301.1970 3824.36479 3398.121 3571.4485 8685.276   100  b  
##  2368.7135 2419.81877 2401.309 2434.4365 3397.875   100   c 
##  1794.3855 1922.38012 1828.580 1863.3475 6635.522   100    d

Pour finir on peut profiler la fonction dmvnorm() :

n <- 10e5
library(mvtnorm)
profvis::profvis(dmvnorm(matrix(1.96, nrow = n, ncol = 2)))