Benchmark Nasıl Yapılır?

Yazdığınız fonksiyonun ne kadar hızlı çalıştığını görmek veya iki farklı fonksiyon arasından daha hızlı olanı bulmak için uygulanan hız ve performans analizlerine benchmark testleri adı verilir. (Aslına bakılırsa, benchmark her türlü ölçüm ve karşılaştırma işlemlerine denir.) Bu yazıda, R içinde benchmark testi yapabilmemizi sağlayan birkaç yola değineceğiz.

Bu noktada performans testine neden ihtiyaç duyacağınızı düşünebilirsiniz. Sonuçta günlük çalışmalarda yazdığımız kodların büyük bir kısmı birkaç saniye içinde tamamlanıp sonuç veriyor. Gerçekten de tek bir işlemin 1 veya 5 saniyede tamamlanması pek önemli olmayabilir. Peki ya bu işlem 1000 defa tekrarlanıyor ise? Bu durumda fonksiyondaki 4 saniyelik bir yavaşlama katlanarak 1 saatlik bir zaman kaybına yol açabilir. Bu sebeple, uzun işlem sürelerine sahip kodları çalıştırmadan önce, küçük ölçekli veriler ile birden fazla yaklaşım deneyerek ve benchmark uygulayarak en hızlı olanın seçilmesi anlam kazanıyor.

1. Basit Yol:

Bir fonksiyonun ne kadar süre aldığını kullanmak için en basit yol, R’ın çekirdek fonksiyonlarından Sys.time() kullanmaktan geçiyor. Bu fonksiyon, çalıştığı andaki zamanın çıktısını veriyor. Süresini ölçmek istediğiniz işlemin başında ve sonunda Sys.time() çalıştırıp ve çıktılarını birbirinden çıkardığımızda bize harcanan süreye ulaşabiliyorsunuz. Bu şekilde geçen süreyi basitçe öğrenmek mümkün.

Bu arada, örnek olarak Sys.sleep() adlı fonksiyonu kullandık. Bu fonksiyon, R arayüzünü belirttiğiniz süre boyunca bekletmeye yarıyor.

> start <- Sys.time()
> Sys.sleep(1)
> stop <- Sys.time() 
> stop-start 
# Time difference of 1.025316 secs

2. Pratik Yol:

R ile birlikte gelen bir diğer fonskiyon olan system.time(), bu işlemi biraz daha kolaylaştırıyor. Fonksiyon, içine yazdığınız herhangi bir komutun süresini size doğrudan iletebiliyor. Bu fonksiyon oluşturduğu 5 farklı çıktıdan 3 tanesini size doğrudan gösteriyor.

Bunlar sırasıyla, user, system ve elapsed değerleri. user (user CPU time) değeri, belirttiğiniz işlem için R tarafından harcanan CPU süresini; system (system CPU time) ise işletim sistemi tarafından harcanan CPU süresini gösteriyor. elapsed ise fonksiyonun tamamlanması için geçen gerçek süreyi belirtiyor.

> system.time(Sys.sleep(1))
# user  system elapsed
# 0.003 0.003  1.002

3. Profesyonel Yol:

Bundan daha profesyonel bir benchmark yapmak istiyorsanız, microbenchmark gibi ek paketler yardımınıza koşuyor. Bu paket ile, bir fonksiyonun ne kadar sürede gerçekleştiğine dair tekrarlı ölçümler yapmak mümkün.

Komut içinde belirtilecek times parametresi ile ilgilendiğiniz fonksiyonun süresini birden fazla tekrarda ölçülmesini sağlayabiliyorsunuz. unit parametresi ile ölçümde kullanılacak zaman birimini değiştirebiliyorsunuz. Örneğin, “ns” nanosaniye. “us” mikrosaniye. “ms” milisaniye. “s” saniye vs. şeklinde.

> library("microbenchmark")
> bm <- microbenchmark(Sys.sleep(1), times = 10, unit="s") 
> bm
# Unit: seconds
# expr              min       lq     mean   median      uq      max neval
# Sys.sleep(1) 1.000206 1.000494 1.001372 1.001359 1.00215 1.002604 10
 
> bm$time
[1] 1000666025 1001361411 1000630117 1001003650 1002548837

Elbette burada bahsedilen yollar dışında da süreyi ölçmeyi sağlayan başka yol ve paketler de (tictoc gibi) mevcut. Ancak, aynı işe yarayan onlarca farklı yolu anlatıp sıkmak istemedim. Umarım işinize yararlar.

Uygulama

Şimdi yukarıda bahsettiğimiz microbenchmark uygulamasını gerçek bir örnekte deneyelim. Bu örnekte, R’ın çekirdek fonksiyonu olan ve standart sapma hesaplayan sd() fonksiyonu ile aynı işi sağlayacak kendi yazacağımız kod ile karşılaştıralım.

# Standart sapma hesaplayan betik
> sd.m    <- function(x) {
              sqrt(sum((x-mean(x))^2) / (length(x)-1))}

# Örnek veriseti oluşturma
> sayilar <- rnorm(1000,50,5)

# Validasyon
> sd.m(sayilar) == sd(sayilar)
TRUE

# Benchmark ve Veri Düzenleme
# Çekirdek ve elle yazılan fonksiyonun sürelerini kaydedip tablo oluşturuyoruz.
> base.sure   <- microbenchmark(sd(sayilar), times=100) 
> manual.sure <- microbenchmark(sd.m(sayilar), times=100)
> tablo <- data.frame(Base=base.sure$time,            
                      Manual=manual.sure$time)

# Görselleştirme
boxplot(tablo)


Sonuçlara baktığımızda ilginç bir şekilde çekirdek fonksiyonun daha yavaş olduğunu görebiliyoruz. Bunun sebebi, kullandığımız örnek setinin çok küçük olması. Daha büyük verisetlerinde çekirdek fonksiyonları çok daha başarı sağlayabiliyor. Her durumda, elimizdeki veriseti için çekirdek fonksiyondan daha hızlı çalışan bir fonksiyon yazmayı başardığımız gözüküyor. (yey!)

Moleküler Biyolog ve Genetikçi.İstanbul Teknik Üniversitesi'nde doktora öğrencisi. 5 yıldır biyoinformatik ve yeni nesil dizileme üzerine çalışıyor. Genetik ve biyoinformatik üzerine Variant adlı blogda yazıyor.

Yorum bırakın:

Your email address will not be published.

Site Footer

Sliding Sidebar

Hakkımda

Hakkımda

Moleküler Biyolog ve Genetikçi. İstanbul Teknik Üniversitesi'nde doktora öğrencisi. 5 yıldır biyoinformatik ve yeni nesil dizileme üzerine çalışıyor.

Tweets

Message: Invalid or expired token., Please check your Twitter Authentication Data or internet connection.