12 Temmuz 2012 Perşembe

JAXB Marshaller/Unmarshaller Oluşturma Yavaşlığı


Merhaba,

Performans testleri esnasında server-side uygulamamızın beklenilenden çok daha düşük kapasite ile çalıştığının görülmesi üzerine duruma bir el attım. İlginç bir darboğaz yakaladım. Concurrent çalışan bir noktada bulunan metotlar içerisinde önce JAXBContext instance'ları oluşturulup ardından marshaller ve unmarshaller objeleri oluşturuluyordu. Yük altında bu JAXBContext objelerinin oluşturulması gerçekten yavaş olabiliyormuş. Statik tek bir JAXBContext objesi oluşturup marshaller/unmarshaller objelerini bunun üzerinden oluşturmak gerekiyor. JAXBContext thread-safe bir sınıf. Gönül rahatlığıyla tek instance üzerinden marshaller/unmarshaller newInstance çağırılabilir.

Öncelikle kısaca JAXB'den bahsedeyim. Açılımı Java Architecture for Xml Binding olan bu kütüphane java nesneleri ile xml'leri birbirine adreslememizi sağlar. İki yönlüde çalışabilmektedir. Java objelerinden xml oluşturulabilir (marshal). Yada tam tersi şekilde XML'den Java objeleri oluşturulabilir (unmarshal).

Aşağıda marshaller/unmarshaller objelerinin nasıl oluşturulduğu ve kullanıldığı görülebilir:
...
 JAXBContext jc = JAXBContext.newInstance(MyClass1.class);
 Marshaller m = jc.createMarshaller();
...
JAXBContext jc = JAXBContext.newInstance(MyClass2.class);
Unmarshaller u = jc.createUnmarshaller();
...




Küçük bir load test yazalım. Aşağıdaki sınıf içerisinde 150 thread ile 100 bin defa önce JAXBContext objeleri oluşturup ardından unmarsaller objeleri oluşturuyoruz.


