STL推进多向量变换?

7
我想知道是否有更高效的方法来写 a = a + b + c?
 thrust::transform(b.begin(), b.end(), c.begin(), b.begin(), thrust::plus<int>());
 thrust::transform(a.begin(), a.end(), b.begin(), a.begin(), thrust::plus<int>());

这个方式可以工作,但有没有一行代码就能获得同样的效果呢? 我看了例子中的saxpy实现,不过这使用了2个向量和一个常量值; 这个更加有效吗?
struct arbitrary_functor
{
    template <typename Tuple>
    __host__ __device__
    void operator()(Tuple t)
    {
        // D[i] = A[i] + B[i] + C[i];
        thrust::get<3>(t) = thrust::get<0>(t) + thrust::get<1>(t) + thrust::get<2>(t);
    }
};


int main(){

     // allocate storage
    thrust::host_vector<int> A;
    thrust::host_vector<int> B;
    thrust::host_vector<int> C;

    // initialize input vectors
    A.push_back(10);
    B.push_back(10);
    C.push_back(10);

    // apply the transformation
    thrust::for_each(thrust::make_zip_iterator(thrust::make_tuple(A.begin(), B.begin(), C.begin(), A.begin())),
                     thrust::make_zip_iterator(thrust::make_tuple(A.end(),   B.end(),   C.end(),   A.end())),
                     arbitrary_functor());

    // print the output
       std::cout << A[0] << std::endl;

    return 0;
}

这对我来说看起来相当不错。 - Lightness Races in Orbit
1个回答

8
a = a + b + c的算术强度较低(每4次内存操作仅有2次算术操作),因此计算将受到内存带宽的限制。为了比较您提出的解决方案的效率,我们需要测量它们的带宽需求。
在第一个解决方案中,每次对plus的调用都需要进行两次加载和一次存储以及每次对transform的调用。因此,我们可以将每个transform调用的成本建模为3N,其中N是向量abc的大小。由于对transform进行了两次调用,因此该解决方案的成本为6N
我们可以用同样的方式对第二个解决方案的成本进行建模。每次调用arbitrary_functor都需要进行三次加载和一次存储。因此,这种解决方案的成本模型将为4N,这意味着使用for_each解决方案应该比两次调用transform更有效率。当N很大时,第二种解决方案应该比第一种快6N/4N = 1.5x
当然,您也可以像以前一样将zip_iteratortransform相结合,以避免两次单独调用transform

这是一个非常优雅的分析,但我不禁想知道 zip 迭代器的性能如何(我经常使用它,但对它的工作方式或性能没有什么感觉)。这会对此有任何影响吗? - talonmies
zip_iterator确实会增加内核的占用空间,因为每个zipped iterator都需要寄存器资源。在这个例子中,A被冗余地包含在zip中——一次作为源,一次作为目标。稍微更精简的解决方案可能只会将其一次性发送到zip中,但考虑到arbitary_functor非常简单,这不太可能有所区别。 - Jared Hoberock

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接