2016-06-24 24 views
5

Aşağıdaki fonksiyonu şu vektörler için örneğinif/C++ yazmadan ifelse() hızlandırmak?

getScore <- function(history, similarities) {  
    nh<-ifelse(similarities<0, 6-history,history) 
    x <- nh*abs(similarities) 
    contados <- !is.na(history) 
    x2 <- sum(x, na.rm=TRUE)/sum(abs(similarities[contados]),na.rm=TRUE) 
    x2 
    } 

sonuçlarını almak gerekir:

bir döngü içinde değişir
notes <- c(1:5, NA) 
history <- sample(notes, 1000000, replace=T) 
similarities <- runif(1000000, -1,1) 

. Bu alır:

ptm <- proc.time() 
for (i in (1:10)) getScore(history, similarities) 
proc.time() - ptm 

    user system elapsed 
    3.71 1.11 4.67 

Başlangıçta Sorunun for döngü olduğundan şüpheleniyorsanız, ama ifelse() için sonuç noktaları profilleme.

Rprof("foo.out") 
for (i in (1:10)) getScore(history, similarities) 
Rprof(NULL) 
summaryRprof("foo.out") 

$by.self 
      self.time self.pct total.time total.pct 
"ifelse"  2.96 65.78  3.48  77.33 
"-"    0.24  5.33  0.24  5.33 
"getScore"  0.22  4.89  4.50 100.00 
"<"    0.22  4.89  0.22  4.89 
"*"    0.22  4.89  0.22  4.89 
"abs"   0.22  4.89  0.22  4.89 
"sum"   0.22  4.89  0.22  4.89 
"is.na"   0.12  2.67  0.12  2.67 
"!"    0.08  1.78  0.08  1.78 

$by.total 
      total.time total.pct self.time self.pct 
"getScore"  4.50 100.00  0.22  4.89 
"ifelse"   3.48  77.33  2.96 65.78 
"-"    0.24  5.33  0.24  5.33 
"<"    0.22  4.89  0.22  4.89 
"*"    0.22  4.89  0.22  4.89 
"abs"   0.22  4.89  0.22  4.89 
"sum"   0.22  4.89  0.22  4.89 
"is.na"   0.12  2.67  0.12  2.67 
"!"    0.08  1.78  0.08  1.78 

$sample.interval 
[1] 0.02 

$sampling.time 
[1] 4.5 

ifelse() performansım darboğaz olduğunu. ifelse()'u hızlandırmak için R'de bir yol bulunmadıkça, büyük bir performans artışı olasılığı yoktur.

Ancak ifelse() zaten vectorized yaklaşımdır. Bana kalan tek şansın C/C++ kullanmak olduğunu düşünüyorum. Ama derlenmiş kodu kullanmaktan kaçınmanın bir yolu var mı?

+1

Yalnızca zaten çalışan kodları optimize etmek istiyorsanız, bu bir StackReverlight sorusu değil CodeReview sorusudur. http://codereview.stackexchange.com/ –

cevap

5

Bunu daha önce karşılaştım. Her zaman ifelse()'u kullanmak zorunda değiliz. ifelse'un nasıl yazıldığına bakacak olursanız, R konsolunuza "ifelse" yazarak, bu fonksiyonun R dilinde yazıldığını görebilirsiniz ve bu gerçekten çok verimsiz olan çeşitli kontroller yapar.

getScore <- function(history, similarities) { 
    ######## old code ####### 
    # nh <- ifelse(similarities < 0, 6 - history, history) 
    ######## old code ####### 
    ######## new code ####### 
    nh <- history 
    ind <- similarities < 0 
    nh[ind] <- 6 - nh[ind] 
    ######## new code ####### 
    x <- nh * abs(similarities) 
    contados <- !is.na(history) 
    sum(x, na.rm=TRUE)/sum(abs(similarities[contados]), na.rm = TRUE) 
    } 

Sonra tekrar profilleme sonucunu kontrol edelim:

yerine ifelse() kullanmak yerine, bunu yapabiliriz

Rprof("foo.out") 
for (i in (1:10)) getScore(history, similarities) 
Rprof(NULL) 
summaryRprof("foo.out") 

# $by.total 
#   total.time total.pct self.time self.pct 
# "getScore"  2.10 100.00  0.88 41.90 
# "abs"   0.32  15.24  0.32 15.24 
# "*"    0.26  12.38  0.26 12.38 
# "sum"   0.26  12.38  0.26 12.38 
# "<"    0.14  6.67  0.14  6.67 
# "-"    0.14  6.67  0.14  6.67 
# "!"    0.06  2.86  0.06  2.86 
# "is.na"   0.04  1.90  0.04  1.90 

# $sample.interval 
# [1] 0.02 

# $sampling.time 
# [1] 2.1 

Biz 2+ kez performansında artırmak zorunda. Dahası, profil, herhangi bir parçanın yürütme süresine hakim olmayan, düz bir profile benziyor. R ise

vektör indeksleme/okuma/yazma biz, bir vektörün kullanımı, böylece her ne zaman Cı-kod hızında bir.


Test @ Matthew cevabı

