STL толкает множественное векторное преобразование?

Мне было интересно, есть ли более эффективный способ записи 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;
}

person Sharpie    schedule 22.09.2011    source источник
comment
Это выглядит довольно хорошо для меня.   -  person Lightness Races in Orbit    schedule 22.09.2011


Ответы (1)


a = a + b + c имеет низкую арифметическую интенсивность (только две арифметические операции на каждые 4 операции с памятью), поэтому вычисления будут ограничены пропускной способностью памяти. Чтобы сравнить эффективность предложенных вами решений, нам необходимо измерить их требования к пропускной способности.

Каждый вызов transform в первом решении требует двух загрузок и одного сохранения для каждого вызова plus. Таким образом, мы можем смоделировать стоимость каждого вызова transform как 3N, где N — размер векторов a, b и c. Поскольку есть два вызова transform, стоимость этого решения составляет 6N.

Точно так же мы можем смоделировать стоимость второго решения. Каждый вызов arbitrary_functor требует трех загрузок и одного сохранения. Таким образом, модель затрат для этого решения будет 4N, что означает, что решение for_each должно быть более эффективным, чем двойной вызов transform. Когда N велико, второе решение должно работать на 6N/4N = 1.5x быстрее, чем первое.

Конечно, вы всегда можете комбинировать zip_iterator с transform аналогичным образом, чтобы избежать двух отдельных вызовов transform.

person Jared Hoberock    schedule 25.09.2011
comment
Это очень элегантный анализ, но я не могу не задаться вопросом, насколько дорог zip-итератор (я часто им пользуюсь, но не понимаю, как он работает и как он работает). Здесь это как-то влияет? - person talonmies; 25.09.2011
comment
zip_iterator действительно может увеличить размер ядра, поскольку каждый сжатый итератор требует ресурсов регистра. В этом примере A включен в ZIP-файл избыточно — один раз как источник и один раз как место назначения. Немного более экономное решение может отправить его в zip только один раз, но вряд ли это будет иметь значение, учитывая, что arbitary_functor настолько прост. - person Jared Hoberock; 25.09.2011