有效地返回未修改的参数

时间:2018-12-23 22:52:05

标签: c++ c++17

我有一个函数,该函数应该修改对象的实例或返回未修改的对象,可以将其简化为以下代码:

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 :(得分:2)

您可以拥有:

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

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

如果需要const参数,则将需要一些副本,这将比就地修改+按引用返回更为昂贵。

答案 1 :(得分:1)

分析您的尝试

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;
}

其中哪一个最快?那么,您需要配置文件,无论如何您都应该这样做。不要从汇编中得出结论,除非通过分析对其进行了支持。对于性能关键代码个人资料,个人资料,个人资料

功能编程和性能

功能代码(这似乎是您想要的)中,所有对象都是不可变的。每当您需要修改对象时,这意味着一个副本。这会影响性能。您需要确定权衡是否值得。

答案 2 :(得分:0)

您是否考虑过向MyObject添加方法? 看起来您不需要返回不同对象的函数,而是需要一种修改或不修改该对象的方法。 如果需要,方法也可以返回此值或对对象的引用。 最好建议编译器内联。