public class TestJaxbUtil1 {
    private static AtomicCounter ctr = new AtomicCounter();
    public static void main(String[] args) throws JAXBException, InterruptedException {
        ExecutorService tPool = Executors.newFixedThreadPool(150);        for (int i = 0; i < 100000; i++) {
            Runnable r = new Runnable() {
                @Override
                public void run() {
                    try {
                        long t1 = System.currentTimeMillis();
                        JAXBContext jc = JAXBContext.newInstance(MyClass.class);
                        jc.createUnmarshaller();
                        long t2 = System.currentTimeMillis();
                        ctr.increment();
                        System.out.println("Test# : " + ctr.value() + " : " + (t2 - t1) + " ms");
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            };
            tPool.execute(r);
        }
    }
}

Bu test esnasında lokalimde 9-10 saniyeleri gördüğüm oldu. Yapılan iyileştirme JAXBContext objesinin statik yapılıp marshaller/unmarshaller'ların bu tek instance üzerinden oluşturulmasından ibaret. Kod aşağıda:


public class TestJaxbUtil2 {
    private static AtomicCounter ctr = new AtomicCounter();
    private static JAXBContext   jc;
    public static void main(String[] args) throws JAXBException, InterruptedException {
        jc = JAXBContext.newInstance(MyClass.class);
        ExecutorService tPool = Executors.newFixedThreadPool(150);
        for (int i = 0; i < 100000; i++) {
            Runnable r = new Runnable() {
                @Override
                public void run() {
                    try {
                        long t1 = System.currentTimeMillis();
                        jc.createUnmarshaller();
                        long t2 = System.currentTimeMillis();
                        ctr.increment();
                        System.out.println("Test# : " + ctr.value() + " : " + (t2 - t1) + " ms");
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            };
            tPool.execute(r);
        }
    }
}


Bu test sonuçlarına göre unmarshaller oluşturulması en fazla 2 ms sürmüş. Çoğu durumda makul bir süre sayılır :)



java.lang.NumberFormatException: For input string: "�"


Merhaba,


Bugün ilginç bir problem yaşayıp uzun aramalar sonucu çözebildik. Logta aralıklarla akan java.lang.NumberFormatException: For input string: "�"  hatası görüyorduk. 


Stack trace şöyleydi:
java.lang.NumberFormatException: For input string: "�"at sun.misc.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:1280)at java.lang.Double.valueOf(Double.java:486)at java.lang.Double.<init>(Double.java:578)...


Öncelikle problemi çok başka yerlerde aradık.  "�" karakterleri UTF8 header'larına ait karakterler. Bir şekilde konfigürasyon dosyalarımızın encoding'i ile ilgili problem olabileceğini düşündük. Bu dosyalarla epey boğuştuktan sonra debug session'larımızdan birinde hatayı yakaladık. Hata demek haksızlık olur, hatalar zinciri diyelim :)


Öncelikle hatalı class içinde 2  double değişkeni birbirine bölüp yeni bir double elde ediyoruz:
double total = x / y;


Bölenimiz(y) bazen 0 gelebiliyormuş. Bu yüzden bir exception atılmıyor, bölüm değeri (total) NaN (Not A Number) olarak oluşuyor.


Sonrasında bu total değeri DecimalFormat ile formatlanıyor:
DecimalFormat df = new DecimalFormat("#.##");df.format(total);


Tataaam, alın size "�" :)  Çözüm çok basit, bölme işleminde bölen için 0 olup olmama kontrolü.



1 Mart 2012 Perşembe

DVCS incelemesi - GIT ve Mercurial


Yazılım dünyası DVCS (Distributed Version Control System)’lere geçerken seyirci kalıp CVS’i sorgulamamak ayıp olurdu. Bu yazımda CVS’te olmayıp DVCS’lerin (özellikle Mercurial ve GIT’in) bize vaad ettiklerini, DVCS’lerin birbirlerine göre üstünlüklerini-ezikliklerini anlatmaya çalışacağım. 

Tarihçe 
Öncelikle DVCS’lerden önceki furya olan CVS ve SVN gibi araçların eski olduklarından, yazıldıkları dönemlere göre dizayn edildiklerinden bahsetmek gerek. Biraz tarih dersi vermek gerekirse CVS 1986 yılında, SVN’de 2000 yılında yazılmaya başlanmış. Bu araçların dizayn edildiği zamanlarda dağıtık (distributed) çalışma, günümüzdeki farklı lokasyonlardan - uzaktan çalışan insanların bulunduğu foundation projelerinde olduğu kadar önemli olmadığı için öyle dizayn edilmemişler. Ortada bir tane repository var ve tüm geliştiriciler bu repository’ye kod girişi yapıyorlar. Lokal repository gibi bir kavram söz konusu değil.

Uygulanabilirlik
Bu noktada “bizde şunun şurasında aynı yerde çalışan insanlarız, bu zımbırtılara ihtiyacımız yok” diyebilirsiniz, saygı duyarım =) Ama bu distributed çalışma olanağı öncelikle kolaylıkla ve lineer olmayan branching/merging yapmaya izin verdiği için bizimde işimize yarayabilir. Lineer branch history’den biraz daha bahsetmek gerekirse svn’de örnek bir proje geçmişi ve dallanması aşağıdaki şekilde olabilir:


Şekil 1: Visualization of a simple Subversion project [1]

Agile çalışıp SCRUM yapanlar sprint bazlı development branch’lere çıkmak isteyebilir. Hatta bununda ötesinde her developer’ın kendi branch’i olup unit testi veya code review’ları tamamlanmayan kodun sprint branch’ine konulmaması gibi uçuk uygulamalarda bulunabiliriz. Örneğin aşağıdaki akış biraz karışık görünsede oluşturan arkadaş epey geniş düşünmüş ve bütün olası problemleri bertaraf etmeye çalışmış, faydalı bir dizayn olmuş:

 
Şekil 2: A successful GIT branching model [2]

Bu kadar branch’i / merge’ü CVS’te yaptığınızı düşünsenize !
 

GIT & Mercurial vs CVS

Bu temel farkın dışında modern araçların CVS’e üstünlüklerini maddeler halinde sıralamak gerekirse: 
1) CVS atomik işlem yapmadığı için yarıda kalan bir commit sistemi stabil olmayan bir durumda bırakabilir. Veya bir dizi dosyayı commit etmek istersiniz ancak bazıları commit edilip bazıları edilmeyebilir. GIT ve Mercurial’da commitler atomic, dolayısıyla sistem daha stabil. 
2) CVS’te değişikler dosya bazlı tutulurken GIT ve Mercurial’da changeset commit yapılıyor. Bu sayede bir commit silsilesini geri almak istediğimizde CVS’te tek tek dosyaları bir önceki versiyona çekmemiz gerekirken DVCS’lerde changeset’i geri almak kafi. Çözüm olarak CVS’te manual tag’leme yapılmalı. 
3) CVS’te klasörlerin tarihçesi tutulmuyor. Birinci sınıf nesne değil, versiyonlanmıyor. 
4) CVS ile dosya veya klasörleri taşımak veya yeniden adlandırmak mümkün değilken modern DVCS araçlarında bu mümkün. 
5) CVS’te commit’ler üstünde geniş bir kontrol hakkı yok. Örneğin commit öncesi code review şartı koymak pek mümkün ve uygulanabilir değil. Sadece bazı CVS hook script’leriyle pre-commit  ve post-commit yapılabiliyor. 
6) DVCS’lerde branching/merging günlük hayatın bir parçası olarak kabul edilmiş durumda. CVS’te bunlar birer öcü =) CVS’te branch ve tag açmak, yönetmek ve silmek zor.

