Bir Algoritma Bağlamında Güzellik ve Performans – II
Bir önceki yazıya kaldığımız yerden devam edelim.
Tüm bu ölçümler sonuçta JVM’ler üzerinde herhangi bir oynama yapmadan elde edildiler. Algoritmanın performansını arttırmak için yapılacak bir şey var mı diye bakınca aslında algoritmanın kodunda zaten ufak bir-iki iyileştirmenin yapıldığı görülmektedir. Yani algoritmada
double distance = Math.sqrt(Math.pow(x, 2) + Math.pow(y,2));
satırı
double distance = x * x + y * y;
satırı ile değiştirilmişti. Bu ise bizi hem Math.sqrt() ile kare kök almaktan hem de her döngüde iki defa Math.pow() çağrısı yapmaktan kurtardı. Yukarıdaki kodu Math.sqrt() ve Math.pow() metot çağrıları yapacak şekilde değiştirince, n=1000000 için örneğin MacBook Pro’daki 41,70 mili second olan süre yaklaşık 46 miliseconda, n=10000000 için de 415,90 mili seconddan 445 mili seconda çıkıyor. Bu demektir ki biz zaten kodumuzda bu iki metot çağrısını yapmayarak yaklaşık %10luk bir performans artışı kazandık.
Bir başka hızlandırma noktası da rastgele sayı üretimi olabilir. Açıkçası JDK ile gelen yapıların o konudaki en hızlı en optimize kodlar olmadığını tecrübemle biliyorum. Örneğin daha önce de java.util.StringTokenizer‘ı kendim geliştirdiğim çok daha hızlı ve amacım açısından optimize edilmiş bir başka tokenizer ile değiştirmiştim. Aynı şeyi java.lang.Math sınıfındaki random() metodu için de yapabiliriz. Bu durumda ilk akla gelen java.util.Random sınıfı oluyor. Bu sınıfın nextDouble() metodunu kullanınca performansta pek bir değişik olmadı. Sebebi de tam tahmin ettiğim gibi, java.lang.Math sınıfının random() metotu zaten java.util.Random sınıfını kullanıyor 🙂
Apache Commons’daki Math kütüphanesinde de bayağı rastgele veri üreten yapılar var. Dolayısıyla buradaki bir kaç yapıyı denedim ve org.apache.commons.math.random paketindeki MersenneTwister sınıfı mükemmel sonuç verdi. Bu sınıfı kullanmak için koddaki sınıfa
private static BitsStreamGenerator randomData = new MersenneTwister();
satırını ekledim ve
double x = Math.random(); double y = Math.random();
satırlarını
double x = randomData.nextDouble(); double y = randomData.nextDouble();
ile değiştirdim. Macbook Pro üzerindeki şu sonuçlara bakın:
n | Süre (ms) | Error (%) |
10 | 0,00 | 16,92 |
100 | 0,10 | 2,91 |
1.000 | 0,30 | 1,34 |
10.000 | 1,30 | 0,4112 |
100.000 | 3,60 | 0,139 |
1.000.000 | 19,50 | 0,047539 |
10.000.000 | 188,70 | 0,013136 |
100.000.000 | 1898,00 | 0,004824 |
1.000.000.000 | 19534,90 | 0,001324 |
Bu tablodan çıkan sonuç şu, n=10.000 için henüz bir performans ilerlemesi yok ama daha yüksek n sayıları için MersenneTwister sınıfı mükemmel bir performans gösteriyor. Örneğin n=10.000.000 için %57lik, n=100.000.000 için %53lük, n=1.000.000.000 için ise benzer şekilde %52lik bir performans ilerlemesi elde ettik. Burada Apache Commons Math kütüphanesinin hem 2.2 hem de 3.3 sürümlerini kullandığımı ama yukarıdaki sonuçları 2.2 ile aldığımı belirtmeliyim. İlginç bir şekilde 3.3 sürümü, 2.2’ye göre yaklaşık %10 daha yavaş çalıştı. Tabi rastgele sayı üretmek zor iştir, üretilen sayıların rastgele olması açısından olabildiğince bağımsız olması önemlidir. Burada kullandığım MersenneTwister sınıfı için bu konuda daha ayrıntılı bilgi almak isteyenler buraya bakabilirler.
Muhtemelen daha hızlı rastgele sayı üreten yapılar bulunup bu performans daha da arttırılabilir.
Bir sonraki yazıda ise JVM tuning ile neler yapabiliriz buna bakalım.
Toplam görüntülenme sayısı: 902