D Programlama Dili – Programlama dersleri ve D referansı
Ali Çehreli

eniyileştirme: [optimization], kodun daha hızlı çalışacak biçimde davranışı bozulmadan değiştirilmesi
kod içi işlev: [inline function], işlevin çağrılması yerine içeriğinin çağrıldığı noktaya yerleştirilmesi
mikro işlemci: [CPU], bilgisayarın beyni
ön bellek: [cache], hızlı veri veya kod erişimi için kullanılan mikro işlemci iç belleği
... bütün sözlük



İngilizce Kaynaklar


Diğer




Pragmalar

Pragma derleyiciyle etkileşme yöntemlerinden birisidir. Hem derleyiciye bilgi vermeye hem de ondan bilgi almaya yarar. Pragmalar şablonlardan başka kodlarda yararlı olsalar da, özellikle pragma(msg) şablonların hatalarını ayıklarken kullanışlıdır.

Her derleyici kendi özel pragmalarını tanımlayabilir ama aşağıdaki pragmalar standarttır:

pragma(msg)

Derleme zamanında stderr çıkış akımına mesaj yazdırmaya yarar; çalışma zamanına bir etkisi yoktur.

Örneğin, aşağıdaki pragma(msg) bir işlev şablonunun tam olarak hangi parametrelerle çağrıldığını bildirmektedir:

import std.string;

void işlev(A, B)(A a, B b) {
    pragma(msg, format("Şablon parametreleri: '%s' ve '%s'",
                       A.stringof, B.stringof));
    // ...
}

void main() {
    işlev(42, 1.5);
    işlev("merhaba", 'a');
}
Şablon parametreleri: 'int' ve 'double'
Şablon parametreleri: 'string' ve 'char'
pragma(lib)

Programın bağlanması gereken kütüphaneleri bildirmek için kullanılır. Programı sistemde kurulu olan bir kütüphaneyle bağlamanın en kolay yolu budur.

Örneğin, curl kütüphanesini kullanan aşağıdaki program kütüphaneyi derleme satırında belirtmek gerekmeden oluşturulabilir:

import std.stdio;
import std.net.curl;

pragma(lib, "curl");

void main() {
    // Kitabın bu bölümünü indirmek:
    writeln(get("ddili.org/ders/d/pragma.html"));
}
pragma(inline)

İşlev içeriğinin kod içine açılıp açılmayacağını belirler.

Her işlev çağrısının bir masrafı vardır. Bu masraf, işlevin varsa parametrelerinin kopyalanmaları, varsa dönüş değerinin çağırana döndürülmesi, ve sonlandıktan sonra hangi noktadan devam edileceğinin hesabının tutulması ile ilgilidir.

Bu masraf çoğu durumda işlevin kendisinin ve çağıran tarafın diğer işlemlerinin masrafları yanında dikkate alınmayacak kadar küçüktür. Ancak, bazı durumlarda salt işlev çağrısı bile programın hızını ölçülebilir derecede yavaşlatabilir. Bu, özellikle işlev içeriğinin göreceli olarak hızlı olduğu ve yine göreceli olarak küçük bir döngüden çok sayıda çağrıldığı durumlarda görülebilir.

Aşağıdaki program küçük bir işlevi yine küçük bir döngü içinden çağırmakta ve bir sayacın değerini işlevin dönüş değerine bağlı olarak arttırmaktadır:

import std.stdio;
import std.datetime.stopwatch;

// Oldukça hızlı bir işlev içeriği:
ubyte hesapla(ubyte i) {
    return cast(ubyte)(i * 42);
}

void main() {
    size_t sayaç = 0;

    StopWatch kronometre;
    kronometre.start();

    // Çok sayıda tekrarlanan küçük bir döngü:
    foreach (i; 0 .. 100_000_000) {
        const parametre = cast(ubyte)i;

        if (hesapla(parametre) == parametre) {
            ++sayaç;
        }
    }

    kronometre.stop();

    writefln("%s milisaniye", kronometre.peek.total!"msecs");
}

Bu program döngünün ne kadar sürede işletildiğini std.datetime.stopwatch.StopWatch ile ölçmektedir:

