JAVA-ANDROID Performance Part2-SparseArray
Java-Android Performance Part1 makalesine buradan erişebilirsiniz.
Collections(Koleksiyonlar),yazılım geliştirmede yaygın olarak kullanılan şeylerdir.Genel olarak verileri toplu olarak saklarken kullandığımız,ve özellikle verileri anahtar-değer(key-value) çiftleri şeklinde saklamamız gerektiğinde kaçınılmaz olarak kullanırız. Bu koleksiyonların bazıları yapısal olarak kısmen veya tamemen Autoboxing işleyişi üzerine tasarlanmıştır. Ancak bu işleyişler,önceki makalede görüldüğü gibi performans yönünden ciddi kayıplarla karşı karşıya kalmamıza sebeptirler.Şayet “Key-Value” yapısıyla çalışan koleksiyonlardan birini ele alırsak,HashMap bu konuda en öne çıkanlar arasındadır. Çünkü HashMap oldukça esnek bir yapıdadır ve dolayısıyla Key-Value çiftlerini saklamak için en uygun ve en çok tercih edilen bir veri yapısı seçimidir.
Özellikle mobil dünyasında performans ve depolama alanları daha da önem kazanmıştır. Bundan dolayı mobil sistem tasarımcıları performans arttırmaya ve aynı zamanda mevcut depolama alanlarını da efektif kullanımı için yeni tasarımlara yönlenmişlerdir.Bu bağlamda Android sistem ve uygulamalarının performasnını arttırmak için,Android sisteminde mobil geliştirme için derlenmiş birtakım koleksiyonlar yer alır. Bunlardan biri “ArrayMap” ve diğeri ise “SparseArray” dır. Bunlar verimli bellek kullanımında “HashMap” kullanmaktan daha verimlidirler. Ancak ArrayMap ve SparseArray ikilisi birbirleriyle karşılaştırılırsa,SparseArray’da Key her zaman primitive(ilkel,önceki makalede bunlara yer verildi) veri türlerdir.Dolayısıyla böylece Autoboxing’den uzak kalmaktayız.Ve bu makalede “SparseArray” incelenecektir.
Autoboxing işlemlerinden uzak durmak performans yönünden kazançlı olacağımızı belirtmiştik. Ancak SparseArray’ın özelliği sadece bununla sınırlı değil. Bir diğer önemli özelliği ise SparseArray içerisinde dizi elemanını silerken performansa yardımcı olmak için birtakım optimizasyon içerir.
Bu optimizasyonları incelemek için HashMap ve SparseArray kaynak kodlarını inceleyebiliriz.
Aşağıda HashMap ve SparseArray’da eleman(Item) silme işlemine ait kaynak kodlar verilmiştir.
HashMap:
HashMap’da eleman silindikten hemen sonra dizi sıklaştırma(veya sıkıştırma denilebilir) işlemi uygulanır. Yani silinen eleman yerine sağda solda bulunan elemanlar silinenin yerine kaydırılarak sıklaştırma işlemi uygulanır.
Bu sıklaştırma işlemi performans yönünden oldukça etkili bir kayıptır. Şimdi ise SparseArray eleman silme işlemini inceleyelim ve ne yaptığına bakalım.
SparseArray:
SparseArray’da eleman silindikten hemen sonra HashMap gibi sıklaştırma işlemi uygulanmaz. Bunun yerine silinen elemanın yerine “Silindi(DELETED)” işareti bırakılır. Yani silinen eleman “DELETED” olarak işaretlenir. Daha sonra sıklaştırma işlemi belirli durumlara göre uygulanır veya hiç uygulanmaz.İşaret için kullandığı nesne ise aslında boş bir object alanıdır.
private static final Object DELETED = new Object();
Diğer Delete Ve Remove Fonksiyonlar:
SparseArray’da yer alan diğer remove ve delete fonksiyonları ile silme işlemleri yukarıda verildiği gibidir.Örnek olması açısından aşağıda verilen removeAt fonksiyonu verilmiştir.
Görüldüğü gibi eleman(Item) silme işlemi tamamen aynıdır.
DELETED Ve Garbage:
Bunu yaparak temel olarak iki kazanım elde eder. İlki sıklaştırma işlemi yapmadığı için büyük bir performans kaybından kurtulur. İkincisi ise silinen eleman yeri-alanı daha sonra aynı Key(anahtar) için yeniden kullanılabilir haldedir. Dolayısıyla yeni eleman atama işlemi için ekstra dizi içinde yer açmak yerine,boş olan bu yer-alan kullanılır. Bu da bir diğer en önemli performans kazanımıdır.
Elbette “DELETED” olarak işaretlemek dışında bir de “mGarbage” true olarak işaretleniyor. Böylece bazı özel durumlarda çöp toplama sisteminin devreye girmesi sağlanıyor.Bu özel durumlar:
- Ekleme işlemi sırasında alana ihtiyaç varsa ve daha önce silme işlemi yapılmışsa.
- Dizi boyutu alınırken-çağrılırken ve daha önce silme işlemi yapılmışsa.
- Belirtilen index değerine karşılık gelen Key alınırken-çağrılırken ve daha önce silme işlemi yapılmışsa.
- Belirtilen index değerine karşılık gelen Value(değer) alınırken-çağrılırken ve daha önce silme işlemi yapılmışsa.
- Belirtilen bir index’e bir Value(değer) atanırken ve daha önce silme işlemi yapılmışsa.
- Belirtilen bir Key’e ait index değeri alınırken-çağrılırken ve daha önce silme işlemi yapılmışsa.
- Belirtilen bir Value’a ait index değeri alınırken-çağrılırken ve daha önce silme işlemi yapılmışsa.
Aslında class içinde bir çok işlemde çöp toplama sistemi çağrılıyor.
Ancak çöp toplama işlemi çağrılmadan önce,daha önce silme işleminin yapılıp yapılmadığı şartı kontrol ediliyor. Eğer silme işlemi yapılmışsa ve diğer şartlar sağlanıyorsa çöp toplama sistemi çağrılır.
Class içinde çöp toplama sistemi “gc()” fonksiyonu ile çağrılmaktadır.
Aşağıda bazı fonksiyonlara ait kaynak kodlara yer verilmiştir.
Çöp toplama “put()” fonksiyonunda çağrılıyor:
Çöp toplama “size()” fonksiyonunda çağrılıyor:
keyAt() fonksiyonunda çağrılıyor:
SparseArray Fonksiyonları:
SparseArray Autoboxing sorununu ortadan kaldırmak için tasarlanmıştır.Dolayısıyla Key her daim primitive’dir. Ancak ArrayMap için bunu söyleyemeyiz. Dolayısıyla ArrayMap Autoboxing sorununu ortadan kaldırmaz.
SparseArray ile ilgili örnek kullanıma geçmeden önce,aşağıda bu class’a ait fonksiyonlara yer verilmiştir.
-void append(int key, E value) //diziye bir Key-Value değer çifti ekler.
-void clear() //SparseArray’dan tüm Key-Value eşleşmelerini kaldırır. Diziyi boşaltır.
-SparseArray<E> clone() //Dizinin bir kopyasını oluşturur ve döndürür.
-boolean contains(int key) //Key dizi içinde yer alıyorsa true döndürür.
-void delete(int key) //Key’i belirtilen değer çiftini eğer varsa diziden kaldırır.
-E get(int key) //Key olarak belirtilen Value’ı veya null döndürür.
-E get(int key, E valueIfKeyNotFound) //Key olarak belirtilen Value’ı,eğer dizide yer almıyorsa ikinci parametreyle belirtilen nesneyi döndürür.
-int keyAt(int index) //index’i belirtilen Key’i alır. Unutmayın;index değerinin ilk elemanı “0” ile başlar ve size() -1 olarak biter.
-E valueAt(int index) //index’i belirtilen Value’u alır.
-void put(int key, E value) //diziye bir Key-Value değer çifti ekler.Eğer varsa önceki Key-Value eşleşmesini değiştirir.
-void remove(int key) //delete(int key) ile aynıdır.
-void removeAt(int index) //index’i belirtilen Key-Value eşleştirmesini kaldırır-siler.
-size() //Mevcut dizi boyutunu döndürür.
SparseArray Örnek:
SparseArray classlar primitive tabanlı çalıştığı için çeşitli veri yapılarında isimlendirilmiştir.
Bunlar:
-SparseArray
-SparseLongArray
-SparseBooleanArray
-SparseIntArray
Aşağıda bu class’ların tanımlamalarına yer verilmiştir:
Sonuç:
SparseArray ile HashMap’ı karşılaştırdığımızda SparseArray’ın öne çıkan avantajları kısaca şöyledir.
- Primitive(ilkel) veri tipleri kullandığı için belleği daha vermili kullanır.
- Autoboxing kullanmaz.
- Bellek-Alan tahsisi ve boşaltımı yapmaz.
Aşağıda 1000 elemanlı bir SparseArray ve HashMap<Integer,Integer> bellek kullanımlarına yer verilmiştir- Kaynak :
SparseIntArray:
class SparseIntArray {
int[] keys;
int[] values;
int size;
}
Class = 12 + 3 * 4 = 24 bytes
Array = 20 + 1000 * 4 = 4024 bytes
Total = 8,072 bytes
HashMap:
class HashMap<K, V> {
Entry<K, V>[] table;
Entry<K, V> forNull;
int size;
int modCount;
int threshold;
Set<K> keys
Set<Entry<K, V>> entries;
Collection<V> values;
}
Class = 12 + 8 * 4 = 48 bytes
Entry = 32 + 16 + 16 = 64 bytes
Array = 20 + 1000 * 64 = 64024 bytes
Total = 64,136 bytes
Yukarıdaki hesaplamalar JVM tarafından heap üzerinde ayrılan bellek(byte cinsinden) miktarlarıdır. Bir nesnenin boyutunu hesaplamak için “java.lang.instrument” paketinden faydalanabilirsiniz.
Paketi incelemek için Link.
Dezavantajları ise:
- Büyük dizi içerikleri için yavaştır.
- Sadece Android için kullanılabilir.
Diğer Kaynaklar:
-end of