GIT vs Mercurial

GIT ve Mercurial’ın CVS’e üstünlükleri konusunda tartışma konusu olacak pek birşey yok. Ancak Mercurial vs GIT dendiği noktada tartışmalar şiddetleniyor. Google’ın bile buna bulaştığını ve DVCS Analizi [3] isminde bir döküman hazırladığını belirtmem gerekiyor.
GIT ve Mercurial aslında büyük ölçüde birbirlerine benziyorlar. Ancak kendi içlerinde kıyaslamak gerekirse, birbirlerine göre avantaj/dezavantajları şöyle listelenebilir:


GIT
Mercurial
GIT commit, pull, merge gibi operasyonlarda daha hızlı
Mercurial büyük ölçüde pyhton ile yazılmış. Native olarak Windows desteği sunuyor. GIT’te ise cygwin, mingw, msysGit gibi unix porting’leri üzerine kurulum yapabiliyorsunuz. Ki bu durumda GIT hız avantajını kaybetmiş oluyor


GIT branch’leri ref adı altında bir referans objesi ile (pointer to commit) tutuyor. Bu sayede bu pointer’ları (dolayısıyla branch’leri) kolaylıkla yeniden isimlendirebiliriz, silebilir, fetch ve push yapabiliriz. Mercurial’da ise branch’ler üzerinde bu kadar rahat operasyonlara izin verilmiyor. Branch’ler silinemiyor, sadece kapatılabiliyor
Mercurial’daki komut ve seçenek sayısı GIT’ten daha az. Bu yüzden öğrenmesi daha kolay. Ayrıca komut isimleride geleneksel VCS’lere daha çok benziyor


Bu noktada DCVS’lere olası bir geçişin özellikle çok sık ve fazla sayıda branch’e çıkma - merge etme gibi ihtiyaçlardan ortaya çıktığını hatırlatıp bu konuda daha başarılı olduğundan GIT’in öne çıktığını belirtmem gerekiyor.


Birazda piyasada bu araçlar ne durumda, kimler kullanıyor, hangisi daha popüler gibi soruları cevaplamaya çalışayım:

GIT :

- Linux Kernel, Eclipse, Android, Fedora, GNOME, Perl, VLC vs GIT’e geçmiş durumda.
- Eclipse plugin’i mevcut. Eclipse projeleri kodlarıda GIT ile maintain edildiği için native olarak destekliyor. Ayrıca Eclipse plugin’i Mercurial’ın Eclipse plug’inine göre 3 kat daha popüler.
- Syntevo’nun SmartGIT isimli ücretsiz ve cross-platform olan programı ile Eclipse dışındanda başarıyla kullanılabilir.

Mercurial:

- Mozilla, OpenJDK, OpenOffice, Atlassian [4], Python, Netbeans vs Mercurial kullanmakta.
- Eclipse plugin’i mevcut. Ancak GIT pluginine göre 3’te bir oranında popüleriteye sahip. Dolayısıyla Eclipse camiasında daha az destekçisi bulunuyor.
- GUI olarak open-source ve cross-platform olan TortoiseHg’ye sahip.


GIT & Mercurial Komut Listesi

GIT ve Mercurial’ın komut listesine gelirsek şöyle bir tablo ile karşılaşıyoruz:

Git
Mercurial
git pull
hg pull -u
git fetch
hg pull
git reset --hard
hg revert -a --no-backup
git revert <commit>
hg backout <cset>
git add <new_file>
hg add <new_file> (Only equivalent when <new_file> is not tracked.)
git add <file>
Not necessary in Mercurial.
git add -i
hg record
git commit -a
hg commit
git commit --amend
hg qimport -r tip ; hg qrefresh -e ; hg qfinish tip (Requires the MqExtension.)
git blame
hg blame or hg annotate
git blame -C
not available in Mercurial?
git bisect
hg bisect
git rebase --interactive
hg histedit <base cset> (Requires the HisteditExtension.)
git stash
hg shelve (Requires the ShelveExtension or the AtticExtension.)
git merge
hg merge
git cherry-pick <commit>
hg graft <cset>
git rebase <upstream>
hg rebase -d <cset> (Requires the RebaseExtension.)
git format-patch <commits> and git send-mail
hg email -r <csets> (Requires the PatchbombExtension.)
git am <mbox>
hg mimport -m <mbox> (Requires the MboxExtension and the MqExtension. Imports patches to mq.)
git checkout HEAD
hg update
git log -n
hg log --limit n

