C ++数组运算符开销

时间:2013-02-07 16:09:44

标签: c++ arrays performance operator-overloading hpc

我记得回过头来看一些代码,这些代码允许编译器做一些工作并简化像这样的表达式:

// edit: yes the parameters where meant to be passed by reference
//       and maintain constness sorry !
template< typename T >
std::vector<T> operator+( const std::vector<T>& a, const std::vector<T>& b )
{
    assert( a.size() == b.size() );
    std::vector<T> o; o.reserve( a.size() );
    for( std::vector<T>::size_type i = 0; i < a.size(); ++i )
        o[i] = a[i] + b[i];
    return o;
}

// same for operator* but a[i] * b[i] instead

std::vector<double> a, b, c, d, e;

// do some initialization of the vectors

e = a * b + c * d

通常会为每个运算符创建并分配新的向量,而编译器只会创建一个副本并对其执行所有操作。

这种技术是什么?

3 个答案:

答案 0 :(得分:3)

@Agnew很早就提到过,你所描述的技术是expression templates

这通常使用向量 1 的数学概念来完成,而不是std::vector。 广泛的招数是:

  1. 不要对向量进行数学运算返回结果。相反,让他们返回一个代表最终需要完成的操作的proxy objecta * b可以返回一个“乘法代理”对象,该对象只保存对应该乘以的两个​​向量的const引用。

  2. 也为这些代理编写数学运算,允许它们被链接在一起,因此a * b + c * d变为(TempMulProxy) + (TempMulProxy)变为(TempAddProxy),所有这些都不做任何数学运算或复制任何载体

  3. 编写一个赋值操作符,用于获取右侧对象的代理对象。此运算符可以查看整个表达式a * b + c * d并在向量上有效地执行该操作,同时了解目标。所有这些都没有创建多个临时矢量对象。

  4. 1 或矩阵或四元数等...... *

答案 1 :(得分:1)

我这里没有看到问题。然而,我的水晶球告诉我,你想知道你想出的两种方法的更好方法,以便对a * b + c * dab等矢量执行分量算术运算。c },dstd::vector<T>是具有相同大小的向量(std::vector<int> a, b, c, d, e; // fill a, b, c, d with data auto expression = [](int a, int b, int c, int d){ return a * b + c * d; }; assert (a.size() == b.size() && b.size() == c.size() && c.size() == d.size()); e.reserve(a.size()); for(auto _a = a.begin(), _b = b.begin(), _c = c.begin(), _d = d.begin(), _e = e.begin(); _a != a.end(); ++_a, ++_b, ++_c, ++_d, ++_e) { *_e = expression(*_a, *_b, *_c, *_d); } ):

  1. 对于要完成的每个操作,循环遍历元素,执行计算并返回结果向量。将这些操作放在矢量公式中。

  2. 对于输入向量中的每个元素,计算整个表达式并将其写入一个单个最终结果向量。

  3. 有两件事需要考虑:

    • 性能:此处,第二个选项是未来,因为处理器不会分配不必要的临时向量。
    • 可重用性:很明显,实现向量的算法运算并通过简单地在向量上表达目标公式来重用它们是很好的。

    但是,有一个很好的选择来实现第二个看起来很漂亮的选项:

    void componentWise4(std::function<int(int,int,int,int)> f,
                        const std::vector<int> & a,
                        const std::vector<int> & b,
                        const std::vector<int> & c,
                        const std::vector<int> & d,
                        std::vector<int> & result)
    {
        assert (a.size() == b.size() && b.size() == c.size() && c.size() == d.size());
        result.reserve(a.size());
    
        for(auto _a = a.begin(), _b = b.begin(), _c = c.begin(), _d = d.begin(), _result = result.begin();
            _a != a.end();
            ++_a, ++_b, ++_c, ++_d, ++_result)
        {
            *_result = expression(*_a, *_b, *_c, *_d);
        }
    }
    

    这样,您可以将表达式与逻辑分开来评估它:

    std::vector<int> a, b, c, d, e;
    // fill a, b, c, d with data
    
    componentWise4([](int a, int b, int c, int d){ return a * b + c * d; },
                   a, b, c, d, e);
    

    然后将其称为:

    {{1}}

    我确信这个“表达式评估器”可以使用C ++ 11新功能“variadic templates”进行扩展,以支持表达式中的任意数量的参数以及甚至不同的类型。我无法让它工作(可变参数模板的东西),你可以尝试在这里完成我的尝试:http://ideone.com/w88kuG(我是variadic模板的新手,所以我不知道语法)。< / p>

答案 2 :(得分:1)

“C ++编程语言”。 Bjarne Stroustrup在 22.4.7 Temporaries,Copying和循环[num.matrix]。 阅读本书总是一个好主意。

如果你没有,基本上我们有两个选择:

首先:我们编写一组函数来直接计算一些最期望的组合(例如mul_add_and_assign(&amp; U,&amp; M,&amp; V,&amp; W)来计算U = M * V + W)并引导用户选择自己最方便的功能。

第二:我们可以引入一些辅助类(例如VxVVplusV等),它们只保留对每个操作的参数的引用,并定义一个运算符转换为{{1} }。现在我们创建运算符vector+的重载,它们通过引用获取两个向量,并返回相应类型的对象。我们可以创建类型*的类来计算更复杂的操作。现在我们可以重载VxVplusVxV以将operator=分配到VxVplusVxV。在最后一次重载中,我们使用对辅助类对象中的参数的引用进行了所有计算,没有创建或创建最小的临时向量。