mat_getScore <- function(history, similarities) { 
    ######## old code ####### 
    # nh <- ifelse(similarities < 0, 6 - history, history) 
    ######## old code ####### 
    ######## new code ####### 
    ind <- similarities < 0 
    nh <- ind*(6-history) + (!ind)*history 
    ######## new code ####### 
    x <- nh * abs(similarities) 
    contados <- !is.na(history) 
    sum(x, na.rm=TRUE)/sum(abs(similarities[contados]), na.rm = TRUE) 
    } 

Rprof("foo.out") 
for (i in (1:10)) mat_getScore(history, similarities) 
Rprof(NULL) 
summaryRprof("foo.out") 

# $by.total 
#    total.time total.pct self.time self.pct 
# "mat_getScore"  2.60 100.00  0.24  9.23 
# "*"     0.76  29.23  0.76 29.23 
# "!"     0.40  15.38  0.40 15.38 
# "-"     0.34  13.08  0.34 13.08 
# "+"     0.26  10.00  0.26 10.00 
# "abs"    0.20  7.69  0.20  7.69 
# "sum"    0.18  6.92  0.18  6.92 
# "<"     0.16  6.15  0.16  6.15 
# "is.na"    0.06  2.31  0.06  2.31 

# $sample.interval 
# [1] 0.02 

# $sampling.time 
# [1] 2.6 

Ah

? Yavaş?

tam profilleme sonuç

bu yaklaşım noktası çarpma "*" yüzen daha fazla zaman harcıyor gösterir ve mantıksal değil "!" oldukça pahalı görünüyor. Benim yaklaşımım sadece kayan nokta ekleme/çıkarma gerektirir.

Eh, sonuç da mimari bağımlı olabilir. Şu anda Intel Nahalem (Intel Core 2 Duo) üzerinde test yapıyorum. Yani çeşitli platformlarda iki yaklaşım arasındaki kıyaslama memnuniyetle karşılıyor.

Tüm profilleme


Açıklama Söz konusu OP'ın verilerini kullanıyor.

+1

Core 2 duo, Nehalem mimarisi öncesi ve bu farkın bir parçası olabilir. Sandy Bridge i7-3740QM üzerinde test yapıyorum. Burada bir Core 2 makine var –

+0

, çarpma sıklıkla fark etmez ek daha yüksek talimat gecikmesi, sahipken beni bunun –

+1

Nehalem Açık ve ötesinde üzerinde microbenchmark karşılaştıralım. Talimatlar sıra dışıdır ve emekli talimatların sayısı önemli olan şeydir. Veri bağımlılıkları olmadan, her iki talimat da bir saat onayında "emekli" olacak. Seninle aynı, özel bir BLAS kullanmıyorum. Bu yarın koşarak bir Rprof yapmaktan mutlu olacağım, ama gece için emekli olmanın neredeyse zamanı geldi. –

7

Aynı etkiyi elde etmek için bu görev için mantıksal çarpma kullanabilirsiniz: i7-3740QM üzerinde

s <- similarities < 0 
nh <- s*(6-history) + (!s)*history 

Benchmark:

f1 <- function(history, similarities) { s <- similarities < 0 
             s*(6-history) + (!s)*history} 
f2 <- function(history, similarities) ifelse(similarities<0, 6-history,history) 
f3 <- function(history, similarities) { nh <- history 
             ind <- similarities<0 
             nh[ind] <- 6 - nh[ind] 
             nh } 

microbenchmark(f1(history, similarities), 
       f2(history, similarities), 
       f3(history, similarities)) 
## Unit: milliseconds 
##      expr  min   lq   mean    median   uq  max neval cld 
## f1(history, similarities) 22.830260 24.6167695 28.31384860 24.89869950000000 25.651655 81.043713 100 a 
## f2(history, similarities) 364.514460 412.7117810 408.37156626 415.10114899999996 417.345748 437.977256 100 c 
## f3(history, similarities) 84.220279 86.2894795 92.64614571 87.18016549999999 89.616522 149.243051 100 b 

v2 E5-2680 Açık:

## Unit: milliseconds 
##      expr  min  lq  mean median  uq  max neval cld 
## f1(history, similarities) 20.03963 20.10954 21.41055 20.68597 21.25920 50.95278 100 a 
## f2(history, similarities) 314.54913 315.96621 324.91486 319.50290 325.93168 378.26016 100 c 
## f3(history, similarities) 73.81413 73.92162 76.10418 74.79893 75.84634 105.98770 100 b 
T5600 Üzerine

(Core2 Duo Mobile):

## Unit: milliseconds 
         expr  min  lq  mean median  uq  max neval cld 
## f1(history, similarities) 147.2953 152.9307 171.0870 155.5632 167.0998 344.7524 100 b 
## f2(history, similarities) 408.5728 493.3886 517.0573 501.6993 525.8573 797.9624 100 c 
## f3(history, similarities) 102.9621 110.6003 131.1826 112.9961 125.3906 303.1170 100 a 

Aha! Benim yaklaşımım Core 2 mimarisinde daha yavaş.

0

Yukarıdaki yanıtlardan daha hızlı olmamasına rağmen, daha hızlı bir ifelse, ifelse yapısını korur.

ifelse_sign <- function(b,x,y){ 

    x[!b] <- 0 
    y[b] <-0 

    x + y + b *0 
} 
İlgili konular