Tablo1: GIT ve Mercurial komut listesi [5]

 

Referanslar:

2 Şubat 2012 Perşembe

ConcurrentHashMap - Concurrency Level

Merhaba,

Çok thread'li uygulamalarda sıkça kullandığımız java.util.concurrent.ConcurrentHashMap sınıfının concurrencyLevel diye, default değeri 16 olan bir değişkeni mevcut. (Detaylar için bknz: http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ConcurrentHashMap.html)

Yük testi yaparken yoğun olarak kullanılan bir HashMap'te saniye mertebesine uzayan update ve get işlemleri görünce araştırma gereği hissettim. Şayet 1 thread'in yazacağı, diğerlerinin sadece okuyacağı bir map ise bu değerin constructor'da 1 verilmesi tavsiye ediliyor.

Gereğinden büyük bir değer verilmesi zaman kaybına ve gereksiz memory kullanımına yol açıyor. İdeal değerini optimize ederek geliştirilen uygulamaya yönelik olarak seçmek gerekiyor.

Yaptığım testlerde bizim uygulama alanımız için bu değerin 100 olmasına karar verdim. Okuma - yazma sürelerimiz olması gerektiği gibi milisaniye mertebelerine düştü. Hatta tüm akışta yük altında her istek için 2-3 saniye kazancımız olduğunu söyleyebilirim. 

Etkili bir iyileştirme oldu !



Android Phone Storage - Sabit hafıza boyutu

Merhaba, 

Uzun bir aradan sonra arkadaşımın düzenli yazdığı blogu görünce yazasım geldi. 

Şirketimiz çalışanlarına HTC Wildfire S cihazı dağıttı. (Spesifikasyonlarına gsmarena'dan bakabilirsiniz: http://www.gsmarena.com/htc_wildfire_s-3777.php)  Akıllı telefonların hızla büyüdüğü, kocaman olduğu, cebe avuca sığmadığı günümüzde küçük, pratik ve şık bir cihaz olmasından dolayı ilk başta epey hoşuma gittiğini itiraf etmeliyim. Ancak ucuz etin yahnisi misali kullandıkça büyük eksiklikleri olduğunu farkettim. Cihazın kullanıcının kullanımına açtığı dahili hafızası 418 MB. Bununda büyük kısmı işletim sistemi dosyaları, HTC'nin önyüklü olarak gelen ve kaldırılamayan uygulamaları vs gibi şeylerle dolu durumda. Dolayısıyla kullanıcıya kalan yaklaşık 100 MB alan mevcut. 

Severek kullandığım, önceki telefonumda tanıştığım navigasyon uygulaması iGo'yu, çok gerekli olduğunu düşündüğüm QuickOffice'i kurunca bana çok çok az alan kaldı. Cihazda toplam indirilip kurulmuş uygulama sayısı sadece 16. Cihazın hafıza kartına taşımaya izin verdiği uygulamaları zaten taşıdım, kullanıcı dosyalarını temizledim, yinede bana kalan alan 15.8 MB. Bunuda kullanamıyorum zira Android platformunda internal memory flash olduğu için ram olarakta kullanılıyor. Daha da azaltırsam cihaz kasılmaya başlıyor.

Velhasılı kelam akıllı olmasını uygulama potansiyeline borçlu olan telefonuma çok sağlam kriterlerle seçmek zorunda kaldığım, 5 tanesi oyun olmak üzere toplam sadece 16 uygulama kurabilmek epey gücüme gitti !

Biraz araştırınca piyasadaki birçok cihazın dahili hafızasının 1 GB bile olmadığını gördüm. Android platformu için elzem olan dahili hafıza'yı üreticilerin büyük maliyet olarak görüp cihazlara 160 MB, 280 MB, 512 MB gibi komik boyutlarda donanımlar koyması saçma.

Özetle Android işletim sistemli bir telefon alırken işlemcisinin yanında özellikle dahili hafıza boyutuna bakılmasını, en azından 1-2 GB olanlarının satın alınmasını tavsiye ediyorum.