thrust
Vectors
Just like the std::vector
.
#include <thrust/host_vector.h>
#include <thrust/device_vector.h>
#include <thrust/copy.h>
#include <thrust/fill.h>
#include <thrust/sequence.h>
#include <iostream>
int main(void) {
thrust::host_vector<int> H(10, 0);
thrust::device_vector<int> D = H; // support H <--> D copy (call cudaMemcpy inside)
int s = H.size();
D[0] = 0;
int x = H[0];
// functions
thrust::fill(D.begin(), D.begin + 3, 1);
thrust::sequence(H.begin, H.end()); // assign range [0, 1, ..., H.size() - 1]
thrust::copy(H.begin(), H.end(), D.begin()); // copy all H to D
// also support raw device array, but need to wrap it with device_ptr<T>
int *arr;
cudaMalloc((void**)&arr, 10 * sizeof(int));
thrust::device_ptr<int> dev_arr(arr); // wrap!
thrust::fill(dev_arr, dev_arr + 10, 1);
// also support unwrap.
thrust::device_ptr<int> dev_ptr = thrust::device_malloc<int>(N);
int* arr = thrust::raw_pointer_cast(dev_arr); // unwrap
// support copy to STL
vector<int> S(D.size());
thrust::copy(D.begin(), D.end(), S.begin()); // cudaMemcpy inside
}
Algorithms
Thrust algorithms will auto detect host/device and run the correct version.
#include <thrust/device_vector.h>
#include <thrust/transform.h>
#include <thrust/sequence.h>
#include <thrust/copy.h>
#include <thrust/fill.h>
#include <thrust/replace.h>
#include <thrust/scan.h>
#include <thrust/sort.h>
#include <thrust/functional.h>
#include <iostream>
int main(void)
{
// allocate three device_vectors with 10 elements
thrust::device_vector<int> X(10);
thrust::device_vector<int> Y(10);
thrust::device_vector<int> Z(10);
// initialize X to 0,1,2,3, ....
thrust::sequence(X.begin(), X.end());
// compute Y = -X
thrust::transform(X.begin(), X.end(), Y.begin(), thrust::negate<int>());
// fill Z with twos
thrust::fill(Z.begin(), Z.end(), 2);
// compute Y = X mod 2
thrust::transform(X.begin(), X.end(), Z.begin(), Y.begin(), thrust::modulus<int>());
// replace all the ones in Y with tens
thrust::replace(Y.begin(), Y.end(), 1, 10);
// reduce
int sum = thrust::reduce(D.begin(), D.end(), (int) 0, thrust::plus<int>());
int sum = thrust::reduce(D.begin(), D.end(), (int) 0);
int sum = thrust::reduce(D.begin(), D.end())
// count the 1s
thrust::device_vector<int> vec(5,0);
vec[1] = 1;
vec[3] = 1;
vec[4] = 1;
int result = thrust::count(vec.begin(), vec.end(), 1); // 3
// prefix-sum (scan)
int data[6] = {1, 0, 2, 2, 1, 3};
thrust::inclusive_scan(data, data + 6, data); // in-place, data is now {1, 1, 3, 5, 6, 9}
int data[6] = {1, 0, 2, 2, 1, 3};
thrust::exclusive_scan(data, data + 6, data); // in-place, data is now {0, 1, 1, 3, 5, 6} (right shifted)
// sort
const int N = 6;
int A[N] = {1, 4, 2, 8, 5, 7};
thrust::sort(A, A + N); // in-place sort
int keys[N] = {1, 4, 2, 8, 5, 7};
char values[N] = {'a', 'b', 'c', 'd', 'e', 'f'};
thrust::sort_by_key(keys, keys + N, values); // both keys and values are sorted in-place
// print Y
thrust::copy(Y.begin(), Y.end(), std::ostream_iterator<int>(std::cout, "\n"));
return 0;
}
transform
is very useful for paralleled vector operations (at most two parameters), for example, we can implement our own function and parallel it:
// custorm_func(x, y) = x + y * a;
struct custom_func {
const float a;
custom_func(float _a) : a(_a) {}
__host__ __device__ float operator() (const float& x, const float& y) const {
return x + y * a;
}
};
// call with
thrust::transform(X.begin(), X.end(), Y.begin(), Y.end(), custom_func(a));
// square<T> computes the square of a number f(x) -> x*x
template <typename T>
struct square
{
__host__ __device__ T operator()(const T& x) const {
return x * x;
}
};
// compute norm
float norm = std::sqrt(thrust::transform_reduce(d_x.begin(), d_x.end(), square<float>(), 0, thrust::plus<float>()));
iterators
#include <thrust/iterator/constant_iterator.h>
thrust::constant_iterator<int> first(10); // init value is 10
first[0]; // 10
first[100]; // 10
thrust.reduce(first, first + 3); // 30
#include <thrust/iterator/counting_iterator.h>
thrust::counting_iterator<int> first(10); // init value is 10, auto incremental
first[0]; // 10
first[1]; // 11
first[100]; // 110
#include <thrust/iterator/zip_iterator.h>
// initialize vectors
thrust::device_vector<int> A(3);
thrust::device_vector<char> B(3);
A[0] = 10; A[1] = 20; A[2] = 30;
B[0] = 'x'; B[1] = 'y'; B[2] = 'z';
// create iterator (type omitted)
first = thrust::make_zip_iterator(thrust::make_tuple(A.begin(), B.begin()));
last = thrust::make_zip_iterator(thrust::make_tuple(A.end(), B.end()));
first[0] // returns tuple(10, 'x')
first[1] // returns tuple(20, 'y')
first[2] // returns tuple(30, 'z')
// maximum of [first, last)
thrust::tuple<int,char> init = first[0];
thrust::reduce(first, last, init, thrust::maximum<tuple<int,char>>()); // returns tuple(30, 'z')