geri

Senkronize olmayan java.util.Set gerçeklemelerinin performans karşılaştırması

29/04/2012

Yine bir Pazar günü evde aylaklık edip uzun zamandır birikmiş olan Twitter favorilerimi eritmeye çalışırken hangi durumda hangi java.util.Set gerçeklemesinin kullanılması gerektiği konusunda net bir fikrim olmadığını farkettim. Bu sebeple senkronize olmayan java.util.Set gerçeklemelerini performans açısından karşılaştırarak dokümantasyonlarında yazan bilgileri doğrulamak istedim. Yaptığım testte HashSet, LinkedHashSet ve TreeSet için temel Set işlevlerini(add, remove, contains, size ve iterator) 1,000,000 kez tekrarladım ve geçen süreyi ölçtüm. Elde ettiğim sonuçlar bu üç gerçeklemenin de dokümantasyonlarında yazdığı şekilde çalıştığını gösterdi. Sonuçları aşağıdaki tabloda paylaşıyorum. Süreler µs(1/1000ms) cinsindendir.

add remove contains size iterator
HashSet 2039583 970396 1032553 4176 44500
LinkedHashSet 2158727 987442 1073558 11339 34027
TreeSet 4754283 4287016 4394679 12125 114829

Tabloyu şöyle özetleyebiliriz. HashSet ve LinkedHashSet performansı birbirine oldukça yakın. Fazladan yönetilmesi gereken LinkedList sebebiyle LinkedHashSet add, remove ve contains işlevlerinde HashSet'e göre çok az da olsa kötü performans sergiliyor. Buna karşılık LinkedHashSet'in iterator performansı HashSet'e göre %30 kadar daha iyi. TreeSet ise gerçekten Set elemanlarının sıralanması istenmiyorsa yanına yaklaşılmaması gereken bir gerçekleme olarak kendini gösteriyor.

Bu sonuçlara bakarak ben varsayılan gerçekleme olarak LinkedHashSet ile yoluma devam etmeye karar verdim. Başka bir Pazar günü de senkronize olan gerçeklemelere bulaşmaya çalışacağım.

Bu arada http://implement.asyonturkcedegil.com/

Güncelleme

Ahmet A. Akın ve Mehmet D. Akın'ın önerileriyle test kodunu ve sonuç analizini güncelledim. Testleri art arda 3 kez çalıştırıp sonuçları add, remove ve contains için ayrı tablolarda gösterdim. Süreler ms cinsindendir. Denemek isterseniz Ahmet A. Akın'ın biraz daha geliştirdiği test kodu burada.

add testi Test 0 Test 1 Test 2
HashSet 1021 563 697
LinkedHashSet 978 984 800
TreeSet 3830 3569 3837
remove testi Test 0 Test 1 Test 2
HashSet 489 497 686
LinkedHashSet 749 789 550
TreeSet 3786 3440 3291
contains testi Test 0 Test 1 Test 2
HashSet 264 261 418
LinkedHashSet 359 432 334
TreeSet 3331 3247 3245

 

Ahmet A. Akın:
Eğer ciddi bir bellek ve performans kaygım yoksa, - Ekleme sırası önemsiz ise HashSet - Ekleme sırası önemli ise LinkedHashSet - Nesnelerin doğal sıralanışı önemli ise TreeSet gönül rahatlığı ile kullanıyorum. Yani bu durumda hızdan çok işe göre yapıyı seçmek daha makul. Özel durumlar için ise özel kütüphaneleri tercih edilebilir. Guava, muhtelif primitive collections vs.
Mikrobenchmarking'i bu devirde doğru yapmak neredeyse mümkün değil (http://www.parleys.com/#id=2103&st=5 http://wiki.jvmlangsummit.com/images/1/1d/PerformanceAnxiety2010.pdf). O nedenle kafayı küçük farklara takmamak iyidir derim. Sonuçta farklı işletim sistemleri, işlemciler ve JVM sürümleri ile testler yapılıyor.
Mehmet D. Akın:
size() metodunu olcmek cok anlamli degil cunku hepsi de sadece nesnenin icindeki size ismindeki bir integerin degerini donduruyor, rakamlardaki farklilik gürültuden ibaret.
Microbenchmarking cok ince bir is, ozellikle Java gibi JVM userinde calisan ve GC iceren bir ortamda islemci, isletim sistemi, JVM versiyonu ve daha baska bir cok etken sonuclari degistirebiliyor.

Follow me on Twitter