Эффективный возврат неизмененного аргумента - PullRequest
0 голосов
/ 24 декабря 2018

У меня есть функция, которая должна либо изменять экземпляр объекта, либо возвращать его неизмененным, что можно упростить до следующего кода:

struct MyObject
{
    bool ShouldNotChange;
    int SomeData[10];
};

void ModifyObject_inplace(MyObject & object)
{
    if (object.ShouldNotChange) return;

    // Modify object here
    object.SomeData[1] = 1;
}

По ряду причин я хотел бы преобразовать этокод в более функциональный стиль:

MyObject ModifyObject(MyObject object)
{
    if (object.ShouldNotChange)
        return object;

    object.SomeData[1] = 1;
    return object;
}

Проблема в том, что эта функция критична к производительности, и при изменении таким образом она становится медленнее.Я пробовал несколько разных вариантов.

MyObject ModifyObject_constref(const MyObject & object)
{
    if (object.ShouldNotChange)
        return object;

    auto result = object;
    result.SomeData[1] = 1;
    return result;
}

std::shared_ptr<MyObject> ModifyObject_ptr(const std::shared_ptr<MyObject> & object_ptr)
{
    if (object_ptr->ShouldNotChange)
        return object_ptr;

    object_ptr->SomeData[1] = 1;
    return object_ptr;
}


MyObject && ModifyObject_rvalue(MyObject object)
{
    if (object.ShouldNotChange)
        return std::move(object);

    MyObject newRoute = object;
    newRoute.SomeData[1] = 1;
    return std::move(newRoute);
}

Но только ModifyObject_inplace дает самый быстрый код (судя по разборке).Фактически, компилятор преобразовал в функцию только ModifyObject_inplace без единого перехода в ассемблерном коде.Я использую VC ++ 2017.

Есть ли способ реализовать его в функциональном стиле без снижения производительности?

Ответы [ 3 ]

0 голосов
/ 24 декабря 2018

Анализ ваших попыток

MyObject ModifyObject(MyObject object)

Для этого потребуется как минимум копия объекта.Не идеально, когда вы можете избежать этого.

MyObject ModifyObject_constref(const MyObject & object)

Снова вам нужно скопировать параметр в возвращаемый объект.Та же проблема.

std::shared_ptr<MyObject> ModifyObject_ptr(const std::shared_ptr<MyObject> & object_ptr)

Нет, нет, нет, нет ... Нет!shared_ptr используется для управления временем жизни ресурса, когда несколько объектов с разным временем жизни являются владельцами ресурса.Это его использование.Период.Если у вас нет такой ситуации, не используйте shared_ptr.Более того, shared_ptr оказывает очень значительное влияние на производительность.Существует по крайней мере внешнее общее состояние и 2 косвенные ссылки, связанные с shared_ptr.

MyObject && ModifyObject_rvalue(MyObject object)
   // ...
   return std::move(object);

Неопределенное поведение!Вы возвращаете ссылку на аргумент функции.object заканчивает свое время жизни, когда функция завершается, вы получаете ссылку на мертвый объект.Также в вашем случае перемещение эквивалентно копированию.В MyObject нет ресурса, который можно было бы украсть за ход, поэтому ход фактически выполняет копирование.

Решение

Как показало Jarod42

MyObject& ModifyObject(MyObject& object)

Самый быстрый, потому что вы просто передаете ссылки.Новые объекты не создаются, копирование не производится.

Обязательно, возвращаемое значение.В идеале, аргумент const

Если у вас есть аргумент const, и вам нужно вернуть измененный объект, то вам, безусловно, нужно создать новый объект, и это включает в себя копию.Если это ваше требование, то я бы выбрал:

MyObject ModifyObject_1(MyObject object)
{
    if (!object.ShouldNotChange)
        object.SomeData[1] = 1;

    return object;
}

или

MyObject ModifyObject_2(const MyObject& object)
{
    MyObject r{object};

    if (!r.ShouldNotChange)
        r.SomeData[1] = 1;

    return r;
}

или

MyObject ModifyObject_3(const MyObject& object)
{

    if (object.ShouldNotChange)
        return object;

    MyObject r{object};
    r.SomeData[1] = 1;

    return r;
}

Какой из них самый быстрый?Ну, вам нужно профиль , что вы должны сделать в любом случае.Не делайте выводов из сборки, если они не подкреплены профилированием.Для кода, критичного к производительности профиль, профиль, профиль !

Функциональное программирование и производительность

В чистый функциональный код (это то, что вам нужно) все объекты неизменны.Это подразумевает копию всякий раз, когда вам нужно изменить объект.Это влияет на производительность.Вам нужно решить, стоит ли компромисс.

0 голосов
/ 24 декабря 2018

Рассматривали ли вы добавить метод в MyObject?Похоже, вам нужна не функция, которая возвращает разные объекты, а метод, который либо изменяет объект, либо нет.Метод также может вернуть это или ссылку на объект, если это необходимо.Возможно, стоит предложить встроенный компилятор.

0 голосов
/ 24 декабря 2018

Вы можете иметь:

MyObject& ModifyObject(MyObject& object)
{
    if (object.ShouldNotChange) return object;

    // Modify object here
    object.SomeData[1] = 1;
    return object;
}

Если вам требуется аргумент const, потребуется некоторая копия, которая будет дороже, чем модификация на месте + возврат по ссылке.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...