Как да проверите за копиране на C++ ellision

Попаднах на тази статия за copy ellision в C++ и съм виждал коментари за това в библиотеката за повишаване. Това е привлекателно, тъй като предпочитам функциите ми да изглеждат така

verylargereturntype DoSomething(...)

отколкото

void DoSomething(..., verylargereturntype& retval)

И така, имам два въпроса относно това

  1. Google практически няма никаква документация за това, колко реално е това?
  2. Как мога да проверя дали тази оптимизация наистина се извършва? Предполагам, че включва разглеждане на сглобката, но нека просто кажем, че това не е силната ми страна. Ако някой може да даде много елементарен пример за това как изглежда успешната елизия, това би било много полезно

Няма да използвам copy ellision само за разкрасяване на нещата, но ако мога да бъда гарантиран, че работи, звучи доста полезно.


person Steve    schedule 05.04.2010    source източник
comment
Между другото, статията е грешна, когато дефинира lvalues ​​и rvalues. Вижте въпроси 6.7 и 20.39b на c-faq.com.   -  person conio    schedule 06.04.2010


Отговори (7)


Мисля, че това е много често прилагана оптимизация, защото:

  1. не е трудно за компилатора да го направи
  2. това може да бъде огромна печалба
  3. това е област на C++, която беше често критикувана, преди оптимизацията да стане обичайна

Ако просто сте любопитни, поставете debug printf() във вашия конструктор за копиране:

class foo {
public:
    foo(): x(0) {};

    foo(int x_) : x( x_) {};

    foo( foo const& other) : x( other.x) {
        printf( "copied a foo\n");
    };

    static foo foobar() {
        foo tmp( 2);

        return tmp;
    }


private:
    int x;
};



int main()
{
    foo myFoo;

    myFoo = foo::foobar();

    return 0;
}

Отпечатва „copied a foo“, когато стартирам неоптимизирана компилация, но нищо, когато компилирам оптимизирана.

person Michael Burr    schedule 05.04.2010
comment
При някои компилатори RVO може да се извика дори с минимални или никакви настройки за оптимизация. - person fbrereto; 06.04.2010
comment
Донякъде съм ужасен, че компилаторът е оптимизирал планиран страничен ефект... Изглежда, че трябва да оптимизира вашия конструктор за копиране само ако е конструкторът за копиране по подразбиране или нещо подобно. - person sblom; 06.04.2010
comment
Стандартът изрично позволява тази оптимизация, така че най-общо казано е, че не трябва да разчитате на страничните ефекти на вашия конструктор за копиране, освен очевидното. Това е доста важна техника за оптимизация и ограничаването й до обекти с тривиални конструктори за копиране би било изключително ограничаващо. - person Dennis Zickefoose; 06.04.2010
comment

Следвайки основните математически принципи, случаи 4, 5, 6 и 7 не трябва да се закръглят до стотинка. Не закръглявате, като започвате от най-дясното число и закръглявате нагоре. Гледате само една цифра вдясно от числото, до което искате да закръглите.

http://www.enchantedlearning.com/math/rounding/

компютърът просто изпълнява основна математика, както се предполага.

Редактиране – добавено

по-добра връзка: http://math.about.com/od/arithmetic/a/Rounding.htm

- person Michael Burr; 06.04.2010
comment
@sblom: има само един конструктор за копиране и той е оптимизиран само ако обектът, от който копира, е без име. Ако искате страничните ефекти, просто дайте име на междинния обект. - person Potatoswatter; 06.04.2010
comment
@Potatocom: Дори ако върнатата стойност е именувана, копието все още може да бъде оптимизирано. По-трудно е, но компилаторите са умни. - person Dennis Zickefoose; 06.04.2010
comment
Всъщност примерът, който публикувах и тествах, използва именувана връщана стойност (обикновена, да, но въпреки това именувана). Наименуваната оптимизация на връщаната стойност е известна като NRVO и може да е малко по-рядко срещана от обикновения RVO, но мисля, че те се обработват почти по същия начин от съвременните компилатори. - person Michael Burr; 06.04.2010
comment
@Michael: зависи от настройките на компилатора и оптимизацията. Известно е, че Visual не прилага RVO в компилации за отстраняване на грешки, докато gcc прави URVO според мен. Сега не съм сигурен, че наистина си струва да се ровя в спецификациите на компилатора, за да знам точно коя оптимизация се прилага на кое ниво... - person Matthieu M.; 06.04.2010

