【问题标题】:How to return large data efficiently in C++11如何在 C++11 中高效返回大数据
【发布时间】:2016-09-04 05:01:04
【问题描述】:

我对在 C++11 中返回大数据感到非常困惑。最有效的方法是什么? 这是我的相关功能:

void numericMethod1(vector<double>& solution,
                    const double input);

void numericMethod2(pair<vector<double>,vector<double>>& solution1,
                    vector<double>& solution2,
                    const double input1,
                    const double input2);

这是我使用它们的方式:

int main()
{
    // apply numericMethod1
    double input = 0;
    vector<double> solution;
    numericMethod1(solution, input);

    // apply numericMethod2
    double input1 = 1;
    double input2 = 2;
    pair<vector<double>,vector<double>> solution1;
    vector<double> solution2;
    numericMethod2(solution1, solution2, input1, input2);

    return 0;
}

问题是,std::move() 在后续实现中没有用吗?

实施:

void numericMethod1(vector<double>& solution,
                    const double input)
{
    vector<double> tmp_solution;

    for (...)
    {
    // some operation about tmp_solution
    // after that this vector become very large
    }

    solution = std::move(tmp_solution);
}

void numericMethod2(pair<vector<double>,vector<double>>& solution1,
                    vector<double>& solution2,
                    const double input1,
                    const double input2)
{
    vector<double> tmp_solution1_1;
    vector<double> tmp_solution1_2;
    vector<double> tmp_solution2;

    for (...)
    {
    // some operation about tmp_solution1_1, tmp_solution1_2 and tmp_solution2
    // after that the three vector become very large
    }

    solution1.first = std::move(tmp_solution1_1);
    solution1.second = std::move(tmp_solution1_2);
    solution2 = std::move(tmp_solution2);
}

如果它们没有用,我如何处理这些大返回值而不复制多次? 免费更改 API!

更新

感谢 StackOverFlow 和这些答案,在深入研究相关问题后,我更了解这个问题。由于 RVO,我更改了 API,为了更清楚,我不再使用 std::pair。这是我的新代码:

struct SolutionType
{
    vector<double> X;
    vector<double> Y;
};

SolutionType newNumericMethod(const double input1,
                              const double input2);

int main()
{
    // apply newNumericMethod
    double input1 = 1;
    double input2 = 2;
    SolutionType solution = newNumericMethod(input1, input2);

    return 0;
}

SolutionType newNumericMethod(const double input1,
                              const double input2);
{
    SolutionType tmp_solution; // this will call the default constructor, right?
    // since the name is too long, i make alias.
    vector<double> &x = tmp_solution.X;
    vector<double> &y = tmp_solution.Y;

    for (...)
    {
    // some operation about x and y
    // after that these two vectors become very large
    }

    return tmp_solution;
}

我怎么知道 RVO 发生了?或我如何确保 RVO 发生?

