Performance of thrust::count

cuda, thrust

Solution

The performance of `thrust::count` is just fine, it is the way you are trying to use it that is problematic for performance. If you had a lot of particles and only a few cells, then your implementation using `thrust::count` is probably not a bad idea. Your problem is that you have 1500 cells. That means 1500 invocations of `count` and 1500 device to host memory transfers each time you want to do the computation. The latency of all the kernel launches and all the PCI-e bus copies will kill performance, as you have found.

A better approach for a large number of cells would be something like this:

thrust::device_ptr<int> rawin = thrust::device_pointer_cast(dev_particle_cell_indices);

// Sort a scratch copy of the cell indices by value
thrust::device_vector<int> cidx(num_particles);
thrust::copy(rawin, rawin+num_particles, cidx.begin());
thrust::sort(cidx.begin(), cidx.end());

// Use binary search to extract all the cell counts/offsets
thrust::counting_iterator<int> cellnumber(0);
thrust::device_vector<int> offsets(num_cells), counts(num_cells);

// Offsets come from lower_bound of the ordered cell numbers
thrust::lower_bound(cidx.begin(), cidx.end(), cellnumber, cellnumber+num_cells, offsets.begin());

// Counts come from the adjacent_difference of the upper_bound of the ordered cell numbers
thrust::upper_bound(cidx.begin(), cidx.end(), cellnumber, cellnumber+num_cells, counts.begin());
thrust::adjacent_difference(counts.begin(), counts.end(), counts.begin());

// Copy back to the host pointer
thrust::copy(counts.begin(), counts.end(), particle_counts);
thrust::copy(offsets.begin(), offsets.end(), particle_offsets);

Here, we first sort a local copy of the cell indices, then use the thrust binary search functions to perform the same operation as your code, but with far fewer passes through the data in GPU memory and only two device to host memory copies to get all the results back to the host.

When I benchmark your `thrust::count` implementation with the code I posted above for a non-trivial case (10000 random particles and 2000 cells on a GeForce 320M with CUDA 4.1 on OS X), I find that your version takes about 0.95 seconds to run, whereas the sort/search version takes about 0.003 seconds to run. So there is probably several hundred times speedup available to you using thrust if you use a more efficient strategy and more appropriate algorithms.

Problem

I have the following code as part of a reorganization of data for later use in a CUDA kernel: ``` thrust::device_ptr<int> dev_ptr = thrust::device_pointer_cast(dev_particle_cell_indices); int total = 0; for(int i = 0; i < num_cells; i++) { particle_offsets[i] = total; // int num = 0; int num = thrust::count(dev_ptr, dev_ptr + num_particles, i); particle_counts[i] = num; total += num; } ``` Now, if I set `num` to 0 (uncomment the 5th line, and comment out the 6th), the application runs at over 30 fps, which is my goal. However, when I set `num` equal to the `thrust::count` call, the framerate drops to about 1-2 fps. Why would that happen? My understanding is that thrust is supposed to be a collection of highly optimized algorithms which harness the power of the GPU, so I'm surprised that it would have that kind of impact on the performance of my program. This is my first time using thrust though, so I may be unaware of some important details. Is there something about using `thrust::count` in a loop which is causing it to run so slowly? How can I optimize my usage of it? To give some figures, in my current test case, `num_particles` is about 2000, and `num_cells` is about 1500.

Original source