I've implemented a for loop consisting of several Thrust transformations. My aim is to calculate r[i]
for each value of i
from 0 to N
. To put simply, r
is a column vector and each of its elements can be calculated independently.
Therefore, I'm looking a way of parallelizing the for loop given below:
for(int i=0; i < N; i++) {
thrust::device_vector<float> P(N, 0.0);
thrust::device_vector<int> corr_col_indices_d(col_indices.begin() + row_begin[i], col_indices.begin() + row_begin[i+1]); // indices of the columns
thrust::device_vector<float> corr_values_d(values_d.begin() + row_begin[i], values_d.begin() + row_begin[i+1]); // values of the columns
// P[j] = corr_values_d[k] if j is in corr_col_indices_d, else 0 (increment k if j is in corr_col_indices_d)
thrust::scatter(corr_values_d.begin(), corr_values_d.end(), corr_col_indices_d.begin(), P.begin());
r2[i] = thrust::inner_product(P.begin(), P.end(), r1.begin(), 0.0f);
}
1) After lots of googling, roaming around Stackoverflow and NVIDIA, I attempted to put all successive transformations into a bigger "transform" with a loop variable i
.
auto counting_iter = thrust::make_counting_iterator(0);
thrust::transform(counting_iter, counting_iter + N, r2.begin(), [&](int i) {
thrust::device_vector<float> P(N, 0.0);
thrust::device_vector<int> corr_col_indices_d(col_indices.begin() + row_begin[i], col_indices.begin() + row_begin[i+1]); /
thrust::device_vector<float> corr_values_d(values_d.begin() + row_begin[i], values_d.begin() + row_begin[i+1]);
thrust::scatter(corr_values_d.begin(), corr_values_d.end(), corr_col_indices_d.begin(), P.begin());
thrust::transform(P.begin(), P.end(), r1.begin(), P.begin(), thrust::multiplies<float>());
return thrust::reduce(P.begin(), P.end());
});
Unfortunately it doesn't work. Either the there is no such a thing as giving transformations like this, or my syntax is wrong.
2) Then I tried to create a functor that takes all these device_vectors
as input and operates on them. As stated here, it's not possible to pass device_vectors
to a functor from outside - therefore I attempted to give them as raw pointers.
struct loop {
// constructor that takes a vector as a parameter
__host__ __device__
loop(int *t_row_begin, int *t_col_indices, float*t_values, float *r1):
t_row_begin_(t_row_begin), t_col_indices_(t_col_indices), t_values_(t_values), r1_(r1) {}
// member variable to store the vector
int *t_row_begin_;
int *t_col_indices_;
float *t_values_;
float *r1_;
__host__ __device__
float operator()(int i) const {
thrust::device_vector<float> P(N, 0.0);
thrust::device_vector<int> corr_col_indices_d(t_col_indices_ + t_row_begin_[i], t_col_indices_ + t_row_begin_[i + 1]); // indices of the columns
thrust::device_vector<float> corr_values_d(t_values_ + t_row_begin_[i], t_values_ + t_row_begin_[i+1]); // values of the columns
thrust::scatter(corr_values_d.begin(), corr_values_d.end(), corr_col_indices_d.begin(), P.begin());
return thrust::inner_product(P.begin(), P.end(), r1.begin(), 0.0f);
}
};
and the loop itself:
loop lp(thrust::raw_pointer_cast(row_begin_d.data()),
thrust::raw_pointer_cast(col_indices_d.data()),
thrust::raw_pointer_cast(values_d.data()),
thrust::raw_pointer_cast(r1.data()));
auto iter = thrust::make_counting_iterator(0);
// perform the operations for each iteration of the loop using transform
thrust::transform(iter, iter + N, r2.begin(), lp);
3) I even tried passing arguments to operator rather than the constructor of the functor:
struct loop {
__host__ __device__
float operator()(int i, thrust::device_vector<int>& col_indices, thrust::device_vector<float>& values_d, thrust::device_vector<int>& row_begin, thrust::device_vector<float>& r1) const {
thrust::device_vector<float> P(N, 0.0);
thrust::device_vector<int> corr_col_indices_d(col_indices.begin() + row_begin[i], col_indices.begin() + row_begin[i+1]); // indices of the columns
thrust::device_vector<float> corr_values_d(values_d.begin() + row_begin[i], values_d.begin() + row_begin[i+1]); // values of the columns
thrust::scatter(corr_values_d.begin(), corr_values_d.end(), corr_col_indices_d.begin(), P.begin());
return thrust::inner_product(P.begin(), P.end(), r1.begin(), 0.0f);
}
};
auto iter = thrust::make_counting_iterator(0);
thrust::transform(iter, iter + N, r2.begin(),
thrust::make_transform_iterator(iter, loop()),
thrust::make_zip_iterator(thrust::make_tuple(col_indices, values_d, row_begin, r1)));
None of them compiles and all those complicated error messages don't really help. So, I'm looking for some assistance at this point.
CUDA version: 11.2
Thrust version: 1.10.0
Edit: In case you wonder, those vectors correspond to components of CSR matrix representation:
vector<int> row_begin;
vector<float> values;
vector<int> col_indices;
Updates
- Fused
transform
andreduce
toinner_product
. as suggested by @paleonix.