shared'e Geçiş
dmd'nin 2.030 sürümünden başlayarak, C ve C++'da olduğunun aksine, statik ve evrensel (global) değişkenler artık evrensel alanda değil, her iş parçacığına özel alanda (thread local storage - TLS) bulunuyorlar. Çoğu D programının bu değişimden hiç etkilenmeden derlenmesi ve çalışması beklense de, bilinmesi gereken bazı konular vardır:
- TLS değişkenlerinin hızları
- TLS değişkenlerinin hangileri olduklarını bilebilmek
immutableolarak işaretlemek
sharedolarak işaretlemek
__gsharedile klasik alana yerleştirmek
- Derleyici hataları
- Bağlayıcı hataları
TLS değişkenlerinin hızları
Evet, iş parçacığına özel değişkenleri okumak ve yazmak klasik evrensel değişkenlerden daha yavaştır. Tek bir mikro işlemci işlemi yerine üç işlem gerekeceği düşünülebilir; ancak, en azından Linux üzerinde, derleyici seçeneği olarak PIC (position independent code) seçildiğinde, TLS değişkenlerinin klasik evrensel değişkenlerden az da olsa daha hızlı olmaları beklenir. Bu yüzden hız konusunda bir sorun olacağını düşünmek gerçekçi olmaz. Yine de hız sorunu olacağını bir an için kabul ederek neler yapılabileceğine bakalım:
- Evrensel değişkenleri azaltın. Evrensel değişken kullanımın azaltılması; kodun bağımsızlığını ve geliştirilebilirliğini arttıracağı için, bu zaten hedeflenen bir amaçtır.
- Evrensel değişkenleri
immutableyapın.
immutableverilerin iş parçacıkları tarafından ortak kullanılmaları ve eş zamanlı çalışma sorunları olmadığı için derleyici onları TLS'ye yerleştirmez.
- Evrensel değişkenin yerel bir kopyasını kullanın. Asıl değişken yerine yerel kopyasının kullanılması erişimin daha hızlı olmasını sağlar.
- Değişkeni
__gsharedile normal evrensel değişken haline getirin.
TLS değişkenlerinin hangileri olduklarını bilebilmek
Kendileriyle ilgili bu konuda herhangi bir işlem yapılabilmesi için evrensel değişkenlerin öncelikle tanınmaları gerekir.
Programların ne kadar karmaşık olabilecekleri göz önüne alınırsa, evrensel değişkenleri bulmak her zaman kolay olmayabilir. Eskiden otomatik olarak evrensel olduklarından, onları
grep programıyla da ayırt edemeyiz. Bulduğumuz kadarının da evrensel değişkenlerin tümü olduğundan emin olamayız.
Bu konuda yardımcı olması için yeni bir
dmd seçeneği eklenmiştir:
-vtls. Program o seçeneği kullanarak derlendiğinde, TLS alanına yerleştirilen bütün değişkenler listelenir:
int x; void main() { static int y; }
dmd test -vtls test.d(2): x is thread local test.d(6): y is thread local
immutable olarak işaretlemek
immutable veriler bir kere ilk değerlerini aldıktan sonra değiştirilemezler. Bu, çoklu iş parçacıkları kullanıldığında eş zamanlı çalışmayla ilgili erişim sorunlarının ortadan kalkması anlamına gelir. Bu yüzden,
immutable verilerin TLS'e yerleştirilmelerine de gerek yoktur. Derleyici bu tür değişkenleri TLS'e değil, güvenle klasik evrensel alana yerleştirir.
Programlarda kullanılan evrensel değişkenlerin büyük bir bölümü bu türdendir.
immutable olarak işaretlenmeleri yeter:
int[3] tablo = [6, 123, 0x87];
yerine
immutable int[3] tablo = [6, 123, 0x87];
immutable'ın getirdiği değişmezlik kavramının başka bir yararı, derleyiciye daha fazla eniyiliştirme [optimization] olanağı sunmasıdır.
shared olarak işaretlemek
Gerçekten birden fazla iş parçacığı tarafından erişilen veriler
shared olarak işaretlenmelidir:
shared int birDurum;
Bu,
birDurum'un klasik evrensel alana yerleştirilmesi yanında, türünün de
shared olmasına neden olur:
int* p = &birDurum; // hata: birDurum shared'dir shared(int)* q = &birDurum; // çalışır
const ve
immutable'da olduğu gibi,
shared de geçişlidir. Böylece paylaşılan verilere erişimlerin doğruluğu da derleme zamanında denetlenebilir.
__gshared ile klasik alana yerleştirmek
Yukarıdaki çözümlerin kullanılamadığı zamanlar olabilir:
- klasik evrensel değişkenler kullanan C kodu kullanıldığında
- programı çabucak derlenebilir hale getirerek, daha uygun çözümün daha sonraya bırakıldığında
- program tek iş parçacığı kullandığında [single-threaded]; bu durumda zaten eş zamanlı erişim sorunları yoktur
- en ufak hız kaybı bile önemli olduğunda
- eş zamanlı erişim konusunu kendiniz halletmek istediğinizde
D bir sistem dili olduğu için, size bu serbestiyi
__gshared anahtar sözcüğü ile sağlar:
__gshared int x; void main() { __gshared int y; }
__gshared, değişkeni klasik evrensel alana yerleştirir.
Doğal olarak, safe modda
__gshared kullanılamaz.
Kodun içindeki
__gshared değişkenler daha uygun çözümlerin uygulanması için kolayca bulunabilirler.
Derleyici hataları
TLS ile ilgili en çok karşılaşılacak hata herhalde şudur:
int x; int* p = &x;
test.d(2): Error: non-constant expression & x
(Türkçesi: "sabit olmayan ifade")
Klasik evrensel değişkenlerle normalde derlenebilen o ifade, TLS değişkenleriyle derlenemez. Bunun nedeni, TLS değişkenlerinin adreslerinin ne bağlayıcı [linker] ne de yükleyici [loader] tarafından bilinebilmesidir. Bu değişkenlerin adresleri çalışma zamanında hesaplanır.
Çözüm, bu tür değişkenleri statik kurucu işlevlerde ilklemektir.
Bağlayıcı hataları
Evrensel değişkenlerle ilgili bağlayıcı hataları da alabilirsiniz. Çoğu durumda bunların nedeni; değişkenin bir modül tarafından TLS'e yerleştirmesine karşın, başka bir modül tarafından klasik evrensel alana yerleştirilmesidir. Böyle bir durum; program, dmd'nin önceki bir sürümü ile derlenmiş olan kütüphanelerle bağlanırken oluşabilir. Kullanılan
libphobos2.a kütüphanesinin yeni sürümle gelen yeni kütüphane olduğundan emin olun.
Bu durumla C ile etkileşirken de karşılaşılabilir. C, TLS değişkenlerini de destekliyor olsa da; C'de evrensel değişkenler için varsayılan yerleşim, klasik evrensel yerleşimdir. Bu gibi durumlarda D iliştirici [binding] dosyalarındaki bildirimlerin TLS ve klasik yerleşim konularında C'dekilere uyduklarından emin olmak gerekir:
C
int x; extern int y; __thread int z;
D
extern (C) { extern shared int x; shared int y; extern int z; }