C++ Dilinde RVO ve NRVO
- Yusuf Hançar
- Jun 13, 2023
- 3 min read
Updated: Jan 18, 2024
Yazılarımızda C++ dilinin avantajlarını, eklentilerini anlatırken bunların derleyici optimizasyonu için sağladığı noktalar üzerinde durduk. Bu yazımızda da fonksiyonlardan dönen değerlerin bu optimizasyona etkisine değineceğiz. Gereksiz kopyalamayı önleyerek performansı artırarak optimize etmesini sağlayan bu teknikler RVO(return value optimization) ve NRVO(named return value optimization) dur. Fonksiyonların geçici nesne döndürmesi olarak değerlendirebiliriz.
Return Value Optimization (RVO)
Bir fonksiyondan dönen değerin direkt olarak çağıran tarafından oluşturulan hedef nesneye yerleştirilmesini sağlar.
Yani, bir fonksiyonun sonucu, fonksiyonun çağrıldığı yerdeki nesneye doğrudan atanır ve ek bir kopyalama işlemi yapılmaz.
Bu, gereksiz kopyalama maliyetini ortadan kaldırır ve performansı artırır.
class SmartCode {
private :
int sc {};
public :
SmartCode() = default;
SmartCode (int val) : sc{val}
{
cout << "SmartCode object at the address " << this << "is constructed with the value " << sc << endl;
}
~SmartCode()
{
cout << "SmartCode object at the address " << this << "is destroyed with the value " << sc << endl;
}
void show()const
{
cout << "(" << sc << ")" << endl;
}
void inc_sc(int x = 1)
{
sc += x;
}
int get_sc()const
{
return sc;
}
};
SmartCode get_cnt(int val)
{
return SmartCode{ val }; // geçici nesne. copy constructor çağırılır
}
int main()
{
SmartCode sc = get_cnt(15);
sc.show(); //sadece bir nesne hayata geldi.
}
*****************************
AÇIKLAMA: sadece bir nesne hayata gelir. Bu durum C++17 ile mecburi hale gelmiştir.
*****************************
CEVAP: ?(try on compiler)
*****************************
std::string get_str()
{
std::string str = "SmartCode";
return str;
}
int main()
{
std::string res = get_str();
return 0;
}
Named Return Value Optimization (NRVO)
Fonksiyon içinde tanımlanan geçici bir nesnenin doğrudan çağıran tarafından oluşturulan hedef nesneye yerleştirilmesini sağlar.
Yani, fonksiyon içinde oluşturulan geçici nesnenin kopyalanması yerine doğrudan hedef nesneye yerleştirilir.
Bu da gereksiz kopyalama maliyetini ortadan kaldırır ve performansı artırır.
class SmartCode {
private :
int sc {};
public :
SmartCode() = default;
SmartCode (int val) : sc{val}
{
cout << "SmartCode object at the address " << this << "is constructed with the value " << sc << endl;
}
~SmartCode()
{
cout << "SmartCode object at the address " << this << "is destroyed with the value " << sc << endl;
}
void show()const
{
cout << "(" << sc << ")" << endl;
}
void inc_sc(int x = 1)
{
sc += x;
}
int get_sc()const
{
return sc;
}
};
SmartCode get_cnt(int val)
{
SmartCode tmp{ val }; // isimlendirilmiş kalıcı nesnedir.
return tmp;
}
int main()
{
SmartCode sc = get_cnt(15);
sc.show();
}
*****************************
AÇIKLAMA: copy constructor çağırılmadı ve yine bir tane nesne hayata gelir.
*****************************
CEVAP: ?(try on compiler)
*****************************
#include <iostream>
#include <string>
std::string get_str()
{
std::string str = "SmartCode";
auto change_str = [&str](){ str += " World!"; };
change_str();
return str; // NRVO
}
int main()
{
std::string res = get_str();
std::cout << "res: " << res << std::endl;
return 0;
}
Kopyalamayı gerçekleştiren fonksiyonlardan birinin olması şarttır.
Derleyicinin nesneye doğrudan result object oluşturmasıdır. Kendisine gelen adreste nesneyi oluşturmasıdır.
copy elision, result object, temporary materialization konularına ayrı yazıda detaylı değinilecek ve buraya link eklenecektir.!!
class SmartCode {
public :
SmartCode()
{
cout << "default constructor" << endl;
}
~SmartCode()
{
cout << "destructor" << endl;
}
SmartCode(int)
{
cout << "SmartCode(int)" << endl;
}
SmartCode(const SmartCode&) = delete;
SmartCode& operator=(const SmartCode&)
{
cout << "copy assignment" << endl;
return *this;
}
SmartCode& operator=(SmartCode&&)
{
cout << "move assignment" << endl;
return *this;
}
};
SmartCode nrvo_func()
{
SmartCode sc;
return sc; // syntax error
}
int main()
{
SmartCode val = foo();
}
*****************************
AÇIKLAMA : Fonksiyonun return değerinin bir yere yazılabilmesi için kopylamam gerekir ancak sınıfın copy constructor'ı delete edilmiştir.
Silinmeseydi eğer kopyalayan kurucu işlevler temporary materialization gerçekleşir ve default ctor ile destructor çağırılırdı.
*****************************
CEVAP: use of deleted function ‘SmartCode::SmartCode(const SmartCode&)’
*****************************
Comments