От цитираната от вас статия:

Въпреки че elision никога не се изисква от стандарта, последните версии на всеки компилатор, който съм тествал, изпълняват тези оптимизации днес. Но дори и да не се чувствате комфортно да връщате тежки обекти по стойност, копирането на elision все пак трябва да промени начина, по който пишете код.

По-известен е като оптимизиране на възвръщаемата стойност.

person msw    schedule 05.04.2010

Единственият начин да разберете със сигурност е да погледнете сборката, но задавате грешен въпрос. Не е необходимо да знаете дали компилаторът премахва копието, освен ако това няма значение за времето на програмата. Профайлърът би трябвало лесно да ви каже, ако прекарвате твърде много време в конструктора за копиране.

Начинът на бедния човек да го разбере е да постави статичен брояч в конструктора за копиране и да опита и двете форми на вашата функция. Ако броят е еднакъв, успешно сте избегнали копието.

person Mark Ransom    schedule 05.04.2010
comment
това нещо е наистина голямо и се нарича доста често - person Steve; 06.04.2010
comment
Наистина големи и доста често може да са с порядък по-големи, отколкото си мислите. Изумява ме какво може да направи един съвременен компютър за части от секундата. - person Mark Ransom; 06.04.2010

Вместо това потърсете в Google „Оптимизация на наименувана възвръщаема стойност“ и „Оптимизация на възвръщаема стойност“. Съвременните компилатори всъщност няма да изпълнят копирането в много случаи.

Можете да проверите дали се случва, като върнете тип със странични ефекти - като отпечатване на съобщение. Wikipedia има някои добри примери за това къде изходът на програмата се променя, когато RVO и/или NRVO е в сила.

person Billy ONeal    schedule 05.04.2010

Пример за това как изглежда:

#include <iostream>

struct Foo {
    int a;
    Foo(int a) : a(a) {}
    Foo(const Foo &rhs) : a(rhs.a) { std::cout << "copying\n"; }
};

int main() {
    Foo f = Foo(1);
}

Ако не виждате резултат, тогава е извършено премахване на копирането. Това е премахване на копие от инициализатор. Другият правен случай на избягване на копиране е върната стойност и е тестван от:

Foo getFoo() {
    return Foo(1);
}

int main() {
    Foo f = getFoo();
}

или по-вълнуващо за наименована връщана стойност:

Foo getFoo() {
    Foo f(1);
    return f;
}

int main() {
    Foo f = getFoo();
}

g++ изпълнява всички тези елизии за мен без флагове за оптимизация, но всъщност не можете да знаете дали по-сложен код ще надхитри компилатора.

Имайте предвид, че elision за копиране не помага при присвояването, така че следното винаги ще доведе до извикване на operator=, ако този оператор отпечата нещо:

Foo f(1);
f = getFoo();

Следователно връщането по стойност все още може да доведе до "копие", дори ако се извърши премахване на конструктора на копиране. Така че за разделянето на страхотни класове все още е съображение за производителност на етапа на проектиране. Не искате да пишете кода си така, че поправката му по-късно да бъде голяма работа, ако се окаже, че приложението ви прекарва значителна част от времето си в копиране, което е можело да бъде избегнато.

person Steve Jessop    schedule 05.04.2010

За да отговорите на въпрос 2, можете да напишете демонстрационна програма, в която пишете class DemoReturnType;, която има инструментирани конструктори и деструктори, които просто пишат в cout, когато бъдат извикани. Това трябва да ви даде достатъчно информация за това на какво е способен вашият компилатор.

person quamrana    schedule 05.04.2010

Rvalue препратките решават този проблем в C++0x. Друг е въпросът дали можете или не да получите компилатор с активиран rvalue - последния път, когато проверих, само Visual Studio 2010 го поддържа.

person Puppy    schedule 05.04.2010
comment
GCC също го поддържа. Вижте aristeia.com/C++0x/C++0xFeatureAvailability.htm и щракнете върху раздела Език в долната част. - person Nate; 06.04.2010
comment
Rvalue препратки допълват [N]RVO. Когато е възможно, компилаторът все пак ще избере да конструира обекта на място и да избягва директно преместването/копирането. - person Dennis Zickefoose; 06.04.2010