|
Kod Optimizasyonu ve "volatile" Anahtar Sözcüğü |
|
Gönderiliyor lütfen bekleyin... |
|
|
Bu yazıda C#'ın önemli
ama tam olarak neden kullanıldığı bazı profesyonel programcılar tarafından bile
pek fazla bilinmeyen bir anahtar sözcük olan volatile üzerinde duracağız.
Bir çok popüler derleyici sizin isteğiniz dışında kodunuzun işleyiş mantığına
müdahale edebilmektedir. Bu müdahalenin en bilinen sebeplerinden birisi
uygulumanızın kod boyutunu küçültmek yada uygulamanızın çalışma zamanını
düşürmektir. Aslında biz bu işlemlerin tamamına birden optimizasyon da
diyebiliriz. Zira hemen hemen bütün derleyicilerin optimizasyon işleminin
yapılıp yapılmayacağını belirten bir parametresi vardır. C# derleyicisi için bu
parametre /optimize yada kısaca /o şeklindedir.
Peki optimizastondan neyi anlamalıyız? Genel olarak iki farklı şekilde
optimizasyondan bahsetmek mümkündür. Birincisi daha henüz derleme
aşamasındayken programcının gözünden kaçan bazı gereksiz bildirimlerin veya
tanımlamaların derleyici tarafından derlenecek koda dahil edilmemesi ile
olabilir. Örneğin hiç kullanmadığınız bir değişken için bellekte bir alan
tahsis edilmesinin hiç bir gereği yoktur. Bu yüzden hiç bir yerde kullanılmayan
değişkenlerin derleyiciniz tarafından derleme modülüne iletilmemesi bir
optimizasyon olarak görülmektedir. Bu tip optimizasyon kapsamı içinde ele
alınabilecek diğer bir örnek ise aşağıdaki kod parçası ile gösterilmiştir.
Not : Aşağıdaki kodun bilinçsiz yada dalgın bir programcı tarafından yazıldğını
varsayıyoruz.
int
a = 0;
while(a != 0)
{
a = 2 ;
a = 0 ;
}
|
Yukarıdaki kodda akış hiç bir zaman while bloğunun içine gelmeyecektir. Ve
üstelik eğer a değişkeni farklı bir iş parçacığı(thread) tarafından while
bloğuna girmeden değiştirilip akış while bloğuna girse bile a değişkeni while
bloğu içinde tekrar eski sabit değerine atanıyor. Dolayısıyla while bloğunda
bulunan kodların çalıştırılabilir uygulama dosyasının boyutunu büyütmekten
başka bir işe yaramayacağı açıktır. O halde burda insan üstü bir mekanizmanın
devreye girip kodu optimize etmesi gerekir. Bu mekanizma elbetteki derleyicinin
optimizasyon işine yarayan parametresidir. Optimizasyon işleminde derleyiciden
derleyiciye fark olmasına rağmen yukarıdaki kod parçasının geebileceği en
optimum biçim aşağıda gösterildiği gibidir.
int
a = 0;
while(a != 0)
{
}
|
Diğer bir optimizasyon biçimi ise derleyicinin değişkenlerin elde edilmesinde
yada tekrar yazılmasında belleğin yada işlemcinin tercih edilmesi ile
ilgilidir. Bu noktada mikroişlemciler ile ilgili kısa bir bilgi vermekte fayda
var : Kaynak kodumuz çalıştırılabilir durumdayken aslında makine koduna
çevrilmiştir. Bu komutlar daha önceden mikroişlemcilerde elektronik düzeyde
programlandıkları için bu komutlar tek tek mikroişlemcide kolaylıkla icra
edilir. Mikroişlemciler aynı anda tek bir iş yapabileceği için yapacağı
işlemler içinde kullandığı değişkenleri her defasında bellekten almak yerine
daha hızlı olması açısından mikroişlemcideki data register dediğimiz
kayıtçılarda tutar. Bu register dediğimiz bölümlerin sınırlı sayıda bulunduğunu
belirtmek gerekir. Dolayısıyla bellekte bulunan uygulamamızın ihtiyacına göre
daha doğrusu icra edilecek bir sonraki makine kodunun çeşidine göre işlemci
bellekten ilgili değişkenleri register'larına yükler ve ilgili komutunu
çalıştırır. Doğaldır ki bir değerin mikroişlemci tarafından belleğe(ram)
yazılması ile mikroişlemcideki register bölgesine yazılması arasında dağlar
kadar fark vardır. Bu fark elbette hız faktörüdür. İşte tam bu noktada ikinci
tip optimizasyon kuralını tanımlayabiliriz. Derleyici öyle bloklara
rastlayabilir ki, bu bloklar içinde bulunan bir değişkenin değerini her
defasında bellekten okuyacağına bu değişkenin değerini bir defaya mahsus olmak
üzere mikroişlemcinin ilgili register bölgesine bölgesine kaydeder ve sonraki
okumalarda işlemci bellek yerine bu register bölgesini kullanır. Böylece
kodunuzun çalışma süresinde önemli sayılabilecek bir azalma görülür. Elbetteki
bu optimizasyon işleminin yüzlerce kez tekrarlandığını varsayarak bu sonuca
varıyoruz.
Yukarıda değişken okuma ile ilgili söylediklerimin aynısı bir değişkenin
değerini değiştirmek için de geçerli olduğunu belirtmeliyim. Yani siz
programınızın 10. satırında bir değişkenin değerini bir değerden başka bir
değer çektiğiniz halde derleyici bu işlemi 15.satırda yapabilir. Bu durumda 15.
satıra kadar o değişkenin kullanılmadığı yorumunu yapabiliriz. Bu şekilde
mikroişlemcinin belleğe yazma işlemi geciktirilerek belli ölçüde optimizasyon
sağlanır. Tabi bu optimizasyonun ölçüsü tamamen mikroişlemcinin o anki durumuna
bağlıdır.
Buraya kadar herşey normal. Bir de madolyonun öteki yüzüne bakalım. Bildiğiniz
üzere uygulamalar genellikle çoklu iş parçacıklarından(multi thread) ve
proseslerden oluşur. Her bir proses diğer bir proses teki değişkene işletim
sisteminin izin verdiği ölçüde erişip üzerinde işlemler yapabilir. Aynı şekilde
bir iş parçacığıda diğer bir iş parçacığında bulunan değişkene erişip üzerinde
çeşitli işlemler yapabilir. Peki bunun bizim optimizasyon kurllarımızla
bağlantısı ne? Şöyle ki : derleyici bir değişkenin değerinin farklı bir iş
parçacağı tarafından yada farklı bir proses tarafından işleneceği üzerinde
durmaz. Bu tamamen işletim sisteminin yönetimindedir. Hal böyleyken bizim
yukarıda bahsettiğimiz ikinci optimizasyon tipi bazı durumlarda yarar
getireceğiniz zarar getirebilir. Zira optimizasyon adına bir değişkenin
değerini her defasında bellekten okuma yerine mikroişlemcideki ilgili register
dan okurken o anda farklı bir iş parçacağı yada farklı bir proses hatta ve
hatta işletim sistemi sizin erişmeye çalıştığınız değişkenin değerini sizin
uygulamanızın mantığına göre değiştirebilir. Bu durumda siz o değişkenin son
halini kullanmamış olursunuz. Dolayısıyla programınızda farklı thread lar yada
prosesler arasında paylaşılan veya işletim sistemi tarafından değiştirilmesi
muhtemel olan değişkenlerinizi optimizasyon kuralına tabi tutmamanız gerekir.
Peki bunu nasıl başaracağız?
volatile anahtar
sözcüğü burada imdadımıza yetişiyor. Bir değişkeni volatile anahtar sözcüğü ile
bildirdiğiniz takdirde derleyicinizin optimizasyon ile ilgili parametresini
açık tutsanız bile ilgili değişken yukarıda bahsi geçen tehlikeli optimizasyon
kurallarına tabi tutulmayacaktır. Yani volatile ile bildirilen değişkenlere
programın akışı sırasında her ihtiyaç duyulduğunda değişkenin gerçek yeri olan
belleğe başvurulur. Aynı şekilde bir değişkene yeni bir değer yazılacağı zaman
bu yazma işlemi hiç geciktirilmeden bellekteki yerine yazılır. Böylece volatile
ile bildirilen değişkenler farklı iş parçacıkları yada prosesler tarafından
ortak kullanılıyor olsada programın akışı içerisinde her zaman son versiyonu
elde edilecektir. Çünkü bu değişkenlerin değeri her defasında bellekten
çekilir. Her ne kadar optimizasyondan taviz verme zorunda kalsak ta böylece
uygulamalarımızda çıkabilecek olası bugların(böcek) önüne geçmiş oluruz.
volatile, C# dilindeki
anahtar sözcüklerden biridir. Üye değişken bildirimi ile birlikte kullanılır.
volatile anahtar sözcüğü yalnızca aşağıdaki değişken tipleri ile birlikte
kullanılabilir.
-
Herhangi bir referans
tipindeki değişken ile
-
byte, sbyte, short,
ushort, int, uint, char, float yada bool. türünden olan değişkenler ile
-
byte, sbyte, short,
ushort, int, yada uint türünden semboller içeren numaralandırmalar(enums) ile
-
unsafe modda iken
herhangi bir gösterici türü ile
volatile anahtar
sözcüğünün kullanımına bazı örnekler verelim :
public static volatile
int a;
public volatile bool a;
public volatile int* a;
....
|
Microsoft'un resmi
dökümanlarında(MSDN) verilen bir örneği buraya taşıyarak ne gibi durumlarda
volatile anahtar sözcüğüne ihtiyaç duyacabileceğimizi görelim.
using
System;
using System.Threading;
class
Test
{
public static int result;
public static volatile bool
finished;
static
void Thread2()
{
result = 143;
finished = true;
}
static void Main()
{
finished = false;
new
Thread(new ThreadStart(Thread2)).Start();
for
(;;)
{
if
(finished)
{
Console.WriteLine("result
= {0}", result);
return;
}
}
}
}
|
Yukarıdaki örnek
programdaki püf nokta finished isimli değişkenin ana thread ve ana thread
içinde başlatılan yeni thread tarafından ortak kullanılan bir değişken
olmasıdır. Eğer finished değişkeni volatile olarak bildirilmemiş olsaydı, akış
thread2 metoduna gelmiş olmasına rağmen Main metodu içindeki if bloğu
çalıştırılmayabilirdi. Çünkü derleyici ana thread içinden finished
değişkeninine tampolanmış bir bölgeden(register) erişebilir. Bu durumda
finished değişkeninin gerçek değeri true olmasına rağmen ana thread de finished
değişkeni halen false olarak ele alınır. Bu yüzden finished değişkeninin her
durumda son versiyonunu elde etmek için bu değişken volatile anahtar sözcüğü
ile bildirilmiştir.
volatile anahtar sözcüğünün kullanımına bir örnek daha verelim. Belli bir anda
bir sınıftan sadece bir nesnenin oluşmasını sağlayan
Singleton desenini daha önceki bir makalemde ele almıştım. Bu konu ile
ilgili bilgi eksikliğiniz varsa ilgili makaleyi okumanızı tavsiye ederim. Bahsi
geçen makalede verilen desenin bir uygulaması aşağıda ki gibi yeniden
yazılmıştır.
public
class SingletonDeseni
{
private
static volatile
SingletonDeseni nesne;
private static Object kanalKontrol
= new Object;
private SingletonDeseni()
{
}
public static
Singleton Nesne()
{
if(nesne
== null)
{
lock(kanalKontrol)
{
if(nesne
== null)
{
nesne
= new SingletonDeseni();
}
}
}
return
nesne;
}
}
|
Bu örnekte
SingletonDeseni nesnesinin belli bir anda tekil olarak bulunduğunu çok kanallı
uygulamalar içinde geçerli kılmak için bu nesne volatile olarak bildirilmiştir.
Üstelik bu örnekte farklı bir prosesin müdahalesi olsa bile bu nesneden ancak
ve ancak bir adet yaratılacaktır.
Son olarak volatile
kelimesinin sözlük anlamı üzerinde durmak istiyorum. İki yıl önce İngilizce'den
Türkçe'ye çevrilmiş bir Visual C++ kitabını okuduğumda volatile ile bildirilmiş
değişkenlerden oynak(!) değişkenler diye bahsedildiğine şahit oldum. İlk başta
bu ilginç kullanım bana birşey ifade etmedi ama hislerimin yardımıyla aslında
yazarın volatile dan bahsettiğine karar verdim. Sizde takdir edersiniz ki
yukarıda anlattıklarımız ile "oynak" kelimesi arasında pek bir bağ
bulunmamaktadır. Kitabın çevirisini yapan yazar muhtemelen bir programcı değil
bir çevirmendi. Çünkü eğer iyi bir programcı olsaydı oynak kelimesi yerine daha
uygun bir kelime seçilebilirdi. volatile'ın sözlük anlamı "uçucu olan", "buhar
olan" anlamına gelmektedir. Ancak ben henüz volatile sözcüğüne kendi
mesleğimizle ilgili uygun bir karşılık bulamadım. Bu konuda daha önce
cdili ve cdernek
isimli iki yahoo grubunda çeşitli tartışmalar olmuştur. Bu gruplara üye olarak
ilgili tartışmalarda geçen konuşmaları okumanızı öneririm. Eğer sizin de bu
konuda önerileriniz varsa bizimle paylaşırsanız seviniriz.
Umarım faydalı bir yazı olmuştur. Herkese iyi çalışmalar...
Makale:
Kod Optimizasyonu ve "volatile" Anahtar Sözcüğü C#, Visual C# ve .NET Sefer Algan
|
|
|
-
-
Eklenen Son 10
-
Bu Konuda Geçmiş 10
Bu Konuda Yazılmış Yazılmış 10 Makale Yükleniyor
Son Eklenen 10 Makale Yükleniyor
Bu Konuda Yazılmış Geçmiş Makaleler Yükleniyor
|
|