【问题讨论】:

  • 主题似乎不足以质疑内容......
  • 这些在这种情况下也不是没用,但是为什么不直接使用solution1solution2呢?还是参考他们?
  • @jacek-cz 刚刚编辑,这是一个错误,对不起!
  • @Reigs 如果我理解真正/中心问题... :(

标签: c++ c++11 parameter-passing return-value


【解决方案1】:

当您拥有像非常大的vector&lt;double&gt; 这样的大数据时,您仍然可以按值返回它,因为 C++11 的 移动语义 将用于 @ 987654322@,所以从你的函数中返回它只是某种指针分配(因为vector&lt;double&gt;的内容通常是堆分配的引擎盖下)。

所以我会这样做:

// No worries in returning large vectors by value
std::vector<double> numericMethod1(const double input)
{
    std::vector<double> result;

    // Compute your vector<double>'s content
    ...

    // NOTE: Don't call std::move() here.
    // A simple return statement is just fine.
    return result;
}

(请注意,基于特定的 C++ 编译器,也可以应用 C++98/03 中已经可用的其他类型的优化,例如 RVO/NRVO。)


相反,如果您有一个返回多个输出值的方法,那么我会使用非常量引用,就像在 C++98/03 中一样:

void numericMethod2(pair<vector<double>,vector<double>>& output1,
                    vector<double>& output2,
                    vector<double>& output3,
                    ...
                    const double input1,
                    const double input2);

在实现内部,您仍然可以使用有效的 C++98/03 技术 "swap-timization",您可以调用 std::swap() 来交换局部变量和输出参数:

#include <utility> // for std::swap

void numericMethod2(pair<vector<double>,vector<double>>& solution1,
                    vector<double>& solution2,
                    const double input1,
                    const double input2)

{
    vector<double> tmp_solution1_1;
    vector<double> tmp_solution1_2;
    vector<double> tmp_solution2;

    // Some processing to compute local solution vectors
    ...

    // Return output values to caller via swap-timization
    swap(solution1.first, tmp_solution1_1);
    swap(solution1.second, tmp_solution1_2);
    swap(solution2, tmp_solution2);
}

交换向量通常会将内部向量的 指针 交换到该向量拥有的堆分配内存:所以您只有 指针分配没有深拷贝、内存重新分配或类似的昂贵操作。

【讨论】:

  • 谢谢,现在我更清楚这个问题了。很多东西要学。
  • @Reigs:不客气。如果您发现一些答案对您有帮助,您可能希望将它们作为一个优秀的 StackOverflow 公民进行投票。
  • 我想说一个返回多个输出值的方法应该将这些值包装在一个自定义类、结构或元组中,然后只返回一个实例。
  • 你是对的!我将获得更多的声誉,这样我就可以投票给所有帮助我的人。
  • @ChristianHackl:我不确定,我的意思是:这对我来说更多的是个人喜好的王国......你的建议会在可能的东西之间添加一个潜在的人为分组层有点不相关,除了都是“输出值”
【解决方案2】:

按值返回,依赖RVO (return value optimization)

auto make_big_vector()
{
    vector<huge_thing> v1;
    // fill v1

    // explicit move is not necessary here        
    return v1;
} 

auto make_big_stuff_tuple()
{
    vector<double> v0;
    // fill v0

    vector<huge_thing> v1;
    // fill v1

    // explicit move is necessary for make_tuple's arguments,
    // as make_tuple uses perfect-forwarding:
    // http://en.cppreference.com/w/cpp/utility/tuple/make_tuple

    return std::make_tuple(std::move(v0), std::move(v1));
}

auto r0 = make_big_vector();
auto r1 = make_big_stuff_tuple();

我会将您的函数的 API 更改为简单地按值返回。

【讨论】:

  • 我怎么知道它何时使用'返回值优化'?哦,你刚刚添加了链接。谢谢,我会仔细阅读的。
  • @Reigs:之前在 StackOverflow 上已经讨论过很多次了,有很多漂亮且内容丰富的答案——一定要在这里做一些研究
  • 你也可以得到复制省略或移动省略。
  • @LightnessRacesinOrbit: seems like it - make_tuple 的参数需要移动,更新答案。
  • @vitt 按引用返回有一个很大的优势:易于重用缓冲区。按值返回需要工作才能实现。
【解决方案3】:

您可以使用std::vector::swap 成员函数,它将容器的内容与其他容器的内容进行交换。 不对单个元素调用任何移动、复制或交换操作。

solution1.first.swap(tmp_solution1_1);
solution1.second.swap(tmp_solution1_2);
solution2.swap(tmp_solution2);

编辑:

这些说法不是没用的,

solution1.first = std::move(tmp_solution1_1);
solution1.second = std::move(tmp_solution1_2);
solution2 = std::move(tmp_solution2);

它们调用了std::vector::operator=(&amp;&amp;) 的移动赋值运算符,它确实在右侧移动了向量。

【讨论】:

  • 感谢您的及时回复,您能解释一下吗?构造 solution1.second 后 std::move() 不起作用?
【解决方案4】:

首先,为什么不在numericMethod2中直接使用solution1呢?那更直接。

与 std::array 或 obj[] 不同,值不是存储在堆栈中,而是使用堆(您可以参考标准库代码,它们使用 operator new() 很多)。因此,如果您发现该向量只是临时的并且会返回到其他地方,请使用 std::swap 或 std::move。函数返回实际上可以强制转换为 xvalue

对于标准容器(std::map、std::set、deque、list 等)总是如此

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-02-04
    • 2015-04-29
    • 1970-01-01
    • 2014-05-20
    • 2011-10-02
    相关资源
    最近更新 更多