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/
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