İngilizce Kaynaklar


Diğer




shared'e Geçiş

Çeviren: Ali Çehreli
Tarih: 13 Aralık 2009
İngilizcesi: Migrating to Shared

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:

  1. TLS değişkenlerinin hızları
  2. TLS değişkenlerinin hangileri olduklarını bilebilmek
  3. immutable olarak işaretlemek
  4. shared olarak işaretlemek
  5. __gshared ile klasik alana yerleştirmek
  6. Derleyici hataları
  7. 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:

  1. 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.
  2. Evrensel değişkenleri immutable yapın. immutable verilerin iş parçacıkları tarafından ortak kullanılmaları ve eş zamanlı çalışma sorunları olmadığı için derleyici onları TLS'ye yerleştirmez.
  3. 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.
  4. Değişkeni __gshared ile 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:

  1. klasik evrensel değişkenler kullanan C kodu kullanıldığında
  2. programı çabucak derlenebilir hale getirerek, daha uygun çözümün daha sonraya bırakıldığında
  3. program tek iş parçacığı kullandığında [single-threaded]; bu durumda zaten eş zamanlı erişim sorunları yoktur
  4. en ufak hız kaybı bile önemli olduğunda
  5. 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;
}