674 milisaniye

-inline derleyici seçeneği, işlev içeriklerinin kod içine açılmalarına dayanan bir eniyileştirmeyi etkinleştirir:

$ dmd deneme.d -w -inline

İşlevin kod içine açılması, içeriğinin çağrıldığı noktaya sanki oraya elle yazılmış gibi yerleştirilmesi anlamına gelir. Yukarıdaki döngü bu eniyileştirme uygulandığında aşağıdaki eşdeğeri gibi derlenecektir:

    // Döngünün hesapla()'nın kod içine açıldığındaki eşdeğeri:
    foreach (i; 0 .. 100_000_000) {
        const parametre = cast(ubyte)i;

        const sonuç = cast(ubyte)(parametre * 42);
        if (sonuç == parametre) {
            ++sayaç;
        }
    }

Bu işlev çağrısının böylece ortadan kalkması programı denediğim ortamda %40 kadar bir zaman kazancı sağlamaktadır:

407 milisaniye

İşlevlerin kod içine açılmaları her ne kadar büyük bir kazanç gibi görünse de, bu eniyileştirme her duruma uygun değildir çünkü açılan işlevler kodun fazla büyümesine ve mikro işlemcinin kod ön belleğinden taşmasına neden olabilir. Bunun sonucunda da kod tam tersine daha yavaş işleyebilir. Bu yüzden, işlevlerin kod içine açılmalarının kararı normalde -inline seçeneği ile derleyiciye bırakılır.

Buna rağmen, bazı durumlarda derleyiciye bu konudaki kararında yardım edilmesi yararlı olabilir. inline pragması bu amaçla kullanılır:

Bu pragmalar, içinde geçtikleri işlevi etkileyebildikleri gibi, birden fazla işlev üzerinde etkili olabilmek için kapsam parantezleriyle veya iki nokta üst üste karakteriyle de kullanılabilirler:

pragma(inline, false) {
    // Bu kapsamda tanımlanan işlevler kod içine açılmazlar
    // ...
}

int foo() {
    pragma(inline, true);  // Bu işlev kod içine açılmalıdır
    // ...
}

pragma(inline, true):
// Bu bölümde tanımlanan işlevler kod içine açılmalıdırlar
// ...

pragma(inline):
// Bu bölümde tanımlanan işlevlerin kod içine açılıp
// açılmayacaklarının kararı tekrar derleyici bırakılmıştır
// ...

Programların daha hızlı işlemelerini sağlayan bir başka derleyici seçeneği -O'dur. Bu seçenek derleyicinin başka eniyileştirme algoritmaları işletmesini sağlar. Ancak, bunun sonucunda derleme süreleri fazla uzayabilir.

pragma(startaddress)

Programın başlangıç adresini belirtmeye yarar. Başlangıç adresi zaten D'nin çalışma ortamı tarafından belirlendiğinden normalde bu pragmaya gerek olmaz.

pragma(mangle)

Özgün isim üretirken normal yöntemle üretilecek olandan farklı bir isim kullanılmasını belirler. Özgün isimler bağlayıcının işlevleri ve o işlevleri çağıranları tanıyabilmesi için önemlidir. Bu pragma özellikle D kodunun tesadüfen bir anahtar sözcüğe karşılık gelen bir kütüphane işlevini çağırması gereken durumlarda yararlıdır.

Örneğin, override bir D anahtar sözcüğü olduğundan bir C kütüphanesinin override ismindeki bir işlevi D kodundan çağrılamaz. İşlevin farklı bir isimle çağrılması ama yine de kütüphanenin override isimli işlevine bağlanması gerekir:

/* Bir C kütüphanesinin 'override' ismindeki işlevi ancak
 * 'c_override' gibi bir isimle çağrılabilir. Ancak, bu isim
 * yine de 'override' olarak bağlanmalıdır: */
pragma(mangle, "override")
extern(C) string c_override(string);

void main() {
    /* D kodu işlevi c_override() diye çağırır ama bağlayıcı
     * yine de doğru ismi olan 'override'ı kullanacaktır: */
    auto s = c_override("merhaba");
}