kopyalamanın eliminasyonu (copy elision) -1

Bu yazı serimizde kopyalamanın eliminasyonu (copy elision)  konusunu işleyeceğiz: Kopyalamanın maliyetinden kaçınmak için derleyicinin ekstra (ve pahalı olabilecek) kopyalama işlemlerini elimine edecek biçimde optimizasyon yaparak kod üretmesine ingilizcede popüler olarak “copy elision” deniyor.  Derleyicinin böyle bir tekniği kullanması, belirli  koşullar sağlandığında işlevlerin değerle geri dönmesini ya da işlevlerin değerle çağrılmasını verimli kılıyor.
Konuya bir giriş yapabilmek için önce aşağıdaki koda bir göz atalım:

Bu programın çalıştırdığınızda ekranınızda göreceğiniz yazılar muhtemelen

olacak. Başlangıçta bu durum sizi biraz şaşırtabilir. Image sınıfının operator+ işlevi içinde oluşturulan temp isimli Image nesnesi için varsayılan kurucu işlev çağrılmadı mı? Peki bu nesne işlevin return deyimi ile geri döndürüldüğünde, işlevin geri dönüş değerini tutacak olan myImageCopy isimli nesne için kopyalayan ya da taşıyan kurucu işlevin çağrılması gerekmiyor mu?
Derleyici tipik olarak böylesi bir durumda  İsimlendirilmiş Geri Dönüş Değeri Optimizasyonu (Named Return Value Optimization) diye adlandırılan tekniği kullanarak kopyalamayı elimine edecek bir kod üretiyor. Derleyici myImageCopy nesnesi için sınıfın kopyalayan ya da taşıyan kurucu işlevini çağırsaydı gereksiz yere işlemci zamanı harcanacaktı. Derleyici acaba böylesi bir optimizasyonu gerçekleştirmek için nasıl bir kod üretiyor? Yukarıdaki kodda bazı değişiklikler yapıyoruz:

Benim çalışan programımda ekran çıktısı şöyle oldu:

Ekran çıktısında gördüğünüz gibi operator+ işlevindeki yerel nesnemiz olan  temp ile bu işlevin geri dönüş değeriyle hayata getirilen myCopyImage  nesnesi aynı adrese sahip. operator+ işlevinin kodunun çıkışında yerel nesne olan temp için sonlandırıcı işlev çağrılmıyor. main işlevinin sonunda yalnızca myImage ve myImageCopy nesneleri için sonlandırıcı işlev çağrılıyor.

Derleyicinin böyle bir optimizasyonu yapıp/yapmaması tipik olarak derleyicinin ayarlarıyla seçilebiliyor. Örneğin GCC derleyicisinde kopyalama eliminasyonunu devre dışı bırakmak için  -fno-elide-constructors anahtarı kullanılabiliyor.
Geri dönüş değeri optimizasyonunun yapılabilmesi için mutlaka sınıfın kopyalayıcı kurucu işlevinin ya da taşıyan kurucu işlevinin var olması gerekiyor. Aşağıdaki koda bakalım:

Bu şu anlama geliyor: Nasıl olsa derleyici IGDDO yapacak diye kopyalayan ve/veya taşıyan kurucu işlevi hiç bulundurmamak gibi bir şansımız yok. Kopyalamanın eliminasyonunda ilginç bir nokta daha var: Kopyalayan ya da taşıyan kurucu işlevlerin yan etkilere sahip olması derleyicinin bu optimizasyonu gerçekleştirmesine engel değil:

Yukarıdaki kodda A sınıfının kopyalayan ve taşıyan kurucu işlevleri içinde statik ömürlü yerel değişkenlerin değerleri artırılmış ve global func işlevi çağrılmış. Derleyici muhtemelen yine kopyalama eliminasyonunu gerçekleştirecek bir kod üretecek.
Eğer çağrılan işlevin birden fazla çıkış noktası (return deyimi) varsa derleyici tipik olarak böyle bir optimizayonu gerçekleştir(e)miyor. Aşağıdaki kodu inceleyelim:

Benim çalışan programımın ekran çıktısı şöyle oldu:

Kodu daha verimli hale getirebilmek için kodlayıcının müdahalesi birçok durumda ters etki yapıyor:

operator+ işlevinde kodlayıcı daha verimli bir kodun oluşumunu sağlamak için derleyicinin taşıyan kurucu işlevi çağırmasını istemiş. Böyle bir kod tipik olarak derleyicinin İGDDO kodu üretmesini engelleyecek operator+ işlevinin kodunu yukarıdaki gibi değiştirdiğimde çalışan programın çıktısı aşağıdaki gibi oldu:

Şimdi de derleyicinin kopyalama eliminasyonu uyguladığı tipik senaryolara tek tek bakalım:

foo işlevi içinde çağrılan func işlevinin return ifadesi olarak bir geçici nesne kullanılıyor. func işlevinin geri dönüş değeri ile A sınıfı türünden bir nesne hayata getirilmiş. Bu duruma Geri Dönüş Değeri Eliminasyonu (Return Value Optimization) deniyor. Değeriyle geri dönen nesne bir isme sahip değil. Derleyici a nesnesi için muhtemelen kopyalayan kurucu işlevi çağırmak yerine doğrudan varsayılan kurucu işlevi çağıracak. Kopyalama işlemi elimine edilecek.

Yukarıdaki kodda yine func işlevinin geri dönüş değeri A sınıfı türünden. İşlev otomatik ömürlü yerel bir nesne olan a1‘in değeri ile geri dönüyor. foo işlevi içinde çağrılan func işlevinin geri dönüş değeri ile A sınıfı türünden bir nesne hayata getirilmiş. Bu senaryoda geri dönüş değerine konu olan nesnenin bir ismi olduğu için bu duruma İsimlendirilmiş Geri Dönüş Değeri Optimizasyonu (Named Return Value Optimization) deniyor. Derleyici a nesnesi için kopyalayan kurucu işlevi çağırmak yerine muhtemelen doğrudan varsayılan kurucu işlevi çağıracak. Kopyalama işlemi yine elimine edilecek.

Yukarıdaki kodda bildirilen func işlevinin parametresi A sınıfı türünden. Yani func değerle çağrılan (call by value) bir işlev. foo işlevi içinde func işlevi A sınıfı türünden geçici bir nesne ile çağrılmış. Derleyici func işlevinin parametre değişkeni olan nesne için kopyalayan kurucu işlevi çağırmak yerine muhtemelen doğrudan varsayılan kurucu işlevi çağıracak. Kopyalama elimine edilmiş olacak.

Yukarıdaki kodda ise func işlevi içinde otomatik ömürlü a1 isimli nesne tanımlanmış. İşlevden bir hata nesnesinin gönderilmesiyle çıkılıyor. Derleyicinin oluşturacağı hata nesnesi için yerel a1 nesnesi kullanılıyor. main işlevi içinde func işlevinden gönderilen hata nesnesi değerle yakalanıyor. Oluşturulan catch bloğunun parametresinin A sınıfı türünden olduğunu görüyorsunuz. Derleyici catch parametresi olan a2 nesnesi için muhtemelen kopyalayan kurucu işlevi çağırmak yerine doğrudan varsayılan kurucu işlevi çağıracak.

Bu senaryolardan hiçbirinde derleyicinin kopyalama eliminasyonu yapacağının bir garantisi yok. Standartlar derleyinin böyle bir optimizasyon yapmasına izin veriyor ama bu optimizasyonu zorunlu tutmuyor. C++17 standartlarıyla birlikte bazı durumlarda kopyalama eliminasyonunun garanti altına alınması söz konusu.

Necati Ergin

C ve Sistem Programcıları Derneğinde eğitmen olarak çalışıyor.

Bunlar da ilginizi çekebilir

Bir Cevap Yazın

E-posta hesabınız yayımlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir

Kod Eklemek İçin Okuyun