정보수집 프로파일링(profiling)에 대해 이해하고, 프로파일링을 적용해서 성능병목점을 식별하고 성능을 향상시킨다. R코드 어느 부분에서 가장 많은 수행시간과 메모리 저장공간을 사용했는지 파악하는 것이 중요하다. 수행시간과 메모리 저장공간을 측정하는데 사용되는 도구가 정보수집 프로파일링(Profiling) 이다.
system.time
: 간단히 수행시간을 측정하는 함수Rprof
: R코드 프로파일링하는 함수1Rprofmem
R 메모리 사용량을 프로파일링하는 함수Rprof
, Rprofmem
함수를 추상화해서 시각화하는 팩키지가 profviz
로 출시되었고, rbenchmark
는 비교하는 목적으로 사용되었고, 최근에는 bench
팩키지가 새롭게 도입되었다.
Rprof
프로파일링2Rprof
함수는 정해진 시간, 예를 들어 interval = 0.02
기본디폴트 설정값으로 되어 20 \(frac{1}{1000}\) 천분의 1초로 측정을 한다. 측정결과는 지정한 경로명에 위치한 파일에 저장되고, summaryRprof
함수로 꺼내본다.
Rprof
함수는 R에 기본 내장된 프로파일링 함수로 사용법은 다음과 같다.
Rprof("프로파일링 경과를 저장할 파일경로 및 파일명")
## 프로파일링을 통해 정보를 추적할 R 코드
Rprof(NULL)
## 프로파일링하지 않아 정보를 추정하지 않을 R코드
Rprof("프로파일링 경과를 저장할 파일경로 및 파일명", append=TRUE)
## 프로파일링을 통해 정보를 추적할 R 코드
Rprof(NULL)
# 프로파일링 추적결과를 요약하고 정리
summaryRprof("프로파일링 경과를 저장할 파일경로 및 파일명")
$by.self
self.time self.pct total.time total.pct
sample.interval=20000 0.02 100 0.02 100
$by.total
total.time total.pct self.time self.pct
sample.interval=20000 0.02 100 0.02 100
$sample.interval
[1] 0.02
$sampling.time
[1] 0.02
Rprof
프로파일링 요약by.self
: 해당 함수가 온전히 사용한 시간by.total
: 해당 함수와 호출된 함수 모두에서 사용된 시간sample.interval
: 표본측정 시간, 기본디폴트설정으로 20 천분의 1초로 설정sampling.time
: 프로파일링을 통해 수행된 전체 시간.추가로 한번만 측정하면 측정 중간에 쓰레기수거(Garbage Collection, GC) 작업 등으로 인해 정확한 실행시간 측정에 오차가 발생할 수 있다. 이를 방지하고자, replicate
를 사용해서 반복측정한다.
Rprof
사례example(glm)
을 통해 일반화선형모형을 적합하여 모형을 개발하는데 사용된 시간을 살펴본다. 전체적으로 80 천분의 1초가 소요되었으며 “ifelse”, “psub”, “set”, “unlist”에서 시간이 균등하게 사용된 것을 볼 수 있다.
##========================================================================
## Rprof 예제
##========================================================================
# 1. 프로파일링 시작
Rprof(tmp <- tempfile())
example(glm)
glm> ## Dobson (1990) Page 93: Randomized Controlled Trial :
glm> counts <- c(18,17,15,20,10,20,25,13,12)
glm> outcome <- gl(3,1,9)
glm> treatment <- gl(3,3)
glm> data.frame(treatment, outcome, counts) # showing data
treatment outcome counts
1 1 1 18
2 1 2 17
3 1 3 15
4 2 1 20
5 2 2 10
6 2 3 20
7 3 1 25
8 3 2 13
9 3 3 12
glm> glm.D93 <- glm(counts ~ outcome + treatment, family = poisson())
glm> anova(glm.D93)
Analysis of Deviance Table
Model: poisson, link: log
Response: counts
Terms added sequentially (first to last)
Df Deviance Resid. Df Resid. Dev
NULL 8 10.5814
outcome 2 5.4523 6 5.1291
treatment 2 0.0000 4 5.1291
glm> ## No test:
glm> ##D summary(glm.D93)
glm> ## End(No test)
glm> ## Computing AIC [in many ways]:
glm> (A0 <- AIC(glm.D93))
[1] 56.76132
glm> (ll <- logLik(glm.D93))
'log Lik.' -23.38066 (df=5)
glm> A1 <- -2*c(ll) + 2*attr(ll, "df")
glm> A2 <- glm.D93$family$aic(counts, mu=fitted(glm.D93), wt=1) +
glm+ 2 * length(coef(glm.D93))
glm> stopifnot(exprs = {
glm+ all.equal(A0, A1)
glm+ all.equal(A1, A2)
glm+ all.equal(A1, glm.D93$aic)
glm+ })
glm> ## No test:
glm> ##D ## an example with offsets from Venables & Ripley (2002, p.189)
glm> ##D utils::data(anorexia, package = "MASS")
glm> ##D
glm> ##D anorex.1 <- glm(Postwt ~ Prewt + Treat + offset(Prewt),
glm> ##D family = gaussian, data = anorexia)
glm> ##D summary(anorex.1)
glm> ## End(No test)
glm>
glm> # A Gamma example, from McCullagh & Nelder (1989, pp. 300-2)
glm> clotting <- data.frame(
glm+ u = c(5,10,15,20,30,40,60,80,100),
glm+ lot1 = c(118,58,42,35,27,25,21,19,18),
glm+ lot2 = c(69,35,26,21,18,16,13,12,12))
glm> summary(glm(lot1 ~ log(u), data = clotting, family = Gamma))
Call:
glm(formula = lot1 ~ log(u), family = Gamma, data = clotting)
Deviance Residuals:
Min 1Q Median 3Q Max
-0.04008 -0.03756 -0.02637 0.02905 0.08641
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) -0.0165544 0.0009275 -17.85 4.28e-07 ***
log(u) 0.0153431 0.0004150 36.98 2.75e-09 ***
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
(Dispersion parameter for Gamma family taken to be 0.002446059)
Null deviance: 3.51283 on 8 degrees of freedom
Residual deviance: 0.01673 on 7 degrees of freedom
AIC: 37.99
Number of Fisher Scoring iterations: 3
glm> summary(glm(lot2 ~ log(u), data = clotting, family = Gamma))
Call:
glm(formula = lot2 ~ log(u), family = Gamma, data = clotting)
Deviance Residuals:
Min 1Q Median 3Q Max
-0.05574 -0.02925 0.01030 0.01714 0.06371
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) -0.0239085 0.0013265 -18.02 4.00e-07 ***
log(u) 0.0235992 0.0005768 40.91 1.36e-09 ***
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
(Dispersion parameter for Gamma family taken to be 0.001813354)
Null deviance: 3.118557 on 8 degrees of freedom
Residual deviance: 0.012672 on 7 degrees of freedom
AIC: 27.032
Number of Fisher Scoring iterations: 3
glm> ## Aliased ("S"ingular) -> 1 NA coefficient
glm> (fS <- glm(lot2 ~ log(u) + log(u^2), data = clotting, family = Gamma))
Call: glm(formula = lot2 ~ log(u) + log(u^2), family = Gamma, data = clotting)
Coefficients:
(Intercept) log(u) log(u^2)
-0.02391 0.02360 NA
Degrees of Freedom: 8 Total (i.e. Null); 7 Residual
Null Deviance: 3.119
Residual Deviance: 0.01267 AIC: 27.03
glm> tools::assertError(update(fS, singular.ok=FALSE), verbose=interactive())
glm> ## -> .. "singular fit encountered"
glm>
glm> ## Not run:
glm> ##D ## for an example of the use of a terms object as a formula
glm> ##D demo(glm.vr)
glm> ## End(Not run)
glm>
glm>
Rprof()
# 2. 프로파일링 결과 요약
summaryRprof(tmp)
$by.self
self.time self.pct total.time total.pct
"eval" 0.04 66.67 0.06 100.00
"remap" 0.02 33.33 0.02 33.33
$by.total
total.time total.pct self.time self.pct
"eval" 0.06 100.00 0.04 66.67
"block_exec" 0.06 100.00 0.00 0.00
"call_block" 0.06 100.00 0.00 0.00
"evaluate" 0.06 100.00 0.00 0.00
"evaluate::evaluate" 0.06 100.00 0.00 0.00
"evaluate_call" 0.06 100.00 0.00 0.00
"handle" 0.06 100.00 0.00 0.00
"in_dir" 0.06 100.00 0.00 0.00
"knitr::knit" 0.06 100.00 0.00 0.00
"process_file" 0.06 100.00 0.00 0.00
"process_group" 0.06 100.00 0.00 0.00
"process_group.block" 0.06 100.00 0.00 0.00
"rmarkdown::render" 0.06 100.00 0.00 0.00
"timing_fn" 0.06 100.00 0.00 0.00
"withCallingHandlers" 0.06 100.00 0.00 0.00
"withVisible" 0.06 100.00 0.00 0.00
"example" 0.04 66.67 0.00 0.00
"remap" 0.02 33.33 0.02 33.33
"glm" 0.02 33.33 0.00 0.00
"of0" 0.02 33.33 0.00 0.00
"of1" 0.02 33.33 0.00 0.00
"paste0" 0.02 33.33 0.00 0.00
"render" 0.02 33.33 0.00 0.00
"renderDont" 0.02 33.33 0.00 0.00
"source" 0.02 33.33 0.00 0.00
"summary" 0.02 33.33 0.00 0.00
"tools::Rd2ex" 0.02 33.33 0.00 0.00
"writeLines" 0.02 33.33 0.00 0.00
"WriteLines" 0.02 33.33 0.00 0.00
$sample.interval
[1] 0.02
$sampling.time
[1] 0.06
Rprof
시각화숫자를 통해 프로파일링 결과를 살펴보는 것도 의미가 있지만, 시각화를 통해 전체적인 프로파일링 정보를 살펴본다. profr
팩키지를 설치하고 ggplot2
시각화 팩키지로 프로파일링된 결과를 살펴본다.
일반선형모형을 적합시킨 결과 콜스택 깊이가 20까지 올라간 것도 확인이 된다.
##========================================================================
## Rprof 시각화
##========================================================================
# install.packages("profr")
library(profr)
library(ggplot2)
profr(example(glm))
x <-ggplot(x)
diamonds
회귀분석 프로파일링 예제profr
로 시각화하는데 data("diamonds")
부터 abline(lm.m, col = "blue")
까지 R코드 실행부분을 별도 R스크립트로 저장하였다.
##========================================================================
## 다이아몬드 회귀분석
##========================================================================
# 0. 팩키지 가져오기
library(tidyverse)
# 1. 텍스트 정보
Rprof(tmp <- tempfile())
data("diamonds")
# 1. 산점도
plot(price ~ carat, data=diamonds)
# 2. 선형모형 적합
lm(price ~ carat, data=diamonds)
lm.m <-# 3. 데이터와 모형 적합 평가
abline(lm.m, col = "blue")
Rprof()
summaryRprof(tmp)
$by.self
self.time self.pct total.time total.pct
"plot.default" 0.80 83.33 0.94 97.92
"paste" 0.08 8.33 0.08 8.33
"deparse1" 0.02 2.08 0.10 10.42
"Axis.default" 0.02 2.08 0.02 2.08
"get" 0.02 2.08 0.02 2.08
"plot.new" 0.02 2.08 0.02 2.08
$by.total
total.time total.pct self.time self.pct
"block_exec" 0.96 100.00 0.00 0.00
"call_block" 0.96 100.00 0.00 0.00
"eval" 0.96 100.00 0.00 0.00
"evaluate" 0.96 100.00 0.00 0.00
"evaluate::evaluate" 0.96 100.00 0.00 0.00
"evaluate_call" 0.96 100.00 0.00 0.00
"handle" 0.96 100.00 0.00 0.00
"in_dir" 0.96 100.00 0.00 0.00
"knitr::knit" 0.96 100.00 0.00 0.00
"plot" 0.96 100.00 0.00 0.00
"process_file" 0.96 100.00 0.00 0.00
"process_group" 0.96 100.00 0.00 0.00
"process_group.block" 0.96 100.00 0.00 0.00
"rmarkdown::render" 0.96 100.00 0.00 0.00
"timing_fn" 0.96 100.00 0.00 0.00
"withCallingHandlers" 0.96 100.00 0.00 0.00
"withVisible" 0.96 100.00 0.00 0.00
"plot.default" 0.94 97.92 0.80 83.33
"do.call" 0.94 97.92 0.00 0.00
"plot.formula" 0.94 97.92 0.00 0.00
"deparse1" 0.10 10.42 0.02 2.08
"paste" 0.08 8.33 0.08 8.33
"Axis.default" 0.02 2.08 0.02 2.08
"get" 0.02 2.08 0.02 2.08
"plot.new" 0.02 2.08 0.02 2.08
"Axis" 0.02 2.08 0.00 0.00
"localAxis" 0.02 2.08 0.00 0.00
$sample.interval
[1] 0.02
$sampling.time
[1] 0.96
상기 코드를 스크립트 파일(diamonds-prof-ex.R
)로 저장시킨 후에 이를 실행시킨다.
# 2. 시각화
library(profr)
library(ggplot2)
profr(source("code/diamonds-prof-ex.R")) x <-
ggplot(x)
profvis
팩키지profvis
팩키지 profvis()
함수를 사용하게 되면 R마크다운 문서에도 관련 내용을 담을 수가 있고 앞선 내용을 최신 방식으로 RStudio IDE 에서도 실행이 가능하다.
::profvis({
profvis##========================================================================
## 다이아몬드 회귀분석
##========================================================================
# 0. 팩키지 가져오기
library(tidyverse)
# 1. 텍스트 정보
data("diamonds")
# 1. 산점도
plot(price ~ carat, data=diamonds)
# 2. 선형모형 적합
lm(price ~ carat, data=diamonds)
lm.m <-# 3. 데이터와 모형 적합 평가
abline(lm.m, col = "blue")
height = "250px") },
데이터 과학자 이광춘 저작
kwangchun.lee.7@gmail.com