stabilize build system: depends, installer, boost/bdb fixes, cross targets groundwork
This commit is contained in:
@@ -0,0 +1,162 @@
|
||||
//---------------------------------------------------------------------------//
|
||||
// Copyright (c) 2014 Roshan <thisisroshansmail@gmail.com>
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0
|
||||
// See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt
|
||||
//
|
||||
// See http://boostorg.github.com/compute for more information.
|
||||
//---------------------------------------------------------------------------//
|
||||
|
||||
#ifndef BOOST_COMPUTE_ALGORITHM_DETAIL_BALANCED_PATH_HPP
|
||||
#define BOOST_COMPUTE_ALGORITHM_DETAIL_BALANCED_PATH_HPP
|
||||
|
||||
#include <iterator>
|
||||
|
||||
#include <boost/compute/algorithm/find_if.hpp>
|
||||
#include <boost/compute/container/vector.hpp>
|
||||
#include <boost/compute/detail/iterator_range_size.hpp>
|
||||
#include <boost/compute/detail/meta_kernel.hpp>
|
||||
#include <boost/compute/lambda.hpp>
|
||||
#include <boost/compute/system.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace compute {
|
||||
namespace detail {
|
||||
|
||||
///
|
||||
/// \brief Balanced Path kernel class
|
||||
///
|
||||
/// Subclass of meta_kernel to break two sets into tiles according
|
||||
/// to their balanced path.
|
||||
///
|
||||
class balanced_path_kernel : public meta_kernel
|
||||
{
|
||||
public:
|
||||
unsigned int tile_size;
|
||||
|
||||
balanced_path_kernel() : meta_kernel("balanced_path")
|
||||
{
|
||||
tile_size = 4;
|
||||
}
|
||||
|
||||
template<class InputIterator1, class InputIterator2,
|
||||
class OutputIterator1, class OutputIterator2,
|
||||
class Compare>
|
||||
void set_range(InputIterator1 first1,
|
||||
InputIterator1 last1,
|
||||
InputIterator2 first2,
|
||||
InputIterator2 last2,
|
||||
OutputIterator1 result_a,
|
||||
OutputIterator2 result_b,
|
||||
Compare comp)
|
||||
{
|
||||
typedef typename std::iterator_traits<InputIterator1>::value_type value_type;
|
||||
|
||||
m_a_count = iterator_range_size(first1, last1);
|
||||
m_a_count_arg = add_arg<uint_>("a_count");
|
||||
|
||||
m_b_count = iterator_range_size(first2, last2);
|
||||
m_b_count_arg = add_arg<uint_>("b_count");
|
||||
|
||||
*this <<
|
||||
"uint i = get_global_id(0);\n" <<
|
||||
"uint target = (i+1)*" << tile_size << ";\n" <<
|
||||
"uint start = max(convert_int(0),convert_int(target)-convert_int(b_count));\n" <<
|
||||
"uint end = min(target,a_count);\n" <<
|
||||
"uint a_index, b_index;\n" <<
|
||||
"while(start<end)\n" <<
|
||||
"{\n" <<
|
||||
" a_index = (start + end)/2;\n" <<
|
||||
" b_index = target - a_index - 1;\n" <<
|
||||
" if(!(" << comp(first2[expr<uint_>("b_index")],
|
||||
first1[expr<uint_>("a_index")]) << "))\n" <<
|
||||
" start = a_index + 1;\n" <<
|
||||
" else end = a_index;\n" <<
|
||||
"}\n" <<
|
||||
"a_index = start;\n" <<
|
||||
"b_index = target - start;\n" <<
|
||||
"if(b_index < b_count)\n" <<
|
||||
"{\n" <<
|
||||
" " << decl<const value_type>("x") << " = " <<
|
||||
first2[expr<uint_>("b_index")] << ";\n" <<
|
||||
" uint a_start = 0, a_end = a_index, a_mid;\n" <<
|
||||
" uint b_start = 0, b_end = b_index, b_mid;\n" <<
|
||||
" while(a_start<a_end)\n" <<
|
||||
" {\n" <<
|
||||
" a_mid = (a_start + a_end)/2;\n" <<
|
||||
" if(" << comp(first1[expr<uint_>("a_mid")], expr<value_type>("x")) << ")\n" <<
|
||||
" a_start = a_mid+1;\n" <<
|
||||
" else a_end = a_mid;\n" <<
|
||||
" }\n" <<
|
||||
" while(b_start<b_end)\n" <<
|
||||
" {\n" <<
|
||||
" b_mid = (b_start + b_end)/2;\n" <<
|
||||
" if(" << comp(first2[expr<uint_>("b_mid")], expr<value_type>("x")) << ")\n" <<
|
||||
" b_start = b_mid+1;\n" <<
|
||||
" else b_end = b_mid;\n" <<
|
||||
" }\n" <<
|
||||
" uint a_run = a_index - a_start;\n" <<
|
||||
" uint b_run = b_index - b_start;\n" <<
|
||||
" uint x_count = a_run + b_run;\n" <<
|
||||
" uint b_advance = max(x_count / 2, x_count - a_run);\n" <<
|
||||
" b_end = min(b_count, b_start + b_advance + 1);\n" <<
|
||||
" uint temp_start = b_index, temp_end = b_end, temp_mid;" <<
|
||||
" while(temp_start < temp_end)\n" <<
|
||||
" {\n" <<
|
||||
" temp_mid = (temp_start + temp_end + 1)/2;\n" <<
|
||||
" if(" << comp(expr<value_type>("x"), first2[expr<uint_>("temp_mid")]) << ")\n" <<
|
||||
" temp_end = temp_mid-1;\n" <<
|
||||
" else temp_start = temp_mid;\n" <<
|
||||
" }\n" <<
|
||||
" b_run = temp_start - b_start + 1;\n" <<
|
||||
" b_advance = min(b_advance, b_run);\n" <<
|
||||
" uint a_advance = x_count - b_advance;\n" <<
|
||||
" uint star = convert_uint((a_advance == b_advance + 1) " <<
|
||||
"&& (b_advance < b_run));\n" <<
|
||||
" a_index = a_start + a_advance;\n" <<
|
||||
" b_index = target - a_index + star;\n" <<
|
||||
"}\n" <<
|
||||
result_a[expr<uint_>("i")] << " = a_index;\n" <<
|
||||
result_b[expr<uint_>("i")] << " = b_index;\n";
|
||||
|
||||
}
|
||||
|
||||
template<class InputIterator1, class InputIterator2,
|
||||
class OutputIterator1, class OutputIterator2>
|
||||
void set_range(InputIterator1 first1,
|
||||
InputIterator1 last1,
|
||||
InputIterator2 first2,
|
||||
InputIterator2 last2,
|
||||
OutputIterator1 result_a,
|
||||
OutputIterator2 result_b)
|
||||
{
|
||||
typedef typename std::iterator_traits<InputIterator1>::value_type value_type;
|
||||
::boost::compute::less<value_type> less_than;
|
||||
set_range(first1, last1, first2, last2, result_a, result_b, less_than);
|
||||
}
|
||||
|
||||
event exec(command_queue &queue)
|
||||
{
|
||||
if((m_a_count + m_b_count)/tile_size == 0) {
|
||||
return event();
|
||||
}
|
||||
|
||||
set_arg(m_a_count_arg, uint_(m_a_count));
|
||||
set_arg(m_b_count_arg, uint_(m_b_count));
|
||||
|
||||
return exec_1d(queue, 0, (m_a_count + m_b_count)/tile_size);
|
||||
}
|
||||
|
||||
private:
|
||||
size_t m_a_count;
|
||||
size_t m_a_count_arg;
|
||||
size_t m_b_count;
|
||||
size_t m_b_count_arg;
|
||||
};
|
||||
|
||||
} //end detail namespace
|
||||
} //end compute namespace
|
||||
} //end boost namespace
|
||||
|
||||
#endif // BOOST_COMPUTE_ALGORITHM_DETAIL_BALANCED_PATH_HPP
|
||||
@@ -0,0 +1,133 @@
|
||||
//---------------------------------------------------------------------------//
|
||||
// Copyright (c) 2014 Roshan <thisisroshansmail@gmail.com>
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0
|
||||
// See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt
|
||||
//
|
||||
// See http://boostorg.github.com/compute for more information.
|
||||
//---------------------------------------------------------------------------//
|
||||
|
||||
#ifndef BOOST_COMPUTE_ALGORITHM_DETAIL_BINARY_FIND_HPP
|
||||
#define BOOST_COMPUTE_ALGORITHM_DETAIL_BINARY_FIND_HPP
|
||||
|
||||
#include <boost/compute/functional.hpp>
|
||||
#include <boost/compute/algorithm/find_if.hpp>
|
||||
#include <boost/compute/algorithm/transform.hpp>
|
||||
#include <boost/compute/command_queue.hpp>
|
||||
#include <boost/compute/detail/parameter_cache.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace compute {
|
||||
namespace detail{
|
||||
|
||||
///
|
||||
/// \brief Binary find kernel class
|
||||
///
|
||||
/// Subclass of meta_kernel to perform single step in binary find.
|
||||
///
|
||||
template<class InputIterator, class UnaryPredicate>
|
||||
class binary_find_kernel : public meta_kernel
|
||||
{
|
||||
public:
|
||||
binary_find_kernel(InputIterator first,
|
||||
InputIterator last,
|
||||
UnaryPredicate predicate)
|
||||
: meta_kernel("binary_find")
|
||||
{
|
||||
typedef typename std::iterator_traits<InputIterator>::value_type value_type;
|
||||
|
||||
m_index_arg = add_arg<uint_ *>(memory_object::global_memory, "index");
|
||||
m_block_arg = add_arg<uint_>("block");
|
||||
|
||||
atomic_min<uint_> atomic_min_uint;
|
||||
|
||||
*this <<
|
||||
"uint i = get_global_id(0) * block;\n" <<
|
||||
decl<value_type>("value") << "=" << first[var<uint_>("i")] << ";\n" <<
|
||||
"if(" << predicate(var<value_type>("value")) << ") {\n" <<
|
||||
atomic_min_uint(var<uint_ *>("index"), var<uint_>("i")) << ";\n" <<
|
||||
"}\n";
|
||||
}
|
||||
|
||||
size_t m_index_arg;
|
||||
size_t m_block_arg;
|
||||
};
|
||||
|
||||
///
|
||||
/// \brief Binary find algorithm
|
||||
///
|
||||
/// Finds the end of true values in the partitioned range [first, last).
|
||||
/// \return Iterator pointing to end of true values
|
||||
///
|
||||
/// \param first Iterator pointing to start of range
|
||||
/// \param last Iterator pointing to end of range
|
||||
/// \param predicate Predicate according to which the range is partitioned
|
||||
/// \param queue Queue on which to execute
|
||||
///
|
||||
template<class InputIterator, class UnaryPredicate>
|
||||
inline InputIterator binary_find(InputIterator first,
|
||||
InputIterator last,
|
||||
UnaryPredicate predicate,
|
||||
command_queue &queue = system::default_queue())
|
||||
{
|
||||
const device &device = queue.get_device();
|
||||
|
||||
boost::shared_ptr<parameter_cache> parameters =
|
||||
detail::parameter_cache::get_global_cache(device);
|
||||
|
||||
const std::string cache_key = "__boost_binary_find";
|
||||
|
||||
size_t find_if_limit = 128;
|
||||
size_t threads = parameters->get(cache_key, "tpb", 128);
|
||||
size_t count = iterator_range_size(first, last);
|
||||
|
||||
InputIterator search_first = first;
|
||||
InputIterator search_last = last;
|
||||
|
||||
scalar<uint_> index(queue.get_context());
|
||||
|
||||
// construct and compile binary_find kernel
|
||||
binary_find_kernel<InputIterator, UnaryPredicate>
|
||||
binary_find_kernel(search_first, search_last, predicate);
|
||||
::boost::compute::kernel kernel = binary_find_kernel.compile(queue.get_context());
|
||||
|
||||
// set buffer for index
|
||||
kernel.set_arg(binary_find_kernel.m_index_arg, index.get_buffer());
|
||||
|
||||
while(count > find_if_limit) {
|
||||
index.write(static_cast<uint_>(count), queue);
|
||||
|
||||
// set block and run binary_find kernel
|
||||
uint_ block = static_cast<uint_>((count - 1)/(threads - 1));
|
||||
kernel.set_arg(binary_find_kernel.m_block_arg, block);
|
||||
queue.enqueue_1d_range_kernel(kernel, 0, threads, 0);
|
||||
|
||||
size_t i = index.read(queue);
|
||||
|
||||
if(i == count) {
|
||||
search_first = search_last - ((count - 1)%(threads - 1));
|
||||
break;
|
||||
} else {
|
||||
search_last = search_first + i;
|
||||
search_first = search_last - ((count - 1)/(threads - 1));
|
||||
}
|
||||
|
||||
// Make sure that first and last stay within the input range
|
||||
search_last = (std::min)(search_last, last);
|
||||
search_last = (std::max)(search_last, first);
|
||||
|
||||
search_first = (std::max)(search_first, first);
|
||||
search_first = (std::min)(search_first, last);
|
||||
|
||||
count = iterator_range_size(search_first, search_last);
|
||||
}
|
||||
|
||||
return find_if(search_first, search_last, predicate, queue);
|
||||
}
|
||||
|
||||
} // end detail namespace
|
||||
} // end compute namespace
|
||||
} // end boost namespace
|
||||
|
||||
#endif // BOOST_COMPUTE_ALGORITHM_DETAIL_BINARY_FIND_HPP
|
||||
@@ -0,0 +1,77 @@
|
||||
//---------------------------------------------------------------------------//
|
||||
// Copyright (c) 2014 Roshan <thisisroshansmail@gmail.com>
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0
|
||||
// See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt
|
||||
//
|
||||
// See http://boostorg.github.com/compute for more information.
|
||||
//---------------------------------------------------------------------------//
|
||||
|
||||
#ifndef BOOST_COMPUTE_ALGORITHM_DETAIL_COMPACT_HPP
|
||||
#define BOOST_COMPUTE_ALGORITHM_DETAIL_COMPACT_HPP
|
||||
|
||||
#include <iterator>
|
||||
|
||||
#include <boost/compute/container/vector.hpp>
|
||||
#include <boost/compute/detail/iterator_range_size.hpp>
|
||||
#include <boost/compute/detail/meta_kernel.hpp>
|
||||
#include <boost/compute/system.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace compute {
|
||||
namespace detail {
|
||||
|
||||
///
|
||||
/// \brief Compact kernel class
|
||||
///
|
||||
/// Subclass of meta_kernel to compact the result of set kernels to
|
||||
/// get actual sets
|
||||
///
|
||||
class compact_kernel : public meta_kernel
|
||||
{
|
||||
public:
|
||||
unsigned int tile_size;
|
||||
|
||||
compact_kernel() : meta_kernel("compact")
|
||||
{
|
||||
tile_size = 4;
|
||||
}
|
||||
|
||||
template<class InputIterator1, class InputIterator2, class OutputIterator>
|
||||
void set_range(InputIterator1 start,
|
||||
InputIterator2 counts_begin,
|
||||
InputIterator2 counts_end,
|
||||
OutputIterator result)
|
||||
{
|
||||
m_count = iterator_range_size(counts_begin, counts_end) - 1;
|
||||
|
||||
*this <<
|
||||
"uint i = get_global_id(0);\n" <<
|
||||
"uint count = i*" << tile_size << ";\n" <<
|
||||
"for(uint j = " << counts_begin[expr<uint_>("i")] << "; j<" <<
|
||||
counts_begin[expr<uint_>("i+1")] << "; j++, count++)\n" <<
|
||||
"{\n" <<
|
||||
result[expr<uint_>("j")] << " = " << start[expr<uint_>("count")]
|
||||
<< ";\n" <<
|
||||
"}\n";
|
||||
}
|
||||
|
||||
event exec(command_queue &queue)
|
||||
{
|
||||
if(m_count == 0) {
|
||||
return event();
|
||||
}
|
||||
|
||||
return exec_1d(queue, 0, m_count);
|
||||
}
|
||||
|
||||
private:
|
||||
size_t m_count;
|
||||
};
|
||||
|
||||
} //end detail namespace
|
||||
} //end compute namespace
|
||||
} //end boost namespace
|
||||
|
||||
#endif // BOOST_COMPUTE_ALGORITHM_DETAIL_COMPACT_HPP
|
||||
+190
@@ -0,0 +1,190 @@
|
||||
//---------------------------------------------------------------------------//
|
||||
// Copyright (c) 2013 Kyle Lutz <kyle.r.lutz@gmail.com>
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0
|
||||
// See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt
|
||||
//
|
||||
// See http://boostorg.github.com/compute for more information.
|
||||
//---------------------------------------------------------------------------//
|
||||
|
||||
#ifndef BOOST_COMPUTE_ALGORITHM_DETAIL_COPY_ON_DEVICE_HPP
|
||||
#define BOOST_COMPUTE_ALGORITHM_DETAIL_COPY_ON_DEVICE_HPP
|
||||
|
||||
#include <iterator>
|
||||
|
||||
#include <boost/compute/command_queue.hpp>
|
||||
#include <boost/compute/async/future.hpp>
|
||||
#include <boost/compute/iterator/buffer_iterator.hpp>
|
||||
#include <boost/compute/iterator/discard_iterator.hpp>
|
||||
#include <boost/compute/memory/svm_ptr.hpp>
|
||||
#include <boost/compute/detail/iterator_range_size.hpp>
|
||||
#include <boost/compute/detail/meta_kernel.hpp>
|
||||
#include <boost/compute/detail/parameter_cache.hpp>
|
||||
#include <boost/compute/detail/work_size.hpp>
|
||||
#include <boost/compute/detail/vendor.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace compute {
|
||||
namespace detail {
|
||||
|
||||
template<class InputIterator, class OutputIterator>
|
||||
inline event copy_on_device_cpu(InputIterator first,
|
||||
OutputIterator result,
|
||||
size_t count,
|
||||
command_queue &queue)
|
||||
{
|
||||
meta_kernel k("copy");
|
||||
const device& device = queue.get_device();
|
||||
|
||||
k <<
|
||||
"uint block = " <<
|
||||
"(uint)ceil(((float)count)/get_global_size(0));\n" <<
|
||||
"uint index = get_global_id(0) * block;\n" <<
|
||||
"uint end = min(count, index + block);\n" <<
|
||||
"while(index < end){\n" <<
|
||||
result[k.var<uint_>("index")] << '=' <<
|
||||
first[k.var<uint_>("index")] << ";\n" <<
|
||||
"index++;\n" <<
|
||||
"}\n";
|
||||
|
||||
k.add_set_arg<const uint_>("count", static_cast<uint_>(count));
|
||||
|
||||
size_t global_work_size = device.compute_units();
|
||||
if(count <= 1024) global_work_size = 1;
|
||||
return k.exec_1d(queue, 0, global_work_size);
|
||||
}
|
||||
|
||||
template<class InputIterator, class OutputIterator>
|
||||
inline event copy_on_device_gpu(InputIterator first,
|
||||
OutputIterator result,
|
||||
size_t count,
|
||||
command_queue &queue)
|
||||
{
|
||||
typedef typename std::iterator_traits<InputIterator>::value_type input_type;
|
||||
|
||||
const device& device = queue.get_device();
|
||||
boost::shared_ptr<parameter_cache> parameters =
|
||||
detail::parameter_cache::get_global_cache(device);
|
||||
std::string cache_key =
|
||||
"__boost_copy_kernel_" + boost::lexical_cast<std::string>(sizeof(input_type));
|
||||
|
||||
uint_ vpt = parameters->get(cache_key, "vpt", 4);
|
||||
uint_ tpb = parameters->get(cache_key, "tpb", 128);
|
||||
|
||||
meta_kernel k("copy");
|
||||
k <<
|
||||
"uint index = get_local_id(0) + " <<
|
||||
"(" << vpt * tpb << " * get_group_id(0));\n" <<
|
||||
"for(uint i = 0; i < " << vpt << "; i++){\n" <<
|
||||
" if(index < count){\n" <<
|
||||
result[k.var<uint_>("index")] << '=' <<
|
||||
first[k.var<uint_>("index")] << ";\n" <<
|
||||
" index += " << tpb << ";\n"
|
||||
" }\n"
|
||||
"}\n";
|
||||
|
||||
k.add_set_arg<const uint_>("count", static_cast<uint_>(count));
|
||||
size_t global_work_size = calculate_work_size(count, vpt, tpb);
|
||||
return k.exec_1d(queue, 0, global_work_size, tpb);
|
||||
}
|
||||
|
||||
template<class InputIterator, class OutputIterator>
|
||||
inline event dispatch_copy_on_device(InputIterator first,
|
||||
InputIterator last,
|
||||
OutputIterator result,
|
||||
command_queue &queue)
|
||||
{
|
||||
const size_t count = detail::iterator_range_size(first, last);
|
||||
|
||||
if(count == 0){
|
||||
// nothing to do
|
||||
return event();
|
||||
}
|
||||
|
||||
const device& device = queue.get_device();
|
||||
// copy_on_device_cpu() does not work for CPU on Apple platform
|
||||
// due to bug in its compiler.
|
||||
// See https://github.com/boostorg/compute/pull/626
|
||||
if((device.type() & device::cpu) && !is_apple_platform_device(device))
|
||||
{
|
||||
return copy_on_device_cpu(first, result, count, queue);
|
||||
}
|
||||
return copy_on_device_gpu(first, result, count, queue);
|
||||
}
|
||||
|
||||
template<class InputIterator, class OutputIterator>
|
||||
inline OutputIterator copy_on_device(InputIterator first,
|
||||
InputIterator last,
|
||||
OutputIterator result,
|
||||
command_queue &queue)
|
||||
{
|
||||
dispatch_copy_on_device(first, last, result, queue);
|
||||
return result + std::distance(first, last);
|
||||
}
|
||||
|
||||
template<class InputIterator>
|
||||
inline discard_iterator copy_on_device(InputIterator first,
|
||||
InputIterator last,
|
||||
discard_iterator result,
|
||||
command_queue &queue)
|
||||
{
|
||||
(void) queue;
|
||||
|
||||
return result + std::distance(first, last);
|
||||
}
|
||||
|
||||
template<class InputIterator, class OutputIterator>
|
||||
inline future<OutputIterator> copy_on_device_async(InputIterator first,
|
||||
InputIterator last,
|
||||
OutputIterator result,
|
||||
command_queue &queue)
|
||||
{
|
||||
event event_ = dispatch_copy_on_device(first, last, result, queue);
|
||||
return make_future(result + std::distance(first, last), event_);
|
||||
}
|
||||
|
||||
#ifdef CL_VERSION_2_0
|
||||
// copy_on_device() specialization for svm_ptr
|
||||
template<class T>
|
||||
inline svm_ptr<T> copy_on_device(svm_ptr<T> first,
|
||||
svm_ptr<T> last,
|
||||
svm_ptr<T> result,
|
||||
command_queue &queue)
|
||||
{
|
||||
size_t count = iterator_range_size(first, last);
|
||||
if(count == 0){
|
||||
return result;
|
||||
}
|
||||
|
||||
queue.enqueue_svm_memcpy(
|
||||
result.get(), first.get(), count * sizeof(T)
|
||||
);
|
||||
|
||||
return result + count;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
inline future<svm_ptr<T> > copy_on_device_async(svm_ptr<T> first,
|
||||
svm_ptr<T> last,
|
||||
svm_ptr<T> result,
|
||||
command_queue &queue)
|
||||
{
|
||||
size_t count = iterator_range_size(first, last);
|
||||
if(count == 0){
|
||||
return future<svm_ptr<T> >();
|
||||
}
|
||||
|
||||
event event_ = queue.enqueue_svm_memcpy_async(
|
||||
result.get(), first.get(), count * sizeof(T)
|
||||
);
|
||||
|
||||
return make_future(result + count, event_);
|
||||
}
|
||||
#endif // CL_VERSION_2_0
|
||||
|
||||
} // end detail namespace
|
||||
} // end compute namespace
|
||||
} // end boost namespace
|
||||
|
||||
#endif // BOOST_COMPUTE_ALGORITHM_DETAIL_COPY_ON_DEVICE_HPP
|
||||
+193
@@ -0,0 +1,193 @@
|
||||
//---------------------------------------------------------------------------//
|
||||
// Copyright (c) 2013 Kyle Lutz <kyle.r.lutz@gmail.com>
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0
|
||||
// See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt
|
||||
//
|
||||
// See http://boostorg.github.com/compute for more information.
|
||||
//---------------------------------------------------------------------------//
|
||||
|
||||
#ifndef BOOST_COMPUTE_ALGORITHM_DETAIL_COPY_TO_DEVICE_HPP
|
||||
#define BOOST_COMPUTE_ALGORITHM_DETAIL_COPY_TO_DEVICE_HPP
|
||||
|
||||
#include <iterator>
|
||||
|
||||
#include <boost/utility/addressof.hpp>
|
||||
|
||||
#include <boost/compute/command_queue.hpp>
|
||||
#include <boost/compute/async/future.hpp>
|
||||
#include <boost/compute/iterator/buffer_iterator.hpp>
|
||||
#include <boost/compute/memory/svm_ptr.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace compute {
|
||||
namespace detail {
|
||||
|
||||
template<class HostIterator, class DeviceIterator>
|
||||
inline DeviceIterator copy_to_device(HostIterator first,
|
||||
HostIterator last,
|
||||
DeviceIterator result,
|
||||
command_queue &queue)
|
||||
{
|
||||
typedef typename
|
||||
std::iterator_traits<DeviceIterator>::value_type
|
||||
value_type;
|
||||
typedef typename
|
||||
std::iterator_traits<DeviceIterator>::difference_type
|
||||
difference_type;
|
||||
|
||||
size_t count = iterator_range_size(first, last);
|
||||
if(count == 0){
|
||||
return result;
|
||||
}
|
||||
|
||||
size_t offset = result.get_index();
|
||||
|
||||
queue.enqueue_write_buffer(result.get_buffer(),
|
||||
offset * sizeof(value_type),
|
||||
count * sizeof(value_type),
|
||||
::boost::addressof(*first));
|
||||
|
||||
return result + static_cast<difference_type>(count);
|
||||
}
|
||||
|
||||
template<class HostIterator, class DeviceIterator>
|
||||
inline DeviceIterator copy_to_device_map(HostIterator first,
|
||||
HostIterator last,
|
||||
DeviceIterator result,
|
||||
command_queue &queue)
|
||||
{
|
||||
typedef typename
|
||||
std::iterator_traits<DeviceIterator>::value_type
|
||||
value_type;
|
||||
typedef typename
|
||||
std::iterator_traits<DeviceIterator>::difference_type
|
||||
difference_type;
|
||||
|
||||
size_t count = iterator_range_size(first, last);
|
||||
if(count == 0){
|
||||
return result;
|
||||
}
|
||||
|
||||
size_t offset = result.get_index();
|
||||
|
||||
// map result buffer to host
|
||||
value_type *pointer = static_cast<value_type*>(
|
||||
queue.enqueue_map_buffer(
|
||||
result.get_buffer(),
|
||||
CL_MAP_WRITE,
|
||||
offset * sizeof(value_type),
|
||||
count * sizeof(value_type)
|
||||
)
|
||||
);
|
||||
|
||||
// copy [first; last) to result buffer
|
||||
std::copy(first, last, pointer);
|
||||
|
||||
// unmap result buffer
|
||||
boost::compute::event unmap_event = queue.enqueue_unmap_buffer(
|
||||
result.get_buffer(),
|
||||
static_cast<void*>(pointer)
|
||||
);
|
||||
unmap_event.wait();
|
||||
|
||||
return result + static_cast<difference_type>(count);
|
||||
}
|
||||
|
||||
template<class HostIterator, class DeviceIterator>
|
||||
inline future<DeviceIterator> copy_to_device_async(HostIterator first,
|
||||
HostIterator last,
|
||||
DeviceIterator result,
|
||||
command_queue &queue)
|
||||
{
|
||||
typedef typename
|
||||
std::iterator_traits<DeviceIterator>::value_type
|
||||
value_type;
|
||||
typedef typename
|
||||
std::iterator_traits<DeviceIterator>::difference_type
|
||||
difference_type;
|
||||
|
||||
size_t count = iterator_range_size(first, last);
|
||||
if(count == 0){
|
||||
return future<DeviceIterator>();
|
||||
}
|
||||
|
||||
size_t offset = result.get_index();
|
||||
|
||||
event event_ =
|
||||
queue.enqueue_write_buffer_async(result.get_buffer(),
|
||||
offset * sizeof(value_type),
|
||||
count * sizeof(value_type),
|
||||
::boost::addressof(*first));
|
||||
|
||||
return make_future(result + static_cast<difference_type>(count), event_);
|
||||
}
|
||||
|
||||
#ifdef CL_VERSION_2_0
|
||||
// copy_to_device() specialization for svm_ptr
|
||||
template<class HostIterator, class T>
|
||||
inline svm_ptr<T> copy_to_device(HostIterator first,
|
||||
HostIterator last,
|
||||
svm_ptr<T> result,
|
||||
command_queue &queue)
|
||||
{
|
||||
size_t count = iterator_range_size(first, last);
|
||||
if(count == 0){
|
||||
return result;
|
||||
}
|
||||
|
||||
queue.enqueue_svm_memcpy(
|
||||
result.get(), ::boost::addressof(*first), count * sizeof(T)
|
||||
);
|
||||
|
||||
return result + count;
|
||||
}
|
||||
|
||||
template<class HostIterator, class T>
|
||||
inline future<svm_ptr<T> > copy_to_device_async(HostIterator first,
|
||||
HostIterator last,
|
||||
svm_ptr<T> result,
|
||||
command_queue &queue)
|
||||
{
|
||||
size_t count = iterator_range_size(first, last);
|
||||
if(count == 0){
|
||||
return future<svm_ptr<T> >();
|
||||
}
|
||||
|
||||
event event_ = queue.enqueue_svm_memcpy_async(
|
||||
result.get(), ::boost::addressof(*first), count * sizeof(T)
|
||||
);
|
||||
|
||||
return make_future(result + count, event_);
|
||||
}
|
||||
|
||||
template<class HostIterator, class T>
|
||||
inline svm_ptr<T> copy_to_device_map(HostIterator first,
|
||||
HostIterator last,
|
||||
svm_ptr<T> result,
|
||||
command_queue &queue)
|
||||
{
|
||||
size_t count = iterator_range_size(first, last);
|
||||
if(count == 0){
|
||||
return result;
|
||||
}
|
||||
|
||||
// map
|
||||
queue.enqueue_svm_map(result.get(), count * sizeof(T), CL_MAP_WRITE);
|
||||
|
||||
// copy [first; last) to result buffer
|
||||
std::copy(first, last, static_cast<T*>(result.get()));
|
||||
|
||||
// unmap result
|
||||
queue.enqueue_svm_unmap(result.get()).wait();
|
||||
|
||||
return result + count;
|
||||
}
|
||||
#endif // CL_VERSION_2_0
|
||||
|
||||
} // end detail namespace
|
||||
} // end compute namespace
|
||||
} // end boost namespace
|
||||
|
||||
#endif // BOOST_COMPUTE_ALGORITHM_DETAIL_COPY_TO_DEVICE_HPP
|
||||
@@ -0,0 +1,198 @@
|
||||
//---------------------------------------------------------------------------//
|
||||
// Copyright (c) 2013 Kyle Lutz <kyle.r.lutz@gmail.com>
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0
|
||||
// See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt
|
||||
//
|
||||
// See http://boostorg.github.com/compute for more information.
|
||||
//---------------------------------------------------------------------------//
|
||||
|
||||
#ifndef BOOST_COMPUTE_ALGORITHM_DETAIL_COPY_TO_HOST_HPP
|
||||
#define BOOST_COMPUTE_ALGORITHM_DETAIL_COPY_TO_HOST_HPP
|
||||
|
||||
#include <iterator>
|
||||
|
||||
#include <boost/utility/addressof.hpp>
|
||||
|
||||
#include <boost/compute/command_queue.hpp>
|
||||
#include <boost/compute/async/future.hpp>
|
||||
#include <boost/compute/iterator/buffer_iterator.hpp>
|
||||
#include <boost/compute/memory/svm_ptr.hpp>
|
||||
#include <boost/compute/detail/iterator_plus_distance.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace compute {
|
||||
namespace detail {
|
||||
|
||||
template<class DeviceIterator, class HostIterator>
|
||||
inline HostIterator copy_to_host(DeviceIterator first,
|
||||
DeviceIterator last,
|
||||
HostIterator result,
|
||||
command_queue &queue)
|
||||
{
|
||||
typedef typename
|
||||
std::iterator_traits<DeviceIterator>::value_type
|
||||
value_type;
|
||||
|
||||
size_t count = iterator_range_size(first, last);
|
||||
if(count == 0){
|
||||
return result;
|
||||
}
|
||||
|
||||
const buffer &buffer = first.get_buffer();
|
||||
size_t offset = first.get_index();
|
||||
|
||||
queue.enqueue_read_buffer(buffer,
|
||||
offset * sizeof(value_type),
|
||||
count * sizeof(value_type),
|
||||
::boost::addressof(*result));
|
||||
|
||||
return iterator_plus_distance(result, count);
|
||||
}
|
||||
|
||||
template<class DeviceIterator, class HostIterator>
|
||||
inline HostIterator copy_to_host_map(DeviceIterator first,
|
||||
DeviceIterator last,
|
||||
HostIterator result,
|
||||
command_queue &queue)
|
||||
{
|
||||
typedef typename
|
||||
std::iterator_traits<DeviceIterator>::value_type
|
||||
value_type;
|
||||
typedef typename
|
||||
std::iterator_traits<DeviceIterator>::difference_type
|
||||
difference_type;
|
||||
|
||||
size_t count = iterator_range_size(first, last);
|
||||
if(count == 0){
|
||||
return result;
|
||||
}
|
||||
|
||||
size_t offset = first.get_index();
|
||||
|
||||
// map [first; last) buffer to host
|
||||
value_type *pointer = static_cast<value_type*>(
|
||||
queue.enqueue_map_buffer(
|
||||
first.get_buffer(),
|
||||
CL_MAP_READ,
|
||||
offset * sizeof(value_type),
|
||||
count * sizeof(value_type)
|
||||
)
|
||||
);
|
||||
|
||||
// copy [first; last) to result buffer
|
||||
std::copy(
|
||||
pointer,
|
||||
pointer + static_cast<difference_type>(count),
|
||||
result
|
||||
);
|
||||
|
||||
// unmap [first; last)
|
||||
boost::compute::event unmap_event = queue.enqueue_unmap_buffer(
|
||||
first.get_buffer(),
|
||||
static_cast<void*>(pointer)
|
||||
);
|
||||
unmap_event.wait();
|
||||
|
||||
return iterator_plus_distance(result, count);
|
||||
}
|
||||
|
||||
template<class DeviceIterator, class HostIterator>
|
||||
inline future<HostIterator> copy_to_host_async(DeviceIterator first,
|
||||
DeviceIterator last,
|
||||
HostIterator result,
|
||||
command_queue &queue)
|
||||
{
|
||||
typedef typename
|
||||
std::iterator_traits<DeviceIterator>::value_type
|
||||
value_type;
|
||||
|
||||
size_t count = iterator_range_size(first, last);
|
||||
if(count == 0){
|
||||
return future<HostIterator>();
|
||||
}
|
||||
|
||||
const buffer &buffer = first.get_buffer();
|
||||
size_t offset = first.get_index();
|
||||
|
||||
event event_ =
|
||||
queue.enqueue_read_buffer_async(buffer,
|
||||
offset * sizeof(value_type),
|
||||
count * sizeof(value_type),
|
||||
::boost::addressof(*result));
|
||||
|
||||
return make_future(iterator_plus_distance(result, count), event_);
|
||||
}
|
||||
|
||||
#ifdef CL_VERSION_2_0
|
||||
// copy_to_host() specialization for svm_ptr
|
||||
template<class T, class HostIterator>
|
||||
inline HostIterator copy_to_host(svm_ptr<T> first,
|
||||
svm_ptr<T> last,
|
||||
HostIterator result,
|
||||
command_queue &queue)
|
||||
{
|
||||
size_t count = iterator_range_size(first, last);
|
||||
if(count == 0){
|
||||
return result;
|
||||
}
|
||||
|
||||
queue.enqueue_svm_memcpy(
|
||||
::boost::addressof(*result), first.get(), count * sizeof(T)
|
||||
);
|
||||
|
||||
return result + count;
|
||||
}
|
||||
|
||||
template<class T, class HostIterator>
|
||||
inline future<HostIterator> copy_to_host_async(svm_ptr<T> first,
|
||||
svm_ptr<T> last,
|
||||
HostIterator result,
|
||||
command_queue &queue)
|
||||
{
|
||||
size_t count = iterator_range_size(first, last);
|
||||
if(count == 0){
|
||||
return future<HostIterator>();
|
||||
}
|
||||
|
||||
event event_ = queue.enqueue_svm_memcpy_async(
|
||||
::boost::addressof(*result), first.get(), count * sizeof(T)
|
||||
);
|
||||
|
||||
return make_future(iterator_plus_distance(result, count), event_);
|
||||
}
|
||||
|
||||
template<class T, class HostIterator>
|
||||
inline HostIterator copy_to_host_map(svm_ptr<T> first,
|
||||
svm_ptr<T> last,
|
||||
HostIterator result,
|
||||
command_queue &queue)
|
||||
{
|
||||
size_t count = iterator_range_size(first, last);
|
||||
if(count == 0){
|
||||
return result;
|
||||
}
|
||||
|
||||
// map
|
||||
queue.enqueue_svm_map(first.get(), count * sizeof(T), CL_MAP_READ);
|
||||
|
||||
// copy [first; last) to result
|
||||
std::copy(
|
||||
static_cast<T*>(first.get()),
|
||||
static_cast<T*>(last.get()),
|
||||
result
|
||||
);
|
||||
|
||||
// unmap [first; last)
|
||||
queue.enqueue_svm_unmap(first.get()).wait();
|
||||
|
||||
return iterator_plus_distance(result, count);
|
||||
}
|
||||
#endif // CL_VERSION_2_0
|
||||
|
||||
} // end detail namespace
|
||||
} // end compute namespace
|
||||
} // end boost namespace
|
||||
|
||||
#endif // BOOST_COMPUTE_ALGORITHM_DETAIL_COPY_TO_HOST_HPP
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
//---------------------------------------------------------------------------//
|
||||
// Copyright (c) 2013 Kyle Lutz <kyle.r.lutz@gmail.com>
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0
|
||||
// See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt
|
||||
//
|
||||
// See http://boostorg.github.com/compute for more information.
|
||||
//---------------------------------------------------------------------------//
|
||||
|
||||
#ifndef BOOST_COMPUTE_ALGORITHM_DETAIL_COUNT_IF_WITH_BALLOT_HPP
|
||||
#define BOOST_COMPUTE_ALGORITHM_DETAIL_COUNT_IF_WITH_BALLOT_HPP
|
||||
|
||||
#include <boost/compute/context.hpp>
|
||||
#include <boost/compute/command_queue.hpp>
|
||||
#include <boost/compute/container/vector.hpp>
|
||||
#include <boost/compute/algorithm/reduce.hpp>
|
||||
#include <boost/compute/functional/detail/nvidia_ballot.hpp>
|
||||
#include <boost/compute/functional/detail/nvidia_popcount.hpp>
|
||||
#include <boost/compute/detail/meta_kernel.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace compute {
|
||||
namespace detail {
|
||||
|
||||
template<class InputIterator, class Predicate>
|
||||
inline size_t count_if_with_ballot(InputIterator first,
|
||||
InputIterator last,
|
||||
Predicate predicate,
|
||||
command_queue &queue)
|
||||
{
|
||||
size_t count = iterator_range_size(first, last);
|
||||
size_t block_size = 32;
|
||||
size_t block_count = count / block_size;
|
||||
if(block_count * block_size != count){
|
||||
block_count++;
|
||||
}
|
||||
|
||||
const ::boost::compute::context &context = queue.get_context();
|
||||
|
||||
::boost::compute::vector<uint_> counts(block_count, context);
|
||||
|
||||
::boost::compute::detail::nvidia_popcount<uint_> popc;
|
||||
::boost::compute::detail::nvidia_ballot<uint_> ballot;
|
||||
|
||||
meta_kernel k("count_if_with_ballot");
|
||||
k <<
|
||||
"const uint gid = get_global_id(0);\n" <<
|
||||
|
||||
"bool value = false;\n" <<
|
||||
"if(gid < count)\n" <<
|
||||
" value = " << predicate(first[k.var<const uint_>("gid")]) << ";\n" <<
|
||||
|
||||
"uint bits = " << ballot(k.var<const uint_>("value")) << ";\n" <<
|
||||
|
||||
"if(get_local_id(0) == 0)\n" <<
|
||||
counts.begin()[k.var<uint_>("get_group_id(0)") ]
|
||||
<< " = " << popc(k.var<uint_>("bits")) << ";\n";
|
||||
|
||||
k.add_set_arg<const uint_>("count", count);
|
||||
|
||||
k.exec_1d(queue, 0, block_size * block_count, block_size);
|
||||
|
||||
uint_ result;
|
||||
::boost::compute::reduce(
|
||||
counts.begin(),
|
||||
counts.end(),
|
||||
&result,
|
||||
queue
|
||||
);
|
||||
return result;
|
||||
}
|
||||
|
||||
} // end detail namespace
|
||||
} // end compute namespace
|
||||
} // end boost namespace
|
||||
|
||||
#endif // BOOST_COMPUTE_ALGORITHM_DETAIL_COUNT_IF_WITH_BALLOT_HPP
|
||||
+87
@@ -0,0 +1,87 @@
|
||||
//---------------------------------------------------------------------------//
|
||||
// Copyright (c) 2013 Kyle Lutz <kyle.r.lutz@gmail.com>
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0
|
||||
// See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt
|
||||
//
|
||||
// See http://boostorg.github.com/compute for more information.
|
||||
//---------------------------------------------------------------------------//
|
||||
|
||||
#ifndef BOOST_COMPUTE_ALGORITHM_DETAIL_COUNT_IF_WITH_REDUCE_HPP
|
||||
#define BOOST_COMPUTE_ALGORITHM_DETAIL_COUNT_IF_WITH_REDUCE_HPP
|
||||
|
||||
#include <boost/compute/algorithm/reduce.hpp>
|
||||
#include <boost/compute/iterator/transform_iterator.hpp>
|
||||
#include <boost/compute/types/fundamental.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace compute {
|
||||
namespace detail {
|
||||
|
||||
template<class Predicate, class Arg>
|
||||
struct invoked_countable_predicate
|
||||
{
|
||||
invoked_countable_predicate(Predicate p, Arg a)
|
||||
: predicate(p), arg(a)
|
||||
{
|
||||
}
|
||||
|
||||
Predicate predicate;
|
||||
Arg arg;
|
||||
};
|
||||
|
||||
template<class Predicate, class Arg>
|
||||
inline meta_kernel& operator<<(meta_kernel &kernel,
|
||||
const invoked_countable_predicate<Predicate, Arg> &expr)
|
||||
{
|
||||
return kernel << "(" << expr.predicate(expr.arg) << " ? 1 : 0)";
|
||||
}
|
||||
|
||||
// the countable_predicate wraps Predicate and converts its result from
|
||||
// bool to ulong so that it can be used with reduce()
|
||||
template<class Predicate>
|
||||
struct countable_predicate
|
||||
{
|
||||
typedef ulong_ result_type;
|
||||
|
||||
countable_predicate(Predicate predicate)
|
||||
: m_predicate(predicate)
|
||||
{
|
||||
}
|
||||
|
||||
template<class Arg>
|
||||
invoked_countable_predicate<Predicate, Arg> operator()(const Arg &arg) const
|
||||
{
|
||||
return invoked_countable_predicate<Predicate, Arg>(m_predicate, arg);
|
||||
}
|
||||
|
||||
Predicate m_predicate;
|
||||
};
|
||||
|
||||
// counts the number of elements matching predicate using reduce()
|
||||
template<class InputIterator, class Predicate>
|
||||
inline size_t count_if_with_reduce(InputIterator first,
|
||||
InputIterator last,
|
||||
Predicate predicate,
|
||||
command_queue &queue)
|
||||
{
|
||||
countable_predicate<Predicate> reduce_predicate(predicate);
|
||||
|
||||
ulong_ count = 0;
|
||||
::boost::compute::reduce(
|
||||
::boost::compute::make_transform_iterator(first, reduce_predicate),
|
||||
::boost::compute::make_transform_iterator(last, reduce_predicate),
|
||||
&count,
|
||||
::boost::compute::plus<ulong_>(),
|
||||
queue
|
||||
);
|
||||
|
||||
return static_cast<size_t>(count);
|
||||
}
|
||||
|
||||
} // end detail namespace
|
||||
} // end compute namespace
|
||||
} // end boost namespace
|
||||
|
||||
#endif // BOOST_COMPUTE_ALGORITHM_DETAIL_COUNT_IF_WITH_REDUCE_HPP
|
||||
+129
@@ -0,0 +1,129 @@
|
||||
//---------------------------------------------------------------------------//
|
||||
// Copyright (c) 2013 Kyle Lutz <kyle.r.lutz@gmail.com>
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0
|
||||
// See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt
|
||||
//
|
||||
// See http://boostorg.github.com/compute for more information.
|
||||
//---------------------------------------------------------------------------//
|
||||
|
||||
#ifndef BOOST_COMPUTE_ALGORITHM_DETAIL_COUNT_IF_WITH_THREADS_HPP
|
||||
#define BOOST_COMPUTE_ALGORITHM_DETAIL_COUNT_IF_WITH_THREADS_HPP
|
||||
|
||||
#include <numeric>
|
||||
|
||||
#include <boost/compute/detail/meta_kernel.hpp>
|
||||
#include <boost/compute/container/vector.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace compute {
|
||||
namespace detail {
|
||||
|
||||
template<class InputIterator, class Predicate>
|
||||
class count_if_with_threads_kernel : meta_kernel
|
||||
{
|
||||
public:
|
||||
typedef typename
|
||||
std::iterator_traits<InputIterator>::value_type
|
||||
value_type;
|
||||
|
||||
count_if_with_threads_kernel()
|
||||
: meta_kernel("count_if_with_threads")
|
||||
{
|
||||
}
|
||||
|
||||
void set_args(InputIterator first,
|
||||
InputIterator last,
|
||||
Predicate predicate)
|
||||
|
||||
{
|
||||
typedef typename std::iterator_traits<InputIterator>::value_type T;
|
||||
|
||||
m_size = detail::iterator_range_size(first, last);
|
||||
|
||||
m_size_arg = add_arg<const ulong_>("size");
|
||||
m_counts_arg = add_arg<ulong_ *>(memory_object::global_memory, "counts");
|
||||
|
||||
*this <<
|
||||
// thread parameters
|
||||
"const uint gid = get_global_id(0);\n" <<
|
||||
"const uint block_size = size / get_global_size(0);\n" <<
|
||||
"const uint start = block_size * gid;\n" <<
|
||||
"uint end = 0;\n" <<
|
||||
"if(gid == get_global_size(0) - 1)\n" <<
|
||||
" end = size;\n" <<
|
||||
"else\n" <<
|
||||
" end = block_size * gid + block_size;\n" <<
|
||||
|
||||
// count values
|
||||
"uint count = 0;\n" <<
|
||||
"for(uint i = start; i < end; i++){\n" <<
|
||||
decl<const T>("value") << "="
|
||||
<< first[expr<uint_>("i")] << ";\n" <<
|
||||
if_(predicate(var<const T>("value"))) << "{\n" <<
|
||||
"count++;\n" <<
|
||||
"}\n" <<
|
||||
"}\n" <<
|
||||
|
||||
// write count
|
||||
"counts[gid] = count;\n";
|
||||
}
|
||||
|
||||
size_t exec(command_queue &queue)
|
||||
{
|
||||
const device &device = queue.get_device();
|
||||
const context &context = queue.get_context();
|
||||
|
||||
size_t threads = device.compute_units();
|
||||
|
||||
const size_t minimum_block_size = 2048;
|
||||
if(m_size / threads < minimum_block_size){
|
||||
threads = static_cast<size_t>(
|
||||
(std::max)(
|
||||
std::ceil(float(m_size) / minimum_block_size),
|
||||
1.0f
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// storage for counts
|
||||
::boost::compute::vector<ulong_> counts(threads, context);
|
||||
|
||||
// exec kernel
|
||||
set_arg(m_size_arg, static_cast<ulong_>(m_size));
|
||||
set_arg(m_counts_arg, counts.get_buffer());
|
||||
exec_1d(queue, 0, threads, 1);
|
||||
|
||||
// copy counts to the host
|
||||
std::vector<ulong_> host_counts(threads);
|
||||
::boost::compute::copy(counts.begin(), counts.end(), host_counts.begin(), queue);
|
||||
|
||||
// return sum of counts
|
||||
return std::accumulate(host_counts.begin(), host_counts.end(), size_t(0));
|
||||
}
|
||||
|
||||
private:
|
||||
size_t m_size;
|
||||
size_t m_size_arg;
|
||||
size_t m_counts_arg;
|
||||
};
|
||||
|
||||
// counts values that match the predicate using one thread per block. this is
|
||||
// optimized for cpu-type devices with a small number of compute units.
|
||||
template<class InputIterator, class Predicate>
|
||||
inline size_t count_if_with_threads(InputIterator first,
|
||||
InputIterator last,
|
||||
Predicate predicate,
|
||||
command_queue &queue)
|
||||
{
|
||||
count_if_with_threads_kernel<InputIterator, Predicate> kernel;
|
||||
kernel.set_args(first, last, predicate);
|
||||
return kernel.exec(queue);
|
||||
}
|
||||
|
||||
} // end detail namespace
|
||||
} // end compute namespace
|
||||
} // end boost namespace
|
||||
|
||||
#endif // BOOST_COMPUTE_ALGORITHM_DETAIL_COUNT_IF_WITH_THREADS_HPP
|
||||
@@ -0,0 +1,70 @@
|
||||
//---------------------------------------------------------------------------//
|
||||
// Copyright (c) 2013 Kyle Lutz <kyle.r.lutz@gmail.com>
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0
|
||||
// See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt
|
||||
//
|
||||
// See http://boostorg.github.com/compute for more information.
|
||||
//---------------------------------------------------------------------------//
|
||||
|
||||
#ifndef BOOST_COMPUTE_ALGORITHM_DETAIL_FIND_EXTREMA_HPP
|
||||
#define BOOST_COMPUTE_ALGORITHM_DETAIL_FIND_EXTREMA_HPP
|
||||
|
||||
#include <boost/compute/detail/iterator_range_size.hpp>
|
||||
#include <boost/compute/algorithm/detail/find_extrema_on_cpu.hpp>
|
||||
#include <boost/compute/algorithm/detail/find_extrema_with_reduce.hpp>
|
||||
#include <boost/compute/algorithm/detail/find_extrema_with_atomics.hpp>
|
||||
#include <boost/compute/algorithm/detail/serial_find_extrema.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace compute {
|
||||
namespace detail {
|
||||
|
||||
template<class InputIterator, class Compare>
|
||||
inline InputIterator find_extrema(InputIterator first,
|
||||
InputIterator last,
|
||||
Compare compare,
|
||||
const bool find_minimum,
|
||||
command_queue &queue)
|
||||
{
|
||||
size_t count = iterator_range_size(first, last);
|
||||
|
||||
// handle trivial cases
|
||||
if(count == 0 || count == 1){
|
||||
return first;
|
||||
}
|
||||
|
||||
const device &device = queue.get_device();
|
||||
|
||||
// CPU
|
||||
if(device.type() & device::cpu) {
|
||||
return find_extrema_on_cpu(first, last, compare, find_minimum, queue);
|
||||
}
|
||||
|
||||
// GPU
|
||||
// use serial method for small inputs
|
||||
if(count < 512)
|
||||
{
|
||||
return serial_find_extrema(first, last, compare, find_minimum, queue);
|
||||
}
|
||||
// find_extrema_with_reduce() is used only if requirements are met
|
||||
if(find_extrema_with_reduce_requirements_met(first, last, queue))
|
||||
{
|
||||
return find_extrema_with_reduce(first, last, compare, find_minimum, queue);
|
||||
}
|
||||
|
||||
// use serial method for OpenCL version 1.0 due to
|
||||
// problems with atomic_cmpxchg()
|
||||
#ifndef CL_VERSION_1_1
|
||||
return serial_find_extrema(first, last, compare, find_minimum, queue);
|
||||
#endif
|
||||
|
||||
return find_extrema_with_atomics(first, last, compare, find_minimum, queue);
|
||||
}
|
||||
|
||||
} // end detail namespace
|
||||
} // end compute namespace
|
||||
} // end boost namespace
|
||||
|
||||
#endif // BOOST_COMPUTE_ALGORITHM_DETAIL_FIND_EXTREMA_HPP
|
||||
+138
@@ -0,0 +1,138 @@
|
||||
//---------------------------------------------------------------------------//
|
||||
// Copyright (c) 2016 Jakub Szuppe <j.szuppe@gmail.com>
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0
|
||||
// See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt
|
||||
//
|
||||
// See http://boostorg.github.com/compute for more information.
|
||||
//---------------------------------------------------------------------------//
|
||||
|
||||
#ifndef BOOST_COMPUTE_ALGORITHM_DETAIL_FIND_EXTREMA_ON_CPU_HPP
|
||||
#define BOOST_COMPUTE_ALGORITHM_DETAIL_FIND_EXTREMA_ON_CPU_HPP
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include <boost/compute/algorithm/detail/find_extrema_with_reduce.hpp>
|
||||
#include <boost/compute/algorithm/detail/find_extrema_with_atomics.hpp>
|
||||
#include <boost/compute/algorithm/detail/serial_find_extrema.hpp>
|
||||
#include <boost/compute/detail/iterator_range_size.hpp>
|
||||
#include <boost/compute/iterator/buffer_iterator.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace compute {
|
||||
namespace detail {
|
||||
|
||||
template<class InputIterator, class Compare>
|
||||
inline InputIterator find_extrema_on_cpu(InputIterator first,
|
||||
InputIterator last,
|
||||
Compare compare,
|
||||
const bool find_minimum,
|
||||
command_queue &queue)
|
||||
{
|
||||
typedef typename std::iterator_traits<InputIterator>::value_type input_type;
|
||||
typedef typename std::iterator_traits<InputIterator>::difference_type difference_type;
|
||||
size_t count = iterator_range_size(first, last);
|
||||
|
||||
const device &device = queue.get_device();
|
||||
const uint_ compute_units = queue.get_device().compute_units();
|
||||
|
||||
boost::shared_ptr<parameter_cache> parameters =
|
||||
detail::parameter_cache::get_global_cache(device);
|
||||
std::string cache_key =
|
||||
"__boost_find_extrema_cpu_"
|
||||
+ boost::lexical_cast<std::string>(sizeof(input_type));
|
||||
|
||||
// for inputs smaller than serial_find_extrema_threshold
|
||||
// serial_find_extrema algorithm is used
|
||||
uint_ serial_find_extrema_threshold = parameters->get(
|
||||
cache_key,
|
||||
"serial_find_extrema_threshold",
|
||||
16384 * sizeof(input_type)
|
||||
);
|
||||
serial_find_extrema_threshold =
|
||||
(std::max)(serial_find_extrema_threshold, uint_(2 * compute_units));
|
||||
|
||||
const context &context = queue.get_context();
|
||||
if(count < serial_find_extrema_threshold) {
|
||||
return serial_find_extrema(first, last, compare, find_minimum, queue);
|
||||
}
|
||||
|
||||
meta_kernel k("find_extrema_on_cpu");
|
||||
buffer output(context, sizeof(input_type) * compute_units);
|
||||
buffer output_idx(
|
||||
context, sizeof(uint_) * compute_units,
|
||||
buffer::read_write | buffer::alloc_host_ptr
|
||||
);
|
||||
|
||||
size_t count_arg = k.add_arg<uint_>("count");
|
||||
size_t output_arg =
|
||||
k.add_arg<input_type *>(memory_object::global_memory, "output");
|
||||
size_t output_idx_arg =
|
||||
k.add_arg<uint_ *>(memory_object::global_memory, "output_idx");
|
||||
|
||||
k <<
|
||||
"uint block = " <<
|
||||
"(uint)ceil(((float)count)/get_global_size(0));\n" <<
|
||||
"uint index = get_global_id(0) * block;\n" <<
|
||||
"uint end = min(count, index + block);\n" <<
|
||||
|
||||
"uint value_index = index;\n" <<
|
||||
k.decl<input_type>("value") << " = " << first[k.var<uint_>("index")] << ";\n" <<
|
||||
|
||||
"index++;\n" <<
|
||||
"while(index < end){\n" <<
|
||||
k.decl<input_type>("candidate") <<
|
||||
" = " << first[k.var<uint_>("index")] << ";\n" <<
|
||||
"#ifndef BOOST_COMPUTE_FIND_MAXIMUM\n" <<
|
||||
"bool compare = " << compare(k.var<input_type>("candidate"),
|
||||
k.var<input_type>("value")) << ";\n" <<
|
||||
"#else\n" <<
|
||||
"bool compare = " << compare(k.var<input_type>("value"),
|
||||
k.var<input_type>("candidate")) << ";\n" <<
|
||||
"#endif\n" <<
|
||||
"value = compare ? candidate : value;\n" <<
|
||||
"value_index = compare ? index : value_index;\n" <<
|
||||
"index++;\n" <<
|
||||
"}\n" <<
|
||||
"output[get_global_id(0)] = value;\n" <<
|
||||
"output_idx[get_global_id(0)] = value_index;\n";
|
||||
|
||||
size_t global_work_size = compute_units;
|
||||
std::string options;
|
||||
if(!find_minimum){
|
||||
options = "-DBOOST_COMPUTE_FIND_MAXIMUM";
|
||||
}
|
||||
kernel kernel = k.compile(context, options);
|
||||
|
||||
kernel.set_arg(count_arg, static_cast<uint_>(count));
|
||||
kernel.set_arg(output_arg, output);
|
||||
kernel.set_arg(output_idx_arg, output_idx);
|
||||
queue.enqueue_1d_range_kernel(kernel, 0, global_work_size, 0);
|
||||
|
||||
buffer_iterator<input_type> result = serial_find_extrema(
|
||||
make_buffer_iterator<input_type>(output),
|
||||
make_buffer_iterator<input_type>(output, global_work_size),
|
||||
compare,
|
||||
find_minimum,
|
||||
queue
|
||||
);
|
||||
|
||||
uint_* output_idx_host_ptr =
|
||||
static_cast<uint_*>(
|
||||
queue.enqueue_map_buffer(
|
||||
output_idx, command_queue::map_read,
|
||||
0, global_work_size * sizeof(uint_)
|
||||
)
|
||||
);
|
||||
|
||||
difference_type extremum_idx =
|
||||
static_cast<difference_type>(*(output_idx_host_ptr + result.get_index()));
|
||||
return first + extremum_idx;
|
||||
}
|
||||
|
||||
} // end detail namespace
|
||||
} // end compute namespace
|
||||
} // end boost namespace
|
||||
|
||||
#endif // BOOST_COMPUTE_ALGORITHM_DETAIL_FIND_EXTREMA_ON_CPU_HPP
|
||||
+108
@@ -0,0 +1,108 @@
|
||||
//---------------------------------------------------------------------------//
|
||||
// Copyright (c) 2013 Kyle Lutz <kyle.r.lutz@gmail.com>
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0
|
||||
// See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt
|
||||
//
|
||||
// See http://boostorg.github.com/compute for more information.
|
||||
//---------------------------------------------------------------------------//
|
||||
|
||||
#ifndef BOOST_COMPUTE_ALGORITHM_DETAIL_FIND_EXTREMA_WITH_ATOMICS_HPP
|
||||
#define BOOST_COMPUTE_ALGORITHM_DETAIL_FIND_EXTREMA_WITH_ATOMICS_HPP
|
||||
|
||||
#include <boost/compute/types.hpp>
|
||||
#include <boost/compute/command_queue.hpp>
|
||||
#include <boost/compute/container/detail/scalar.hpp>
|
||||
#include <boost/compute/functional/atomic.hpp>
|
||||
#include <boost/compute/detail/meta_kernel.hpp>
|
||||
#include <boost/compute/detail/iterator_range_size.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace compute {
|
||||
namespace detail {
|
||||
|
||||
template<class InputIterator, class Compare>
|
||||
inline InputIterator find_extrema_with_atomics(InputIterator first,
|
||||
InputIterator last,
|
||||
Compare compare,
|
||||
const bool find_minimum,
|
||||
command_queue &queue)
|
||||
{
|
||||
typedef typename std::iterator_traits<InputIterator>::value_type value_type;
|
||||
typedef typename std::iterator_traits<InputIterator>::difference_type difference_type;
|
||||
|
||||
const context &context = queue.get_context();
|
||||
|
||||
meta_kernel k("find_extrema");
|
||||
atomic_cmpxchg<uint_> atomic_cmpxchg_uint;
|
||||
|
||||
k <<
|
||||
"const uint gid = get_global_id(0);\n" <<
|
||||
"uint old_index = *index;\n" <<
|
||||
|
||||
k.decl<value_type>("old") <<
|
||||
" = " << first[k.var<uint_>("old_index")] << ";\n" <<
|
||||
k.decl<value_type>("new") <<
|
||||
" = " << first[k.var<uint_>("gid")] << ";\n" <<
|
||||
|
||||
k.decl<bool>("compare_result") << ";\n" <<
|
||||
"#ifdef BOOST_COMPUTE_FIND_MAXIMUM\n" <<
|
||||
"while(" <<
|
||||
"(compare_result = " << compare(k.var<value_type>("old"),
|
||||
k.var<value_type>("new")) << ")" <<
|
||||
" || (!(compare_result" <<
|
||||
" || " << compare(k.var<value_type>("new"),
|
||||
k.var<value_type>("old")) << ") "
|
||||
"&& gid < old_index)){\n" <<
|
||||
"#else\n" <<
|
||||
// while condition explained for minimum case with less (<)
|
||||
// as comparison function:
|
||||
// while(new_value < old_value
|
||||
// OR (new_value == old_value AND new_index < old_index))
|
||||
"while(" <<
|
||||
"(compare_result = " << compare(k.var<value_type>("new"),
|
||||
k.var<value_type>("old")) << ")" <<
|
||||
" || (!(compare_result" <<
|
||||
" || " << compare(k.var<value_type>("old"),
|
||||
k.var<value_type>("new")) << ") "
|
||||
"&& gid < old_index)){\n" <<
|
||||
"#endif\n" <<
|
||||
|
||||
" if(" << atomic_cmpxchg_uint(k.var<uint_ *>("index"),
|
||||
k.var<uint_>("old_index"),
|
||||
k.var<uint_>("gid")) << " == old_index)\n" <<
|
||||
" break;\n" <<
|
||||
" else\n" <<
|
||||
" old_index = *index;\n" <<
|
||||
"old = " << first[k.var<uint_>("old_index")] << ";\n" <<
|
||||
"}\n";
|
||||
|
||||
size_t index_arg_index = k.add_arg<uint_ *>(memory_object::global_memory, "index");
|
||||
|
||||
std::string options;
|
||||
if(!find_minimum){
|
||||
options = "-DBOOST_COMPUTE_FIND_MAXIMUM";
|
||||
}
|
||||
kernel kernel = k.compile(context, options);
|
||||
|
||||
// setup index buffer
|
||||
scalar<uint_> index(context);
|
||||
kernel.set_arg(index_arg_index, index.get_buffer());
|
||||
|
||||
// initialize index
|
||||
index.write(0, queue);
|
||||
|
||||
// run kernel
|
||||
size_t count = iterator_range_size(first, last);
|
||||
queue.enqueue_1d_range_kernel(kernel, 0, count, 0);
|
||||
|
||||
// read index and return iterator
|
||||
return first + static_cast<difference_type>(index.read(queue));
|
||||
}
|
||||
|
||||
} // end detail namespace
|
||||
} // end compute namespace
|
||||
} // end boost namespace
|
||||
|
||||
#endif // BOOST_COMPUTE_ALGORITHM_DETAIL_FIND_EXTREMA_WITH_ATOMICS_HPP
|
||||
+443
@@ -0,0 +1,443 @@
|
||||
//---------------------------------------------------------------------------//
|
||||
// Copyright (c) 2015 Jakub Szuppe <j.szuppe@gmail.com>
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0
|
||||
// See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt
|
||||
//
|
||||
// See http://boostorg.github.com/compute for more information.
|
||||
//---------------------------------------------------------------------------//
|
||||
|
||||
#ifndef BOOST_COMPUTE_ALGORITHM_DETAIL_FIND_EXTREMA_WITH_REDUCE_HPP
|
||||
#define BOOST_COMPUTE_ALGORITHM_DETAIL_FIND_EXTREMA_WITH_REDUCE_HPP
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include <boost/compute/types.hpp>
|
||||
#include <boost/compute/command_queue.hpp>
|
||||
#include <boost/compute/algorithm/copy.hpp>
|
||||
#include <boost/compute/allocator/pinned_allocator.hpp>
|
||||
#include <boost/compute/container/vector.hpp>
|
||||
#include <boost/compute/detail/meta_kernel.hpp>
|
||||
#include <boost/compute/detail/iterator_range_size.hpp>
|
||||
#include <boost/compute/detail/parameter_cache.hpp>
|
||||
#include <boost/compute/memory/local_buffer.hpp>
|
||||
#include <boost/compute/type_traits/type_name.hpp>
|
||||
#include <boost/compute/utility/program_cache.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace compute {
|
||||
namespace detail {
|
||||
|
||||
template<class InputIterator>
|
||||
bool find_extrema_with_reduce_requirements_met(InputIterator first,
|
||||
InputIterator last,
|
||||
command_queue &queue)
|
||||
{
|
||||
typedef typename std::iterator_traits<InputIterator>::value_type input_type;
|
||||
|
||||
const device &device = queue.get_device();
|
||||
|
||||
// device must have dedicated local memory storage
|
||||
// otherwise reduction would be highly inefficient
|
||||
if(device.get_info<CL_DEVICE_LOCAL_MEM_TYPE>() != CL_LOCAL)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const size_t max_work_group_size = device.get_info<CL_DEVICE_MAX_WORK_GROUP_SIZE>();
|
||||
// local memory size in bytes (per compute unit)
|
||||
const size_t local_mem_size = device.get_info<CL_DEVICE_LOCAL_MEM_SIZE>();
|
||||
|
||||
std::string cache_key = std::string("__boost_find_extrema_reduce_")
|
||||
+ type_name<input_type>();
|
||||
// load parameters
|
||||
boost::shared_ptr<parameter_cache> parameters =
|
||||
detail::parameter_cache::get_global_cache(device);
|
||||
|
||||
// Get preferred work group size
|
||||
size_t work_group_size = parameters->get(cache_key, "wgsize", 256);
|
||||
|
||||
work_group_size = (std::min)(max_work_group_size, work_group_size);
|
||||
|
||||
// local memory size needed to perform parallel reduction
|
||||
size_t required_local_mem_size = 0;
|
||||
// indices size
|
||||
required_local_mem_size += sizeof(uint_) * work_group_size;
|
||||
// values size
|
||||
required_local_mem_size += sizeof(input_type) * work_group_size;
|
||||
|
||||
// at least 4 work groups per compute unit otherwise reduction
|
||||
// would be highly inefficient
|
||||
return ((required_local_mem_size * 4) <= local_mem_size);
|
||||
}
|
||||
|
||||
/// \internal_
|
||||
/// Algorithm finds the first extremum in given range, i.e., with the lowest
|
||||
/// index.
|
||||
///
|
||||
/// If \p use_input_idx is false, it's assumed that input data is ordered by
|
||||
/// increasing index and \p input_idx is not used in the algorithm.
|
||||
template<class InputIterator, class ResultIterator, class Compare>
|
||||
inline void find_extrema_with_reduce(InputIterator input,
|
||||
vector<uint_>::iterator input_idx,
|
||||
size_t count,
|
||||
ResultIterator result,
|
||||
vector<uint_>::iterator result_idx,
|
||||
size_t work_groups_no,
|
||||
size_t work_group_size,
|
||||
Compare compare,
|
||||
const bool find_minimum,
|
||||
const bool use_input_idx,
|
||||
command_queue &queue)
|
||||
{
|
||||
typedef typename std::iterator_traits<InputIterator>::value_type input_type;
|
||||
|
||||
const context &context = queue.get_context();
|
||||
|
||||
meta_kernel k("find_extrema_reduce");
|
||||
size_t count_arg = k.add_arg<uint_>("count");
|
||||
size_t block_arg = k.add_arg<input_type *>(memory_object::local_memory, "block");
|
||||
size_t block_idx_arg = k.add_arg<uint_ *>(memory_object::local_memory, "block_idx");
|
||||
|
||||
k <<
|
||||
// Work item global id
|
||||
k.decl<const uint_>("gid") << " = get_global_id(0);\n" <<
|
||||
|
||||
// Index of element that will be read from input buffer
|
||||
k.decl<uint_>("idx") << " = gid;\n" <<
|
||||
|
||||
k.decl<input_type>("acc") << ";\n" <<
|
||||
k.decl<uint_>("acc_idx") << ";\n" <<
|
||||
"if(gid < count) {\n" <<
|
||||
// Real index of currently best element
|
||||
"#ifdef BOOST_COMPUTE_USE_INPUT_IDX\n" <<
|
||||
k.var<uint_>("acc_idx") << " = " << input_idx[k.var<uint_>("idx")] << ";\n" <<
|
||||
"#else\n" <<
|
||||
k.var<uint_>("acc_idx") << " = idx;\n" <<
|
||||
"#endif\n" <<
|
||||
|
||||
// Init accumulator with first[get_global_id(0)]
|
||||
"acc = " << input[k.var<uint_>("idx")] << ";\n" <<
|
||||
"idx += get_global_size(0);\n" <<
|
||||
"}\n" <<
|
||||
|
||||
k.decl<bool>("compare_result") << ";\n" <<
|
||||
k.decl<bool>("equal") << ";\n\n" <<
|
||||
"while( idx < count ){\n" <<
|
||||
// Next element
|
||||
k.decl<input_type>("next") << " = " << input[k.var<uint_>("idx")] << ";\n" <<
|
||||
"#ifdef BOOST_COMPUTE_USE_INPUT_IDX\n" <<
|
||||
k.decl<uint_>("next_idx") << " = " << input_idx[k.var<uint_>("idx")] << ";\n" <<
|
||||
"#endif\n" <<
|
||||
|
||||
// Comparison between currently best element (acc) and next element
|
||||
"#ifdef BOOST_COMPUTE_FIND_MAXIMUM\n" <<
|
||||
"compare_result = " << compare(k.var<input_type>("next"),
|
||||
k.var<input_type>("acc")) << ";\n" <<
|
||||
"# ifdef BOOST_COMPUTE_USE_INPUT_IDX\n" <<
|
||||
"equal = !compare_result && !" <<
|
||||
compare(k.var<input_type>("acc"),
|
||||
k.var<input_type>("next")) << ";\n" <<
|
||||
"# endif\n" <<
|
||||
"#else\n" <<
|
||||
"compare_result = " << compare(k.var<input_type>("acc"),
|
||||
k.var<input_type>("next")) << ";\n" <<
|
||||
"# ifdef BOOST_COMPUTE_USE_INPUT_IDX\n" <<
|
||||
"equal = !compare_result && !" <<
|
||||
compare(k.var<input_type>("next"),
|
||||
k.var<input_type>("acc")) << ";\n" <<
|
||||
"# endif\n" <<
|
||||
"#endif\n" <<
|
||||
|
||||
// save the winner
|
||||
"acc = compare_result ? acc : next;\n" <<
|
||||
"#ifdef BOOST_COMPUTE_USE_INPUT_IDX\n" <<
|
||||
"acc_idx = compare_result ? " <<
|
||||
"acc_idx : " <<
|
||||
"(equal ? min(acc_idx, next_idx) : next_idx);\n" <<
|
||||
"#else\n" <<
|
||||
"acc_idx = compare_result ? acc_idx : idx;\n" <<
|
||||
"#endif\n" <<
|
||||
"idx += get_global_size(0);\n" <<
|
||||
"}\n\n" <<
|
||||
|
||||
// Work item local id
|
||||
k.decl<const uint_>("lid") << " = get_local_id(0);\n" <<
|
||||
"block[lid] = acc;\n" <<
|
||||
"block_idx[lid] = acc_idx;\n" <<
|
||||
"barrier(CLK_LOCAL_MEM_FENCE);\n" <<
|
||||
|
||||
k.decl<uint_>("group_offset") <<
|
||||
" = count - (get_local_size(0) * get_group_id(0));\n\n";
|
||||
|
||||
k <<
|
||||
"#pragma unroll\n"
|
||||
"for(" << k.decl<uint_>("offset") << " = " << uint_(work_group_size) << " / 2; offset > 0; " <<
|
||||
"offset = offset / 2) {\n" <<
|
||||
"if((lid < offset) && ((lid + offset) < group_offset)) { \n" <<
|
||||
k.decl<input_type>("mine") << " = block[lid];\n" <<
|
||||
k.decl<input_type>("other") << " = block[lid+offset];\n" <<
|
||||
"#ifdef BOOST_COMPUTE_FIND_MAXIMUM\n" <<
|
||||
"compare_result = " << compare(k.var<input_type>("other"),
|
||||
k.var<input_type>("mine")) << ";\n" <<
|
||||
"equal = !compare_result && !" <<
|
||||
compare(k.var<input_type>("mine"),
|
||||
k.var<input_type>("other")) << ";\n" <<
|
||||
"#else\n" <<
|
||||
"compare_result = " << compare(k.var<input_type>("mine"),
|
||||
k.var<input_type>("other")) << ";\n" <<
|
||||
"equal = !compare_result && !" <<
|
||||
compare(k.var<input_type>("other"),
|
||||
k.var<input_type>("mine")) << ";\n" <<
|
||||
"#endif\n" <<
|
||||
"block[lid] = compare_result ? mine : other;\n" <<
|
||||
k.decl<uint_>("mine_idx") << " = block_idx[lid];\n" <<
|
||||
k.decl<uint_>("other_idx") << " = block_idx[lid+offset];\n" <<
|
||||
"block_idx[lid] = compare_result ? " <<
|
||||
"mine_idx : " <<
|
||||
"(equal ? min(mine_idx, other_idx) : other_idx);\n" <<
|
||||
"}\n"
|
||||
"barrier(CLK_LOCAL_MEM_FENCE);\n" <<
|
||||
"}\n\n" <<
|
||||
|
||||
// write block result to global output
|
||||
"if(lid == 0){\n" <<
|
||||
result[k.var<uint_>("get_group_id(0)")] << " = block[0];\n" <<
|
||||
result_idx[k.var<uint_>("get_group_id(0)")] << " = block_idx[0];\n" <<
|
||||
"}";
|
||||
|
||||
std::string options;
|
||||
if(!find_minimum){
|
||||
options = "-DBOOST_COMPUTE_FIND_MAXIMUM";
|
||||
}
|
||||
if(use_input_idx){
|
||||
options += " -DBOOST_COMPUTE_USE_INPUT_IDX";
|
||||
}
|
||||
|
||||
kernel kernel = k.compile(context, options);
|
||||
|
||||
kernel.set_arg(count_arg, static_cast<uint_>(count));
|
||||
kernel.set_arg(block_arg, local_buffer<input_type>(work_group_size));
|
||||
kernel.set_arg(block_idx_arg, local_buffer<uint_>(work_group_size));
|
||||
|
||||
queue.enqueue_1d_range_kernel(kernel,
|
||||
0,
|
||||
work_groups_no * work_group_size,
|
||||
work_group_size);
|
||||
}
|
||||
|
||||
template<class InputIterator, class ResultIterator, class Compare>
|
||||
inline void find_extrema_with_reduce(InputIterator input,
|
||||
size_t count,
|
||||
ResultIterator result,
|
||||
vector<uint_>::iterator result_idx,
|
||||
size_t work_groups_no,
|
||||
size_t work_group_size,
|
||||
Compare compare,
|
||||
const bool find_minimum,
|
||||
command_queue &queue)
|
||||
{
|
||||
// dummy will not be used
|
||||
buffer_iterator<uint_> dummy = result_idx;
|
||||
return find_extrema_with_reduce(
|
||||
input, dummy, count, result, result_idx, work_groups_no,
|
||||
work_group_size, compare, find_minimum, false, queue
|
||||
);
|
||||
}
|
||||
|
||||
template<class InputIterator, class Compare>
|
||||
InputIterator find_extrema_with_reduce(InputIterator first,
|
||||
InputIterator last,
|
||||
Compare compare,
|
||||
const bool find_minimum,
|
||||
command_queue &queue)
|
||||
{
|
||||
typedef typename std::iterator_traits<InputIterator>::difference_type difference_type;
|
||||
typedef typename std::iterator_traits<InputIterator>::value_type input_type;
|
||||
|
||||
const context &context = queue.get_context();
|
||||
const device &device = queue.get_device();
|
||||
|
||||
// Getting information about used queue and device
|
||||
const size_t compute_units_no = device.get_info<CL_DEVICE_MAX_COMPUTE_UNITS>();
|
||||
const size_t max_work_group_size = device.get_info<CL_DEVICE_MAX_WORK_GROUP_SIZE>();
|
||||
|
||||
const size_t count = detail::iterator_range_size(first, last);
|
||||
|
||||
std::string cache_key = std::string("__boost_find_extrema_with_reduce_")
|
||||
+ type_name<input_type>();
|
||||
|
||||
// load parameters
|
||||
boost::shared_ptr<parameter_cache> parameters =
|
||||
detail::parameter_cache::get_global_cache(device);
|
||||
|
||||
// get preferred work group size and preferred number
|
||||
// of work groups per compute unit
|
||||
size_t work_group_size = parameters->get(cache_key, "wgsize", 256);
|
||||
size_t work_groups_per_cu = parameters->get(cache_key, "wgpcu", 100);
|
||||
|
||||
// calculate work group size and number of work groups
|
||||
work_group_size = (std::min)(max_work_group_size, work_group_size);
|
||||
size_t work_groups_no = compute_units_no * work_groups_per_cu;
|
||||
work_groups_no = (std::min)(
|
||||
work_groups_no,
|
||||
static_cast<size_t>(std::ceil(float(count) / work_group_size))
|
||||
);
|
||||
|
||||
// phase I: finding candidates for extremum
|
||||
|
||||
// device buffors for extremum candidates and their indices
|
||||
// each work-group computes its candidate
|
||||
vector<input_type> candidates(work_groups_no, context);
|
||||
vector<uint_> candidates_idx(work_groups_no, context);
|
||||
|
||||
// finding candidates for first extremum and their indices
|
||||
find_extrema_with_reduce(
|
||||
first, count, candidates.begin(), candidates_idx.begin(),
|
||||
work_groups_no, work_group_size, compare, find_minimum, queue
|
||||
);
|
||||
|
||||
// phase II: finding extremum from among the candidates
|
||||
|
||||
// zero-copy buffers for final result (value and index)
|
||||
vector<input_type, ::boost::compute::pinned_allocator<input_type> >
|
||||
result(1, context);
|
||||
vector<uint_, ::boost::compute::pinned_allocator<uint_> >
|
||||
result_idx(1, context);
|
||||
|
||||
// get extremum from among the candidates
|
||||
find_extrema_with_reduce(
|
||||
candidates.begin(), candidates_idx.begin(), work_groups_no, result.begin(),
|
||||
result_idx.begin(), 1, work_group_size, compare, find_minimum, true, queue
|
||||
);
|
||||
|
||||
// mapping extremum index to host
|
||||
uint_* result_idx_host_ptr =
|
||||
static_cast<uint_*>(
|
||||
queue.enqueue_map_buffer(
|
||||
result_idx.get_buffer(), command_queue::map_read,
|
||||
0, sizeof(uint_)
|
||||
)
|
||||
);
|
||||
|
||||
return first + static_cast<difference_type>(*result_idx_host_ptr);
|
||||
}
|
||||
|
||||
template<class InputIterator>
|
||||
InputIterator find_extrema_with_reduce(InputIterator first,
|
||||
InputIterator last,
|
||||
::boost::compute::less<
|
||||
typename std::iterator_traits<
|
||||
InputIterator
|
||||
>::value_type
|
||||
>
|
||||
compare,
|
||||
const bool find_minimum,
|
||||
command_queue &queue)
|
||||
{
|
||||
typedef typename std::iterator_traits<InputIterator>::difference_type difference_type;
|
||||
typedef typename std::iterator_traits<InputIterator>::value_type input_type;
|
||||
|
||||
const context &context = queue.get_context();
|
||||
const device &device = queue.get_device();
|
||||
|
||||
// Getting information about used queue and device
|
||||
const size_t compute_units_no = device.get_info<CL_DEVICE_MAX_COMPUTE_UNITS>();
|
||||
const size_t max_work_group_size = device.get_info<CL_DEVICE_MAX_WORK_GROUP_SIZE>();
|
||||
|
||||
const size_t count = detail::iterator_range_size(first, last);
|
||||
|
||||
std::string cache_key = std::string("__boost_find_extrema_with_reduce_")
|
||||
+ type_name<input_type>();
|
||||
|
||||
// load parameters
|
||||
boost::shared_ptr<parameter_cache> parameters =
|
||||
detail::parameter_cache::get_global_cache(device);
|
||||
|
||||
// get preferred work group size and preferred number
|
||||
// of work groups per compute unit
|
||||
size_t work_group_size = parameters->get(cache_key, "wgsize", 256);
|
||||
size_t work_groups_per_cu = parameters->get(cache_key, "wgpcu", 64);
|
||||
|
||||
// calculate work group size and number of work groups
|
||||
work_group_size = (std::min)(max_work_group_size, work_group_size);
|
||||
size_t work_groups_no = compute_units_no * work_groups_per_cu;
|
||||
work_groups_no = (std::min)(
|
||||
work_groups_no,
|
||||
static_cast<size_t>(std::ceil(float(count) / work_group_size))
|
||||
);
|
||||
|
||||
// phase I: finding candidates for extremum
|
||||
|
||||
// device buffors for extremum candidates and their indices
|
||||
// each work-group computes its candidate
|
||||
// zero-copy buffers are used to eliminate copying data back to host
|
||||
vector<input_type, ::boost::compute::pinned_allocator<input_type> >
|
||||
candidates(work_groups_no, context);
|
||||
vector<uint_, ::boost::compute::pinned_allocator <uint_> >
|
||||
candidates_idx(work_groups_no, context);
|
||||
|
||||
// finding candidates for first extremum and their indices
|
||||
find_extrema_with_reduce(
|
||||
first, count, candidates.begin(), candidates_idx.begin(),
|
||||
work_groups_no, work_group_size, compare, find_minimum, queue
|
||||
);
|
||||
|
||||
// phase II: finding extremum from among the candidates
|
||||
|
||||
// mapping candidates and their indices to host
|
||||
input_type* candidates_host_ptr =
|
||||
static_cast<input_type*>(
|
||||
queue.enqueue_map_buffer(
|
||||
candidates.get_buffer(), command_queue::map_read,
|
||||
0, work_groups_no * sizeof(input_type)
|
||||
)
|
||||
);
|
||||
|
||||
uint_* candidates_idx_host_ptr =
|
||||
static_cast<uint_*>(
|
||||
queue.enqueue_map_buffer(
|
||||
candidates_idx.get_buffer(), command_queue::map_read,
|
||||
0, work_groups_no * sizeof(uint_)
|
||||
)
|
||||
);
|
||||
|
||||
input_type* i = candidates_host_ptr;
|
||||
uint_* idx = candidates_idx_host_ptr;
|
||||
uint_* extremum_idx = idx;
|
||||
input_type extremum = *candidates_host_ptr;
|
||||
i++; idx++;
|
||||
|
||||
// find extremum (serial) from among the candidates on host
|
||||
if(!find_minimum) {
|
||||
while(idx != (candidates_idx_host_ptr + work_groups_no)) {
|
||||
input_type next = *i;
|
||||
bool compare_result = next > extremum;
|
||||
bool equal = next == extremum;
|
||||
extremum = compare_result ? next : extremum;
|
||||
extremum_idx = compare_result ? idx : extremum_idx;
|
||||
extremum_idx = equal ? ((*extremum_idx < *idx) ? extremum_idx : idx) : extremum_idx;
|
||||
idx++, i++;
|
||||
}
|
||||
}
|
||||
else {
|
||||
while(idx != (candidates_idx_host_ptr + work_groups_no)) {
|
||||
input_type next = *i;
|
||||
bool compare_result = next < extremum;
|
||||
bool equal = next == extremum;
|
||||
extremum = compare_result ? next : extremum;
|
||||
extremum_idx = compare_result ? idx : extremum_idx;
|
||||
extremum_idx = equal ? ((*extremum_idx < *idx) ? extremum_idx : idx) : extremum_idx;
|
||||
idx++, i++;
|
||||
}
|
||||
}
|
||||
|
||||
return first + static_cast<difference_type>(*extremum_idx);
|
||||
}
|
||||
|
||||
} // end detail namespace
|
||||
} // end compute namespace
|
||||
} // end boost namespace
|
||||
|
||||
#endif // BOOST_COMPUTE_ALGORITHM_DETAIL_FIND_EXTREMA_WITH_REDUCE_HPP
|
||||
+212
@@ -0,0 +1,212 @@
|
||||
//---------------------------------------------------------------------------//
|
||||
// Copyright (c) 2013 Kyle Lutz <kyle.r.lutz@gmail.com>
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0
|
||||
// See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt
|
||||
//
|
||||
// See http://boostorg.github.com/compute for more information.
|
||||
//---------------------------------------------------------------------------//
|
||||
|
||||
#ifndef BOOST_COMPUTE_ALGORITHM_DETAIL_FIND_IF_WITH_ATOMICS_HPP
|
||||
#define BOOST_COMPUTE_ALGORITHM_DETAIL_FIND_IF_WITH_ATOMICS_HPP
|
||||
|
||||
#include <iterator>
|
||||
|
||||
#include <boost/compute/types.hpp>
|
||||
#include <boost/compute/functional.hpp>
|
||||
#include <boost/compute/command_queue.hpp>
|
||||
#include <boost/compute/container/detail/scalar.hpp>
|
||||
#include <boost/compute/iterator/buffer_iterator.hpp>
|
||||
#include <boost/compute/type_traits/type_name.hpp>
|
||||
#include <boost/compute/detail/meta_kernel.hpp>
|
||||
#include <boost/compute/detail/iterator_range_size.hpp>
|
||||
#include <boost/compute/detail/parameter_cache.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace compute {
|
||||
namespace detail {
|
||||
|
||||
template<class InputIterator, class UnaryPredicate>
|
||||
inline InputIterator find_if_with_atomics_one_vpt(InputIterator first,
|
||||
InputIterator last,
|
||||
UnaryPredicate predicate,
|
||||
const size_t count,
|
||||
command_queue &queue)
|
||||
{
|
||||
typedef typename std::iterator_traits<InputIterator>::value_type value_type;
|
||||
typedef typename std::iterator_traits<InputIterator>::difference_type difference_type;
|
||||
|
||||
const context &context = queue.get_context();
|
||||
|
||||
detail::meta_kernel k("find_if");
|
||||
size_t index_arg = k.add_arg<int *>(memory_object::global_memory, "index");
|
||||
atomic_min<uint_> atomic_min_uint;
|
||||
|
||||
k << k.decl<const uint_>("i") << " = get_global_id(0);\n"
|
||||
<< k.decl<const value_type>("value") << "="
|
||||
<< first[k.var<const uint_>("i")] << ";\n"
|
||||
<< "if(" << predicate(k.var<const value_type>("value")) << "){\n"
|
||||
<< " " << atomic_min_uint(k.var<uint_ *>("index"), k.var<uint_>("i")) << ";\n"
|
||||
<< "}\n";
|
||||
|
||||
kernel kernel = k.compile(context);
|
||||
|
||||
scalar<uint_> index(context);
|
||||
kernel.set_arg(index_arg, index.get_buffer());
|
||||
|
||||
// initialize index to the last iterator's index
|
||||
index.write(static_cast<uint_>(count), queue);
|
||||
queue.enqueue_1d_range_kernel(kernel, 0, count, 0);
|
||||
|
||||
// read index and return iterator
|
||||
return first + static_cast<difference_type>(index.read(queue));
|
||||
}
|
||||
|
||||
template<class InputIterator, class UnaryPredicate>
|
||||
inline InputIterator find_if_with_atomics_multiple_vpt(InputIterator first,
|
||||
InputIterator last,
|
||||
UnaryPredicate predicate,
|
||||
const size_t count,
|
||||
const size_t vpt,
|
||||
command_queue &queue)
|
||||
{
|
||||
typedef typename std::iterator_traits<InputIterator>::value_type value_type;
|
||||
typedef typename std::iterator_traits<InputIterator>::difference_type difference_type;
|
||||
|
||||
const context &context = queue.get_context();
|
||||
const device &device = queue.get_device();
|
||||
|
||||
detail::meta_kernel k("find_if");
|
||||
size_t index_arg = k.add_arg<uint_ *>(memory_object::global_memory, "index");
|
||||
size_t count_arg = k.add_arg<const uint_>("count");
|
||||
size_t vpt_arg = k.add_arg<const uint_>("vpt");
|
||||
atomic_min<uint_> atomic_min_uint;
|
||||
|
||||
// for GPUs reads from global memory are coalesced
|
||||
if(device.type() & device::gpu) {
|
||||
k <<
|
||||
k.decl<const uint_>("lsize") << " = get_local_size(0);\n" <<
|
||||
k.decl<uint_>("id") << " = get_local_id(0) + get_group_id(0) * lsize * vpt;\n" <<
|
||||
k.decl<const uint_>("end") << " = min(" <<
|
||||
"id + (lsize *" << k.var<uint_>("vpt") << ")," <<
|
||||
"count" <<
|
||||
");\n" <<
|
||||
|
||||
// checking if the index is already found
|
||||
"__local uint local_index;\n" <<
|
||||
"if(get_local_id(0) == 0){\n" <<
|
||||
" local_index = *index;\n " <<
|
||||
"};\n" <<
|
||||
"barrier(CLK_LOCAL_MEM_FENCE);\n" <<
|
||||
"if(local_index < id){\n" <<
|
||||
" return;\n" <<
|
||||
"}\n" <<
|
||||
|
||||
"while(id < end){\n" <<
|
||||
" " << k.decl<const value_type>("value") << " = " <<
|
||||
first[k.var<const uint_>("id")] << ";\n"
|
||||
" if(" << predicate(k.var<const value_type>("value")) << "){\n" <<
|
||||
" " << atomic_min_uint(k.var<uint_ *>("index"),
|
||||
k.var<uint_>("id")) << ";\n" <<
|
||||
" return;\n"
|
||||
" }\n" <<
|
||||
" id+=lsize;\n" <<
|
||||
"}\n";
|
||||
// for CPUs (and other devices) reads are ordered so the big cache is
|
||||
// efficiently used.
|
||||
} else {
|
||||
k <<
|
||||
k.decl<uint_>("id") << " = get_global_id(0) * " << k.var<uint_>("vpt") << ";\n" <<
|
||||
k.decl<const uint_>("end") << " = min(" <<
|
||||
"id + " << k.var<uint_>("vpt") << "," <<
|
||||
"count" <<
|
||||
");\n" <<
|
||||
"while(id < end && (*index) > id){\n" <<
|
||||
" " << k.decl<const value_type>("value") << " = " <<
|
||||
first[k.var<const uint_>("id")] << ";\n"
|
||||
" if(" << predicate(k.var<const value_type>("value")) << "){\n" <<
|
||||
" " << atomic_min_uint(k.var<uint_ *>("index"),
|
||||
k.var<uint_>("id")) << ";\n" <<
|
||||
" return;\n" <<
|
||||
" }\n" <<
|
||||
" id++;\n" <<
|
||||
"}\n";
|
||||
}
|
||||
|
||||
kernel kernel = k.compile(context);
|
||||
|
||||
scalar<uint_> index(context);
|
||||
kernel.set_arg(index_arg, index.get_buffer());
|
||||
kernel.set_arg(count_arg, static_cast<uint_>(count));
|
||||
kernel.set_arg(vpt_arg, static_cast<uint_>(vpt));
|
||||
|
||||
// initialize index to the last iterator's index
|
||||
index.write(static_cast<uint_>(count), queue);
|
||||
|
||||
const size_t global_wg_size = static_cast<size_t>(
|
||||
std::ceil(float(count) / vpt)
|
||||
);
|
||||
queue.enqueue_1d_range_kernel(kernel, 0, global_wg_size, 0);
|
||||
|
||||
// read index and return iterator
|
||||
return first + static_cast<difference_type>(index.read(queue));
|
||||
}
|
||||
|
||||
template<class InputIterator, class UnaryPredicate>
|
||||
inline InputIterator find_if_with_atomics(InputIterator first,
|
||||
InputIterator last,
|
||||
UnaryPredicate predicate,
|
||||
command_queue &queue)
|
||||
{
|
||||
typedef typename std::iterator_traits<InputIterator>::value_type value_type;
|
||||
|
||||
size_t count = detail::iterator_range_size(first, last);
|
||||
if(count == 0){
|
||||
return last;
|
||||
}
|
||||
|
||||
const device &device = queue.get_device();
|
||||
|
||||
// load cached parameters
|
||||
std::string cache_key = std::string("__boost_find_if_with_atomics_")
|
||||
+ type_name<value_type>();
|
||||
boost::shared_ptr<parameter_cache> parameters =
|
||||
detail::parameter_cache::get_global_cache(device);
|
||||
|
||||
// for relatively small inputs on GPUs kernel checking one value per thread
|
||||
// (work-item) is more efficient than its multiple values per thread version
|
||||
if(device.type() & device::gpu){
|
||||
const size_t one_vpt_threshold =
|
||||
parameters->get(cache_key, "one_vpt_threshold", 1048576);
|
||||
if(count <= one_vpt_threshold){
|
||||
return find_if_with_atomics_one_vpt(
|
||||
first, last, predicate, count, queue
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// values per thread
|
||||
size_t vpt;
|
||||
if(device.type() & device::gpu){
|
||||
// get vpt parameter
|
||||
vpt = parameters->get(cache_key, "vpt", 32);
|
||||
} else {
|
||||
// for CPUs work is split equally between compute units
|
||||
const size_t max_compute_units =
|
||||
device.get_info<CL_DEVICE_MAX_COMPUTE_UNITS>();
|
||||
vpt = static_cast<size_t>(
|
||||
std::ceil(float(count) / max_compute_units)
|
||||
);
|
||||
}
|
||||
|
||||
return find_if_with_atomics_multiple_vpt(
|
||||
first, last, predicate, count, vpt, queue
|
||||
);
|
||||
}
|
||||
|
||||
} // end detail namespace
|
||||
} // end compute namespace
|
||||
} // end boost namespace
|
||||
|
||||
#endif // BOOST_COMPUTE_ALGORITHM_DETAIL_FIND_IF_WITH_ATOMICS_HPP
|
||||
+136
@@ -0,0 +1,136 @@
|
||||
//---------------------------------------------------------------------------//
|
||||
// Copyright (c) 2013 Kyle Lutz <kyle.r.lutz@gmail.com>
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0
|
||||
// See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt
|
||||
//
|
||||
// See http://boostorg.github.com/compute for more information.
|
||||
//---------------------------------------------------------------------------//
|
||||
|
||||
#ifndef BOOST_COMPUTE_ALGORITHM_DETAIL_INPLACE_REDUCE_HPP
|
||||
#define BOOST_COMPUTE_ALGORITHM_DETAIL_INPLACE_REDUCE_HPP
|
||||
|
||||
#include <iterator>
|
||||
|
||||
#include <boost/utility/result_of.hpp>
|
||||
|
||||
#include <boost/compute/command_queue.hpp>
|
||||
#include <boost/compute/container/vector.hpp>
|
||||
#include <boost/compute/detail/iterator_range_size.hpp>
|
||||
#include <boost/compute/memory/local_buffer.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace compute {
|
||||
namespace detail {
|
||||
|
||||
template<class Iterator, class BinaryFunction>
|
||||
inline void inplace_reduce(Iterator first,
|
||||
Iterator last,
|
||||
BinaryFunction function,
|
||||
command_queue &queue)
|
||||
{
|
||||
typedef typename
|
||||
std::iterator_traits<Iterator>::value_type
|
||||
value_type;
|
||||
|
||||
size_t input_size = iterator_range_size(first, last);
|
||||
if(input_size < 2){
|
||||
return;
|
||||
}
|
||||
|
||||
const context &context = queue.get_context();
|
||||
|
||||
size_t block_size = 64;
|
||||
size_t values_per_thread = 8;
|
||||
size_t block_count = input_size / (block_size * values_per_thread);
|
||||
if(block_count * block_size * values_per_thread != input_size)
|
||||
block_count++;
|
||||
|
||||
vector<value_type> output(block_count, context);
|
||||
|
||||
meta_kernel k("inplace_reduce");
|
||||
size_t input_arg = k.add_arg<value_type *>(memory_object::global_memory, "input");
|
||||
size_t input_size_arg = k.add_arg<const uint_>("input_size");
|
||||
size_t output_arg = k.add_arg<value_type *>(memory_object::global_memory, "output");
|
||||
size_t scratch_arg = k.add_arg<value_type *>(memory_object::local_memory, "scratch");
|
||||
k <<
|
||||
"const uint gid = get_global_id(0);\n" <<
|
||||
"const uint lid = get_local_id(0);\n" <<
|
||||
"const uint values_per_thread =\n"
|
||||
<< uint_(values_per_thread) << ";\n" <<
|
||||
|
||||
// thread reduce
|
||||
"const uint index = gid * values_per_thread;\n" <<
|
||||
"if(index < input_size){\n" <<
|
||||
k.decl<value_type>("sum") << " = input[index];\n" <<
|
||||
"for(uint i = 1;\n" <<
|
||||
"i < values_per_thread && (index + i) < input_size;\n" <<
|
||||
"i++){\n" <<
|
||||
" sum = " <<
|
||||
function(k.var<value_type>("sum"),
|
||||
k.var<value_type>("input[index+i]")) << ";\n" <<
|
||||
"}\n" <<
|
||||
"scratch[lid] = sum;\n" <<
|
||||
"}\n" <<
|
||||
|
||||
// local reduce
|
||||
"for(uint i = 1; i < get_local_size(0); i <<= 1){\n" <<
|
||||
" barrier(CLK_LOCAL_MEM_FENCE);\n" <<
|
||||
" uint mask = (i << 1) - 1;\n" <<
|
||||
" uint next_index = (gid + i) * values_per_thread;\n"
|
||||
" if((lid & mask) == 0 && next_index < input_size){\n" <<
|
||||
" scratch[lid] = " <<
|
||||
function(k.var<value_type>("scratch[lid]"),
|
||||
k.var<value_type>("scratch[lid+i]")) << ";\n" <<
|
||||
" }\n" <<
|
||||
"}\n" <<
|
||||
|
||||
// write output for block
|
||||
"if(lid == 0){\n" <<
|
||||
" output[get_group_id(0)] = scratch[0];\n" <<
|
||||
"}\n"
|
||||
;
|
||||
|
||||
const buffer *input_buffer = &first.get_buffer();
|
||||
const buffer *output_buffer = &output.get_buffer();
|
||||
|
||||
kernel kernel = k.compile(context);
|
||||
|
||||
while(input_size > 1){
|
||||
kernel.set_arg(input_arg, *input_buffer);
|
||||
kernel.set_arg(input_size_arg, static_cast<uint_>(input_size));
|
||||
kernel.set_arg(output_arg, *output_buffer);
|
||||
kernel.set_arg(scratch_arg, local_buffer<value_type>(block_size));
|
||||
|
||||
queue.enqueue_1d_range_kernel(kernel,
|
||||
0,
|
||||
block_count * block_size,
|
||||
block_size);
|
||||
|
||||
input_size =
|
||||
static_cast<size_t>(
|
||||
std::ceil(float(input_size) / (block_size * values_per_thread)
|
||||
)
|
||||
);
|
||||
|
||||
block_count = input_size / (block_size * values_per_thread);
|
||||
if(block_count * block_size * values_per_thread != input_size)
|
||||
block_count++;
|
||||
|
||||
std::swap(input_buffer, output_buffer);
|
||||
}
|
||||
|
||||
if(input_buffer != &first.get_buffer()){
|
||||
::boost::compute::copy(output.begin(),
|
||||
output.begin() + 1,
|
||||
first,
|
||||
queue);
|
||||
}
|
||||
}
|
||||
|
||||
} // end detail namespace
|
||||
} // end compute namespace
|
||||
} // end boost namespace
|
||||
|
||||
#endif // BOOST_COMPUTE_ALGORITHM_DETAIL_INPLACE_REDUCE_HPP
|
||||
+165
@@ -0,0 +1,165 @@
|
||||
//---------------------------------------------------------------------------//
|
||||
// Copyright (c) 2013 Kyle Lutz <kyle.r.lutz@gmail.com>
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0
|
||||
// See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt
|
||||
//
|
||||
// See http://boostorg.github.com/compute for more information.
|
||||
//---------------------------------------------------------------------------//
|
||||
|
||||
#ifndef BOOST_COMPUTE_ALGORITHM_DETAIL_INSERTION_SORT_HPP
|
||||
#define BOOST_COMPUTE_ALGORITHM_DETAIL_INSERTION_SORT_HPP
|
||||
|
||||
#include <boost/compute/kernel.hpp>
|
||||
#include <boost/compute/program.hpp>
|
||||
#include <boost/compute/command_queue.hpp>
|
||||
#include <boost/compute/detail/meta_kernel.hpp>
|
||||
#include <boost/compute/detail/iterator_range_size.hpp>
|
||||
#include <boost/compute/memory/local_buffer.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace compute {
|
||||
namespace detail {
|
||||
|
||||
template<class Iterator, class Compare>
|
||||
inline void serial_insertion_sort(Iterator first,
|
||||
Iterator last,
|
||||
Compare compare,
|
||||
command_queue &queue)
|
||||
{
|
||||
typedef typename std::iterator_traits<Iterator>::value_type T;
|
||||
|
||||
size_t count = iterator_range_size(first, last);
|
||||
if(count < 2){
|
||||
return;
|
||||
}
|
||||
|
||||
meta_kernel k("serial_insertion_sort");
|
||||
size_t local_data_arg = k.add_arg<T *>(memory_object::local_memory, "data");
|
||||
size_t count_arg = k.add_arg<uint_>("n");
|
||||
|
||||
k <<
|
||||
// copy data to local memory
|
||||
"for(uint i = 0; i < n; i++){\n" <<
|
||||
" data[i] = " << first[k.var<uint_>("i")] << ";\n"
|
||||
"}\n"
|
||||
|
||||
// sort data in local memory
|
||||
"for(uint i = 1; i < n; i++){\n" <<
|
||||
" " << k.decl<const T>("value") << " = data[i];\n" <<
|
||||
" uint pos = i;\n" <<
|
||||
" while(pos > 0 && " <<
|
||||
compare(k.var<const T>("value"),
|
||||
k.var<const T>("data[pos-1]")) << "){\n" <<
|
||||
" data[pos] = data[pos-1];\n" <<
|
||||
" pos--;\n" <<
|
||||
" }\n" <<
|
||||
" data[pos] = value;\n" <<
|
||||
"}\n" <<
|
||||
|
||||
// copy sorted data to output
|
||||
"for(uint i = 0; i < n; i++){\n" <<
|
||||
" " << first[k.var<uint_>("i")] << " = data[i];\n"
|
||||
"}\n";
|
||||
|
||||
const context &context = queue.get_context();
|
||||
::boost::compute::kernel kernel = k.compile(context);
|
||||
kernel.set_arg(local_data_arg, local_buffer<T>(count));
|
||||
kernel.set_arg(count_arg, static_cast<uint_>(count));
|
||||
|
||||
queue.enqueue_task(kernel);
|
||||
}
|
||||
|
||||
template<class Iterator>
|
||||
inline void serial_insertion_sort(Iterator first,
|
||||
Iterator last,
|
||||
command_queue &queue)
|
||||
{
|
||||
typedef typename std::iterator_traits<Iterator>::value_type T;
|
||||
|
||||
::boost::compute::less<T> less;
|
||||
|
||||
return serial_insertion_sort(first, last, less, queue);
|
||||
}
|
||||
|
||||
template<class KeyIterator, class ValueIterator, class Compare>
|
||||
inline void serial_insertion_sort_by_key(KeyIterator keys_first,
|
||||
KeyIterator keys_last,
|
||||
ValueIterator values_first,
|
||||
Compare compare,
|
||||
command_queue &queue)
|
||||
{
|
||||
typedef typename std::iterator_traits<KeyIterator>::value_type key_type;
|
||||
typedef typename std::iterator_traits<ValueIterator>::value_type value_type;
|
||||
|
||||
size_t count = iterator_range_size(keys_first, keys_last);
|
||||
if(count < 2){
|
||||
return;
|
||||
}
|
||||
|
||||
meta_kernel k("serial_insertion_sort_by_key");
|
||||
size_t local_keys_arg = k.add_arg<key_type *>(memory_object::local_memory, "keys");
|
||||
size_t local_data_arg = k.add_arg<value_type *>(memory_object::local_memory, "data");
|
||||
size_t count_arg = k.add_arg<uint_>("n");
|
||||
|
||||
k <<
|
||||
// copy data to local memory
|
||||
"for(uint i = 0; i < n; i++){\n" <<
|
||||
" keys[i] = " << keys_first[k.var<uint_>("i")] << ";\n"
|
||||
" data[i] = " << values_first[k.var<uint_>("i")] << ";\n"
|
||||
"}\n"
|
||||
|
||||
// sort data in local memory
|
||||
"for(uint i = 1; i < n; i++){\n" <<
|
||||
" " << k.decl<const key_type>("key") << " = keys[i];\n" <<
|
||||
" " << k.decl<const value_type>("value") << " = data[i];\n" <<
|
||||
" uint pos = i;\n" <<
|
||||
" while(pos > 0 && " <<
|
||||
compare(k.var<const key_type>("key"),
|
||||
k.var<const key_type>("keys[pos-1]")) << "){\n" <<
|
||||
" keys[pos] = keys[pos-1];\n" <<
|
||||
" data[pos] = data[pos-1];\n" <<
|
||||
" pos--;\n" <<
|
||||
" }\n" <<
|
||||
" keys[pos] = key;\n" <<
|
||||
" data[pos] = value;\n" <<
|
||||
"}\n" <<
|
||||
|
||||
// copy sorted data to output
|
||||
"for(uint i = 0; i < n; i++){\n" <<
|
||||
" " << keys_first[k.var<uint_>("i")] << " = keys[i];\n"
|
||||
" " << values_first[k.var<uint_>("i")] << " = data[i];\n"
|
||||
"}\n";
|
||||
|
||||
const context &context = queue.get_context();
|
||||
::boost::compute::kernel kernel = k.compile(context);
|
||||
kernel.set_arg(local_keys_arg, static_cast<uint_>(count * sizeof(key_type)), 0);
|
||||
kernel.set_arg(local_data_arg, static_cast<uint_>(count * sizeof(value_type)), 0);
|
||||
kernel.set_arg(count_arg, static_cast<uint_>(count));
|
||||
|
||||
queue.enqueue_task(kernel);
|
||||
}
|
||||
|
||||
template<class KeyIterator, class ValueIterator>
|
||||
inline void serial_insertion_sort_by_key(KeyIterator keys_first,
|
||||
KeyIterator keys_last,
|
||||
ValueIterator values_first,
|
||||
command_queue &queue)
|
||||
{
|
||||
typedef typename std::iterator_traits<KeyIterator>::value_type key_type;
|
||||
|
||||
serial_insertion_sort_by_key(
|
||||
keys_first,
|
||||
keys_last,
|
||||
values_first,
|
||||
boost::compute::less<key_type>(),
|
||||
queue
|
||||
);
|
||||
}
|
||||
|
||||
} // end detail namespace
|
||||
} // end compute namespace
|
||||
} // end boost namespace
|
||||
|
||||
#endif // BOOST_COMPUTE_ALGORITHM_DETAIL_INSERTION_SORT_HPP
|
||||
@@ -0,0 +1,116 @@
|
||||
//---------------------------------------------------------------------------//
|
||||
// Copyright (c) 2014 Roshan <thisisroshansmail@gmail.com>
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0
|
||||
// See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt
|
||||
//
|
||||
// See http://boostorg.github.com/compute for more information.
|
||||
//---------------------------------------------------------------------------//
|
||||
|
||||
#ifndef BOOST_COMPUTE_ALGORITHM_DETAIL_MERGE_PATH_HPP
|
||||
#define BOOST_COMPUTE_ALGORITHM_DETAIL_MERGE_PATH_HPP
|
||||
|
||||
#include <iterator>
|
||||
|
||||
#include <boost/compute/algorithm/find_if.hpp>
|
||||
#include <boost/compute/container/vector.hpp>
|
||||
#include <boost/compute/detail/iterator_range_size.hpp>
|
||||
#include <boost/compute/detail/meta_kernel.hpp>
|
||||
#include <boost/compute/lambda.hpp>
|
||||
#include <boost/compute/system.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace compute {
|
||||
namespace detail {
|
||||
|
||||
///
|
||||
/// \brief Merge Path kernel class
|
||||
///
|
||||
/// Subclass of meta_kernel to break two sets into tiles according
|
||||
/// to their merge path
|
||||
///
|
||||
class merge_path_kernel : public meta_kernel
|
||||
{
|
||||
public:
|
||||
unsigned int tile_size;
|
||||
|
||||
merge_path_kernel() : meta_kernel("merge_path")
|
||||
{
|
||||
tile_size = 4;
|
||||
}
|
||||
|
||||
template<class InputIterator1, class InputIterator2,
|
||||
class OutputIterator1, class OutputIterator2,
|
||||
class Compare>
|
||||
void set_range(InputIterator1 first1,
|
||||
InputIterator1 last1,
|
||||
InputIterator2 first2,
|
||||
InputIterator2 last2,
|
||||
OutputIterator1 result_a,
|
||||
OutputIterator2 result_b,
|
||||
Compare comp)
|
||||
{
|
||||
m_a_count = iterator_range_size(first1, last1);
|
||||
m_a_count_arg = add_arg<uint_>("a_count");
|
||||
|
||||
m_b_count = iterator_range_size(first2, last2);
|
||||
m_b_count_arg = add_arg<uint_>("b_count");
|
||||
|
||||
*this <<
|
||||
"uint i = get_global_id(0);\n" <<
|
||||
"uint target = (i+1)*" << tile_size << ";\n" <<
|
||||
"uint start = max(convert_int(0),convert_int(target)-convert_int(b_count));\n" <<
|
||||
"uint end = min(target,a_count);\n" <<
|
||||
"uint a_index, b_index;\n" <<
|
||||
"while(start<end)\n" <<
|
||||
"{\n" <<
|
||||
" a_index = (start + end)/2;\n" <<
|
||||
" b_index = target - a_index - 1;\n" <<
|
||||
" if(!(" << comp(first2[expr<uint_>("b_index")],
|
||||
first1[expr<uint_>("a_index")]) << "))\n" <<
|
||||
" start = a_index + 1;\n" <<
|
||||
" else end = a_index;\n" <<
|
||||
"}\n" <<
|
||||
result_a[expr<uint_>("i")] << " = start;\n" <<
|
||||
result_b[expr<uint_>("i")] << " = target - start;\n";
|
||||
}
|
||||
|
||||
template<class InputIterator1, class InputIterator2,
|
||||
class OutputIterator1, class OutputIterator2>
|
||||
void set_range(InputIterator1 first1,
|
||||
InputIterator1 last1,
|
||||
InputIterator2 first2,
|
||||
InputIterator2 last2,
|
||||
OutputIterator1 result_a,
|
||||
OutputIterator2 result_b)
|
||||
{
|
||||
typedef typename std::iterator_traits<InputIterator1>::value_type value_type;
|
||||
::boost::compute::less<value_type> less_than;
|
||||
set_range(first1, last1, first2, last2, result_a, result_b, less_than);
|
||||
}
|
||||
|
||||
event exec(command_queue &queue)
|
||||
{
|
||||
if((m_a_count + m_b_count)/tile_size == 0) {
|
||||
return event();
|
||||
}
|
||||
|
||||
set_arg(m_a_count_arg, uint_(m_a_count));
|
||||
set_arg(m_b_count_arg, uint_(m_b_count));
|
||||
|
||||
return exec_1d(queue, 0, (m_a_count + m_b_count)/tile_size);
|
||||
}
|
||||
|
||||
private:
|
||||
size_t m_a_count;
|
||||
size_t m_a_count_arg;
|
||||
size_t m_b_count;
|
||||
size_t m_b_count_arg;
|
||||
};
|
||||
|
||||
} //end detail namespace
|
||||
} //end compute namespace
|
||||
} //end boost namespace
|
||||
|
||||
#endif // BOOST_COMPUTE_ALGORITHM_DETAIL_MERGE_PATH_HPP
|
||||
+366
@@ -0,0 +1,366 @@
|
||||
//---------------------------------------------------------------------------//
|
||||
// Copyright (c) 2015 Jakub Szuppe <j.szuppe@gmail.com>
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0
|
||||
// See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt
|
||||
//
|
||||
// See http://boostorg.github.com/compute for more information.
|
||||
//---------------------------------------------------------------------------//
|
||||
|
||||
#ifndef BOOST_COMPUTE_ALGORITHM_DETAIL_MERGE_SORT_ON_CPU_HPP
|
||||
#define BOOST_COMPUTE_ALGORITHM_DETAIL_MERGE_SORT_ON_CPU_HPP
|
||||
|
||||
#include <boost/compute/kernel.hpp>
|
||||
#include <boost/compute/program.hpp>
|
||||
#include <boost/compute/command_queue.hpp>
|
||||
#include <boost/compute/algorithm/detail/merge_with_merge_path.hpp>
|
||||
#include <boost/compute/container/vector.hpp>
|
||||
#include <boost/compute/detail/meta_kernel.hpp>
|
||||
#include <boost/compute/detail/iterator_range_size.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace compute {
|
||||
namespace detail {
|
||||
|
||||
template<class KeyIterator, class ValueIterator, class Compare>
|
||||
inline void merge_blocks(KeyIterator keys_first,
|
||||
ValueIterator values_first,
|
||||
KeyIterator keys_result,
|
||||
ValueIterator values_result,
|
||||
Compare compare,
|
||||
size_t count,
|
||||
const size_t block_size,
|
||||
const bool sort_by_key,
|
||||
command_queue &queue)
|
||||
{
|
||||
(void) values_result;
|
||||
(void) values_first;
|
||||
|
||||
meta_kernel k("merge_sort_on_cpu_merge_blocks");
|
||||
size_t count_arg = k.add_arg<const uint_>("count");
|
||||
size_t block_size_arg = k.add_arg<uint_>("block_size");
|
||||
|
||||
k <<
|
||||
k.decl<uint_>("b1_start") << " = get_global_id(0) * block_size * 2;\n" <<
|
||||
k.decl<uint_>("b1_end") << " = min(count, b1_start + block_size);\n" <<
|
||||
k.decl<uint_>("b2_start") << " = min(count, b1_start + block_size);\n" <<
|
||||
k.decl<uint_>("b2_end") << " = min(count, b2_start + block_size);\n" <<
|
||||
k.decl<uint_>("result_idx") << " = b1_start;\n" <<
|
||||
|
||||
// merging block 1 and block 2 (stable)
|
||||
"while(b1_start < b1_end && b2_start < b2_end){\n" <<
|
||||
" if( " << compare(keys_first[k.var<uint_>("b2_start")],
|
||||
keys_first[k.var<uint_>("b1_start")]) << "){\n" <<
|
||||
" " << keys_result[k.var<uint_>("result_idx")] << " = " <<
|
||||
keys_first[k.var<uint_>("b2_start")] << ";\n";
|
||||
if(sort_by_key){
|
||||
k <<
|
||||
" " << values_result[k.var<uint_>("result_idx")] << " = " <<
|
||||
values_first[k.var<uint_>("b2_start")] << ";\n";
|
||||
}
|
||||
k <<
|
||||
" b2_start++;\n" <<
|
||||
" }\n" <<
|
||||
" else {\n" <<
|
||||
" " << keys_result[k.var<uint_>("result_idx")] << " = " <<
|
||||
keys_first[k.var<uint_>("b1_start")] << ";\n";
|
||||
if(sort_by_key){
|
||||
k <<
|
||||
" " << values_result[k.var<uint_>("result_idx")] << " = " <<
|
||||
values_first[k.var<uint_>("b1_start")] << ";\n";
|
||||
}
|
||||
k <<
|
||||
" b1_start++;\n" <<
|
||||
" }\n" <<
|
||||
" result_idx++;\n" <<
|
||||
"}\n" <<
|
||||
"while(b1_start < b1_end){\n" <<
|
||||
" " << keys_result[k.var<uint_>("result_idx")] << " = " <<
|
||||
keys_first[k.var<uint_>("b1_start")] << ";\n";
|
||||
if(sort_by_key){
|
||||
k <<
|
||||
" " << values_result[k.var<uint_>("result_idx")] << " = " <<
|
||||
values_first[k.var<uint_>("b1_start")] << ";\n";
|
||||
}
|
||||
k <<
|
||||
" b1_start++;\n" <<
|
||||
" result_idx++;\n" <<
|
||||
"}\n" <<
|
||||
"while(b2_start < b2_end){\n" <<
|
||||
" " << keys_result[k.var<uint_>("result_idx")] << " = " <<
|
||||
keys_first[k.var<uint_>("b2_start")] << ";\n";
|
||||
if(sort_by_key){
|
||||
k <<
|
||||
" " << values_result[k.var<uint_>("result_idx")] << " = " <<
|
||||
values_first[k.var<uint_>("b2_start")] << ";\n";
|
||||
}
|
||||
k <<
|
||||
" b2_start++;\n" <<
|
||||
" result_idx++;\n" <<
|
||||
"}\n";
|
||||
|
||||
const context &context = queue.get_context();
|
||||
::boost::compute::kernel kernel = k.compile(context);
|
||||
kernel.set_arg(count_arg, static_cast<const uint_>(count));
|
||||
kernel.set_arg(block_size_arg, static_cast<uint_>(block_size));
|
||||
|
||||
const size_t global_size = static_cast<size_t>(
|
||||
std::ceil(float(count) / (2 * block_size))
|
||||
);
|
||||
queue.enqueue_1d_range_kernel(kernel, 0, global_size, 0);
|
||||
}
|
||||
|
||||
template<class Iterator, class Compare>
|
||||
inline void merge_blocks(Iterator first,
|
||||
Iterator result,
|
||||
Compare compare,
|
||||
size_t count,
|
||||
const size_t block_size,
|
||||
const bool sort_by_key,
|
||||
command_queue &queue)
|
||||
{
|
||||
// dummy iterator as it's not sort by key
|
||||
Iterator dummy;
|
||||
merge_blocks(first, dummy, result, dummy, compare, count, block_size, false, queue);
|
||||
}
|
||||
|
||||
template<class Iterator, class Compare>
|
||||
inline void dispatch_merge_blocks(Iterator first,
|
||||
Iterator result,
|
||||
Compare compare,
|
||||
size_t count,
|
||||
const size_t block_size,
|
||||
const size_t input_size_threshold,
|
||||
const size_t blocks_no_threshold,
|
||||
command_queue &queue)
|
||||
{
|
||||
const size_t blocks_no = static_cast<size_t>(
|
||||
std::ceil(float(count) / block_size)
|
||||
);
|
||||
// merge with merge path should used only for the large arrays and at the
|
||||
// end of merging part when there are only a few big blocks left to be merged
|
||||
if(blocks_no <= blocks_no_threshold && count >= input_size_threshold){
|
||||
Iterator last = first + count;
|
||||
for(size_t i = 0; i < count; i+= 2*block_size)
|
||||
{
|
||||
Iterator first1 = (std::min)(first + i, last);
|
||||
Iterator last1 = (std::min)(first1 + block_size, last);
|
||||
Iterator first2 = last1;
|
||||
Iterator last2 = (std::min)(first2 + block_size, last);
|
||||
Iterator block_result = (std::min)(result + i, result + count);
|
||||
merge_with_merge_path(first1, last1, first2, last2,
|
||||
block_result, compare, queue);
|
||||
}
|
||||
}
|
||||
else {
|
||||
merge_blocks(first, result, compare, count, block_size, false, queue);
|
||||
}
|
||||
}
|
||||
|
||||
template<class KeyIterator, class ValueIterator, class Compare>
|
||||
inline void block_insertion_sort(KeyIterator keys_first,
|
||||
ValueIterator values_first,
|
||||
Compare compare,
|
||||
const size_t count,
|
||||
const size_t block_size,
|
||||
const bool sort_by_key,
|
||||
command_queue &queue)
|
||||
{
|
||||
(void) values_first;
|
||||
|
||||
typedef typename std::iterator_traits<KeyIterator>::value_type K;
|
||||
typedef typename std::iterator_traits<ValueIterator>::value_type T;
|
||||
|
||||
meta_kernel k("merge_sort_on_cpu_block_insertion_sort");
|
||||
size_t count_arg = k.add_arg<uint_>("count");
|
||||
size_t block_size_arg = k.add_arg<uint_>("block_size");
|
||||
|
||||
k <<
|
||||
k.decl<uint_>("start") << " = get_global_id(0) * block_size;\n" <<
|
||||
k.decl<uint_>("end") << " = min(count, start + block_size);\n" <<
|
||||
|
||||
// block insertion sort (stable)
|
||||
"for(uint i = start+1; i < end; i++){\n" <<
|
||||
" " << k.decl<const K>("key") << " = " <<
|
||||
keys_first[k.var<uint_>("i")] << ";\n";
|
||||
if(sort_by_key){
|
||||
k <<
|
||||
" " << k.decl<const T>("value") << " = " <<
|
||||
values_first[k.var<uint_>("i")] << ";\n";
|
||||
}
|
||||
k <<
|
||||
" uint pos = i;\n" <<
|
||||
" while(pos > start && " <<
|
||||
compare(k.var<const K>("key"),
|
||||
keys_first[k.var<uint_>("pos-1")]) << "){\n" <<
|
||||
" " << keys_first[k.var<uint_>("pos")] << " = " <<
|
||||
keys_first[k.var<uint_>("pos-1")] << ";\n";
|
||||
if(sort_by_key){
|
||||
k <<
|
||||
" " << values_first[k.var<uint_>("pos")] << " = " <<
|
||||
values_first[k.var<uint_>("pos-1")] << ";\n";
|
||||
}
|
||||
k <<
|
||||
" pos--;\n" <<
|
||||
" }\n" <<
|
||||
" " << keys_first[k.var<uint_>("pos")] << " = key;\n";
|
||||
if(sort_by_key) {
|
||||
k <<
|
||||
" " << values_first[k.var<uint_>("pos")] << " = value;\n";
|
||||
}
|
||||
k <<
|
||||
"}\n"; // block insertion sort
|
||||
|
||||
const context &context = queue.get_context();
|
||||
::boost::compute::kernel kernel = k.compile(context);
|
||||
kernel.set_arg(count_arg, static_cast<uint_>(count));
|
||||
kernel.set_arg(block_size_arg, static_cast<uint_>(block_size));
|
||||
|
||||
const size_t global_size = static_cast<size_t>(std::ceil(float(count) / block_size));
|
||||
queue.enqueue_1d_range_kernel(kernel, 0, global_size, 0);
|
||||
}
|
||||
|
||||
template<class Iterator, class Compare>
|
||||
inline void block_insertion_sort(Iterator first,
|
||||
Compare compare,
|
||||
const size_t count,
|
||||
const size_t block_size,
|
||||
command_queue &queue)
|
||||
{
|
||||
// dummy iterator as it's not sort by key
|
||||
Iterator dummy;
|
||||
block_insertion_sort(first, dummy, compare, count, block_size, false, queue);
|
||||
}
|
||||
|
||||
// This sort is stable.
|
||||
template<class Iterator, class Compare>
|
||||
inline void merge_sort_on_cpu(Iterator first,
|
||||
Iterator last,
|
||||
Compare compare,
|
||||
command_queue &queue)
|
||||
{
|
||||
typedef typename std::iterator_traits<Iterator>::value_type value_type;
|
||||
|
||||
size_t count = iterator_range_size(first, last);
|
||||
if(count < 2){
|
||||
return;
|
||||
}
|
||||
// for small input size only insertion sort is performed
|
||||
else if(count <= 512){
|
||||
block_insertion_sort(first, compare, count, count, queue);
|
||||
return;
|
||||
}
|
||||
|
||||
const context &context = queue.get_context();
|
||||
const device &device = queue.get_device();
|
||||
|
||||
// loading parameters
|
||||
std::string cache_key =
|
||||
std::string("__boost_merge_sort_on_cpu_") + type_name<value_type>();
|
||||
boost::shared_ptr<parameter_cache> parameters =
|
||||
detail::parameter_cache::get_global_cache(device);
|
||||
|
||||
// When there is merge_with_path_blocks_no_threshold or less blocks left to
|
||||
// merge AND input size is merge_with_merge_path_input_size_threshold or more
|
||||
// merge_with_merge_path() algorithm is used to merge sorted blocks;
|
||||
// otherwise merge_blocks() is used.
|
||||
const size_t merge_with_path_blocks_no_threshold =
|
||||
parameters->get(cache_key, "merge_with_merge_path_blocks_no_threshold", 8);
|
||||
const size_t merge_with_path_input_size_threshold =
|
||||
parameters->get(cache_key, "merge_with_merge_path_input_size_threshold", 2097152);
|
||||
|
||||
const size_t block_size =
|
||||
parameters->get(cache_key, "insertion_sort_block_size", 64);
|
||||
block_insertion_sort(first, compare, count, block_size, queue);
|
||||
|
||||
// temporary buffer for merge result
|
||||
vector<value_type> temp(count, context);
|
||||
bool result_in_temporary_buffer = false;
|
||||
|
||||
for(size_t i = block_size; i < count; i *= 2){
|
||||
result_in_temporary_buffer = !result_in_temporary_buffer;
|
||||
if(result_in_temporary_buffer) {
|
||||
dispatch_merge_blocks(first, temp.begin(), compare, count, i,
|
||||
merge_with_path_input_size_threshold,
|
||||
merge_with_path_blocks_no_threshold,
|
||||
queue);
|
||||
} else {
|
||||
dispatch_merge_blocks(temp.begin(), first, compare, count, i,
|
||||
merge_with_path_input_size_threshold,
|
||||
merge_with_path_blocks_no_threshold,
|
||||
queue);
|
||||
}
|
||||
}
|
||||
|
||||
if(result_in_temporary_buffer) {
|
||||
copy(temp.begin(), temp.end(), first, queue);
|
||||
}
|
||||
}
|
||||
|
||||
// This sort is stable.
|
||||
template<class KeyIterator, class ValueIterator, class Compare>
|
||||
inline void merge_sort_by_key_on_cpu(KeyIterator keys_first,
|
||||
KeyIterator keys_last,
|
||||
ValueIterator values_first,
|
||||
Compare compare,
|
||||
command_queue &queue)
|
||||
{
|
||||
typedef typename std::iterator_traits<KeyIterator>::value_type key_type;
|
||||
typedef typename std::iterator_traits<ValueIterator>::value_type value_type;
|
||||
|
||||
size_t count = iterator_range_size(keys_first, keys_last);
|
||||
if(count < 2){
|
||||
return;
|
||||
}
|
||||
// for small input size only insertion sort is performed
|
||||
else if(count <= 512){
|
||||
block_insertion_sort(keys_first, values_first, compare,
|
||||
count, count, true, queue);
|
||||
return;
|
||||
}
|
||||
|
||||
const context &context = queue.get_context();
|
||||
const device &device = queue.get_device();
|
||||
|
||||
// loading parameters
|
||||
std::string cache_key =
|
||||
std::string("__boost_merge_sort_by_key_on_cpu_") + type_name<value_type>()
|
||||
+ "_with_" + type_name<key_type>();
|
||||
boost::shared_ptr<parameter_cache> parameters =
|
||||
detail::parameter_cache::get_global_cache(device);
|
||||
|
||||
const size_t block_size =
|
||||
parameters->get(cache_key, "insertion_sort_by_key_block_size", 64);
|
||||
block_insertion_sort(keys_first, values_first, compare,
|
||||
count, block_size, true, queue);
|
||||
|
||||
// temporary buffer for merge results
|
||||
vector<value_type> values_temp(count, context);
|
||||
vector<key_type> keys_temp(count, context);
|
||||
bool result_in_temporary_buffer = false;
|
||||
|
||||
for(size_t i = block_size; i < count; i *= 2){
|
||||
result_in_temporary_buffer = !result_in_temporary_buffer;
|
||||
if(result_in_temporary_buffer) {
|
||||
merge_blocks(keys_first, values_first,
|
||||
keys_temp.begin(), values_temp.begin(),
|
||||
compare, count, i, true, queue);
|
||||
} else {
|
||||
merge_blocks(keys_temp.begin(), values_temp.begin(),
|
||||
keys_first, values_first,
|
||||
compare, count, i, true, queue);
|
||||
}
|
||||
}
|
||||
|
||||
if(result_in_temporary_buffer) {
|
||||
copy(keys_temp.begin(), keys_temp.end(), keys_first, queue);
|
||||
copy(values_temp.begin(), values_temp.end(), values_first, queue);
|
||||
}
|
||||
}
|
||||
|
||||
} // end detail namespace
|
||||
} // end compute namespace
|
||||
} // end boost namespace
|
||||
|
||||
#endif // BOOST_COMPUTE_ALGORITHM_DETAIL_MERGE_SORT_ON_CPU_HPP
|
||||
+590
@@ -0,0 +1,590 @@
|
||||
//---------------------------------------------------------------------------//
|
||||
// Copyright (c) 2016 Jakub Szuppe <j.szuppe@gmail.com>
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0
|
||||
// See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt
|
||||
//
|
||||
// See http://boostorg.github.com/compute for more information.
|
||||
//---------------------------------------------------------------------------//
|
||||
|
||||
#ifndef BOOST_COMPUTE_ALGORITHM_DETAIL_MERGE_SORT_ON_GPU_HPP_
|
||||
#define BOOST_COMPUTE_ALGORITHM_DETAIL_MERGE_SORT_ON_GPU_HPP_
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include <boost/compute/kernel.hpp>
|
||||
#include <boost/compute/program.hpp>
|
||||
#include <boost/compute/command_queue.hpp>
|
||||
#include <boost/compute/container/vector.hpp>
|
||||
#include <boost/compute/memory/local_buffer.hpp>
|
||||
#include <boost/compute/detail/meta_kernel.hpp>
|
||||
#include <boost/compute/detail/iterator_range_size.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace compute {
|
||||
namespace detail {
|
||||
|
||||
template<class KeyType, class ValueType>
|
||||
inline size_t pick_bitonic_block_sort_block_size(size_t proposed_wg,
|
||||
size_t lmem_size,
|
||||
bool sort_by_key)
|
||||
{
|
||||
size_t n = proposed_wg;
|
||||
|
||||
size_t lmem_required = n * sizeof(KeyType);
|
||||
if(sort_by_key) {
|
||||
lmem_required += n * sizeof(ValueType);
|
||||
}
|
||||
|
||||
// try to force at least 4 work-groups of >64 elements
|
||||
// for better occupancy
|
||||
while(lmem_size < (lmem_required * 4) && (n > 64)) {
|
||||
n /= 2;
|
||||
lmem_required = n * sizeof(KeyType);
|
||||
}
|
||||
while(lmem_size < lmem_required && (n != 1)) {
|
||||
n /= 2;
|
||||
if(n < 1) n = 1;
|
||||
lmem_required = n * sizeof(KeyType);
|
||||
}
|
||||
|
||||
if(n < 2) { return 1; }
|
||||
else if(n < 4) { return 2; }
|
||||
else if(n < 8) { return 4; }
|
||||
else if(n < 16) { return 8; }
|
||||
else if(n < 32) { return 16; }
|
||||
else if(n < 64) { return 32; }
|
||||
else if(n < 128) { return 64; }
|
||||
else if(n < 256) { return 128; }
|
||||
else { return 256; }
|
||||
}
|
||||
|
||||
|
||||
/// Performs bitonic block sort according to \p compare.
|
||||
///
|
||||
/// Since bitonic sort can be only performed when input size is equal to 2^n,
|
||||
/// in this case input size is block size (\p work_group_size), we would have
|
||||
/// to require \p count be a exact multiple of block size. That would not be
|
||||
/// great.
|
||||
/// Instead, bitonic sort kernel is merged with odd-even merge sort so if the
|
||||
/// last block is not equal to 2^n (where n is some natural number) the odd-even
|
||||
/// sort is performed for that block. That way bitonic_block_sort() works for
|
||||
/// input of any size. Block size (\p work_group_size) still have to be equal
|
||||
/// to 2^n.
|
||||
///
|
||||
/// This is NOT stable.
|
||||
///
|
||||
/// \param keys_first first key element in the range to sort
|
||||
/// \param values_first first value element in the range to sort
|
||||
/// \param compare comparison function for keys
|
||||
/// \param count number of elements in the range; count > 0
|
||||
/// \param work_group_size size of the work group, also the block size; must be
|
||||
/// equal to n^2 where n is natural number
|
||||
/// \param queue command queue to perform the operation
|
||||
template<class KeyIterator, class ValueIterator, class Compare>
|
||||
inline size_t bitonic_block_sort(KeyIterator keys_first,
|
||||
ValueIterator values_first,
|
||||
Compare compare,
|
||||
const size_t count,
|
||||
const bool sort_by_key,
|
||||
command_queue &queue)
|
||||
{
|
||||
typedef typename std::iterator_traits<KeyIterator>::value_type key_type;
|
||||
|
||||
meta_kernel k("bitonic_block_sort");
|
||||
size_t count_arg = k.add_arg<const uint_>("count");
|
||||
|
||||
size_t local_keys_arg = k.add_arg<key_type *>(memory_object::local_memory, "lkeys");
|
||||
size_t local_vals_arg = 0;
|
||||
if(sort_by_key) {
|
||||
local_vals_arg = k.add_arg<uchar_ *>(memory_object::local_memory, "lidx");
|
||||
}
|
||||
|
||||
k <<
|
||||
// Work item global and local ids
|
||||
k.decl<const uint_>("gid") << " = get_global_id(0);\n" <<
|
||||
k.decl<const uint_>("lid") << " = get_local_id(0);\n";
|
||||
|
||||
// declare my_key and my_value
|
||||
k <<
|
||||
k.decl<key_type>("my_key") << ";\n";
|
||||
// Instead of copying values (my_value) in local memory with keys
|
||||
// we save local index (uchar) and copy my_value at the end at
|
||||
// final index. This saves local memory.
|
||||
if(sort_by_key)
|
||||
{
|
||||
k <<
|
||||
k.decl<uchar_>("my_index") << " = (uchar)(lid);\n";
|
||||
}
|
||||
|
||||
// load key
|
||||
k <<
|
||||
"if(gid < count) {\n" <<
|
||||
k.var<key_type>("my_key") << " = " <<
|
||||
keys_first[k.var<const uint_>("gid")] << ";\n" <<
|
||||
"}\n";
|
||||
|
||||
// load key and index to local memory
|
||||
k <<
|
||||
"lkeys[lid] = my_key;\n";
|
||||
if(sort_by_key)
|
||||
{
|
||||
k <<
|
||||
"lidx[lid] = my_index;\n";
|
||||
}
|
||||
k <<
|
||||
k.decl<const uint_>("offset") << " = get_group_id(0) * get_local_size(0);\n" <<
|
||||
k.decl<const uint_>("n") << " = min((uint)(get_local_size(0)),(count - offset));\n";
|
||||
|
||||
// When work group size is a power of 2 bitonic sorter can be used;
|
||||
// otherwise, slower odd-even sort is used.
|
||||
|
||||
k <<
|
||||
// check if n is power of 2
|
||||
"if(((n != 0) && ((n & (~n + 1)) == n))) {\n";
|
||||
|
||||
// bitonic sort, not stable
|
||||
k <<
|
||||
// wait for keys and vals to be stored in local memory
|
||||
"barrier(CLK_LOCAL_MEM_FENCE);\n" <<
|
||||
|
||||
"#pragma unroll\n" <<
|
||||
"for(" <<
|
||||
k.decl<uint_>("length") << " = 1; " <<
|
||||
"length < n; " <<
|
||||
"length <<= 1" <<
|
||||
") {\n" <<
|
||||
// direction of sort: false -> asc, true -> desc
|
||||
k.decl<bool>("direction") << "= ((lid & (length<<1)) != 0);\n" <<
|
||||
"for(" <<
|
||||
k.decl<uint_>("k") << " = length; " <<
|
||||
"k > 0; " <<
|
||||
"k >>= 1" <<
|
||||
") {\n" <<
|
||||
|
||||
// sibling to compare with my key
|
||||
k.decl<uint_>("sibling_idx") << " = lid ^ k;\n" <<
|
||||
k.decl<key_type>("sibling_key") << " = lkeys[sibling_idx];\n" <<
|
||||
k.decl<bool>("compare") << " = " <<
|
||||
compare(k.var<key_type>("sibling_key"),
|
||||
k.var<key_type>("my_key")) << ";\n" <<
|
||||
k.decl<bool>("swap") <<
|
||||
" = compare ^ (sibling_idx < lid) ^ direction;\n" <<
|
||||
"my_key = swap ? sibling_key : my_key;\n";
|
||||
if(sort_by_key)
|
||||
{
|
||||
k <<
|
||||
"my_index = swap ? lidx[sibling_idx] : my_index;\n";
|
||||
}
|
||||
k <<
|
||||
"barrier(CLK_LOCAL_MEM_FENCE);\n" <<
|
||||
"lkeys[lid] = my_key;\n";
|
||||
if(sort_by_key)
|
||||
{
|
||||
k <<
|
||||
"lidx[lid] = my_index;\n";
|
||||
}
|
||||
k <<
|
||||
"barrier(CLK_LOCAL_MEM_FENCE);\n" <<
|
||||
"}\n" <<
|
||||
"}\n";
|
||||
|
||||
// end of bitonic sort
|
||||
|
||||
// odd-even sort, not stable
|
||||
k <<
|
||||
"}\n" <<
|
||||
"else { \n";
|
||||
|
||||
k <<
|
||||
k.decl<bool>("lid_is_even") << " = (lid%2) == 0;\n" <<
|
||||
k.decl<uint_>("oddsibling_idx") << " = " <<
|
||||
"(lid_is_even) ? max(lid,(uint)(1)) - 1 : min(lid+1,n-1);\n" <<
|
||||
k.decl<uint_>("evensibling_idx") << " = " <<
|
||||
"(lid_is_even) ? min(lid+1,n-1) : max(lid,(uint)(1)) - 1;\n" <<
|
||||
|
||||
// wait for keys and vals to be stored in local memory
|
||||
"barrier(CLK_LOCAL_MEM_FENCE);\n" <<
|
||||
|
||||
"#pragma unroll\n" <<
|
||||
"for(" <<
|
||||
k.decl<uint_>("i") << " = 0; " <<
|
||||
"i < n; " <<
|
||||
"i++" <<
|
||||
") {\n" <<
|
||||
k.decl<uint_>("sibling_idx") <<
|
||||
" = i%2 == 0 ? evensibling_idx : oddsibling_idx;\n" <<
|
||||
k.decl<key_type>("sibling_key") << " = lkeys[sibling_idx];\n" <<
|
||||
k.decl<bool>("compare") << " = " <<
|
||||
compare(k.var<key_type>("sibling_key"),
|
||||
k.var<key_type>("my_key")) << ";\n" <<
|
||||
k.decl<bool>("swap") <<
|
||||
" = compare ^ (sibling_idx < lid);\n" <<
|
||||
"my_key = swap ? sibling_key : my_key;\n";
|
||||
if(sort_by_key)
|
||||
{
|
||||
k <<
|
||||
"my_index = swap ? lidx[sibling_idx] : my_index;\n";
|
||||
}
|
||||
k <<
|
||||
"barrier(CLK_LOCAL_MEM_FENCE);\n" <<
|
||||
"lkeys[lid] = my_key;\n";
|
||||
if(sort_by_key)
|
||||
{
|
||||
k <<
|
||||
"lidx[lid] = my_index;\n";
|
||||
}
|
||||
k <<
|
||||
"barrier(CLK_LOCAL_MEM_FENCE);\n"
|
||||
"}\n" << // for
|
||||
|
||||
"}\n"; // else
|
||||
// end of odd-even sort
|
||||
|
||||
// save key and value
|
||||
k <<
|
||||
"if(gid < count) {\n" <<
|
||||
keys_first[k.var<const uint_>("gid")] << " = " <<
|
||||
k.var<key_type>("my_key") << ";\n";
|
||||
if(sort_by_key)
|
||||
{
|
||||
k << values_first[k.var<const uint_>("gid")] << " = " <<
|
||||
values_first[k.var<const uint_>("offset + my_index")] << ";\n";
|
||||
}
|
||||
k <<
|
||||
// end if
|
||||
"}\n";
|
||||
|
||||
const context &context = queue.get_context();
|
||||
const device &device = queue.get_device();
|
||||
::boost::compute::kernel kernel = k.compile(context);
|
||||
|
||||
const size_t work_group_size =
|
||||
pick_bitonic_block_sort_block_size<key_type, uchar_>(
|
||||
kernel.get_work_group_info<size_t>(
|
||||
device, CL_KERNEL_WORK_GROUP_SIZE
|
||||
),
|
||||
device.get_info<size_t>(CL_DEVICE_LOCAL_MEM_SIZE),
|
||||
sort_by_key
|
||||
);
|
||||
|
||||
const size_t global_size =
|
||||
work_group_size * static_cast<size_t>(
|
||||
std::ceil(float(count) / work_group_size)
|
||||
);
|
||||
|
||||
kernel.set_arg(count_arg, static_cast<uint_>(count));
|
||||
kernel.set_arg(local_keys_arg, local_buffer<key_type>(work_group_size));
|
||||
if(sort_by_key) {
|
||||
kernel.set_arg(local_vals_arg, local_buffer<uchar_>(work_group_size));
|
||||
}
|
||||
|
||||
queue.enqueue_1d_range_kernel(kernel, 0, global_size, work_group_size);
|
||||
// return size of the block
|
||||
return work_group_size;
|
||||
}
|
||||
|
||||
template<class KeyIterator, class ValueIterator, class Compare>
|
||||
inline size_t block_sort(KeyIterator keys_first,
|
||||
ValueIterator values_first,
|
||||
Compare compare,
|
||||
const size_t count,
|
||||
const bool sort_by_key,
|
||||
const bool stable,
|
||||
command_queue &queue)
|
||||
{
|
||||
if(stable) {
|
||||
// TODO: Implement stable block sort (stable odd-even merge sort)
|
||||
return size_t(1);
|
||||
}
|
||||
return bitonic_block_sort(
|
||||
keys_first, values_first,
|
||||
compare, count,
|
||||
sort_by_key, queue
|
||||
);
|
||||
}
|
||||
|
||||
/// space: O(n + m); n - number of keys, m - number of values
|
||||
template<class KeyIterator, class ValueIterator, class Compare>
|
||||
inline void merge_blocks_on_gpu(KeyIterator keys_first,
|
||||
ValueIterator values_first,
|
||||
KeyIterator out_keys_first,
|
||||
ValueIterator out_values_first,
|
||||
Compare compare,
|
||||
const size_t count,
|
||||
const size_t block_size,
|
||||
const bool sort_by_key,
|
||||
command_queue &queue)
|
||||
{
|
||||
typedef typename std::iterator_traits<KeyIterator>::value_type key_type;
|
||||
typedef typename std::iterator_traits<ValueIterator>::value_type value_type;
|
||||
|
||||
meta_kernel k("merge_blocks");
|
||||
size_t count_arg = k.add_arg<const uint_>("count");
|
||||
size_t block_size_arg = k.add_arg<const uint_>("block_size");
|
||||
|
||||
k <<
|
||||
// get global id
|
||||
k.decl<const uint_>("gid") << " = get_global_id(0);\n" <<
|
||||
"if(gid >= count) {\n" <<
|
||||
"return;\n" <<
|
||||
"}\n" <<
|
||||
|
||||
k.decl<const key_type>("my_key") << " = " <<
|
||||
keys_first[k.var<const uint_>("gid")] << ";\n";
|
||||
|
||||
if(sort_by_key) {
|
||||
k <<
|
||||
k.decl<const value_type>("my_value") << " = " <<
|
||||
values_first[k.var<const uint_>("gid")] << ";\n";
|
||||
}
|
||||
|
||||
k <<
|
||||
// get my block idx
|
||||
k.decl<const uint_>("my_block_idx") << " = gid / block_size;\n" <<
|
||||
k.decl<const bool>("my_block_idx_is_odd") << " = " <<
|
||||
"my_block_idx & 0x1;\n" <<
|
||||
|
||||
k.decl<const uint_>("other_block_idx") << " = " <<
|
||||
// if(my_block_idx is odd) {} else {}
|
||||
"my_block_idx_is_odd ? my_block_idx - 1 : my_block_idx + 1;\n" <<
|
||||
|
||||
// get ranges of my block and the other block
|
||||
// [my_block_start; my_block_end)
|
||||
// [other_block_start; other_block_end)
|
||||
k.decl<const uint_>("my_block_start") << " = " <<
|
||||
"min(my_block_idx * block_size, count);\n" << // including
|
||||
k.decl<const uint_>("my_block_end") << " = " <<
|
||||
"min((my_block_idx + 1) * block_size, count);\n" << // excluding
|
||||
|
||||
k.decl<const uint_>("other_block_start") << " = " <<
|
||||
"min(other_block_idx * block_size, count);\n" << // including
|
||||
k.decl<const uint_>("other_block_end") << " = " <<
|
||||
"min((other_block_idx + 1) * block_size, count);\n" << // excluding
|
||||
|
||||
// other block is empty, nothing to merge here
|
||||
"if(other_block_start == count){\n" <<
|
||||
out_keys_first[k.var<uint_>("gid")] << " = my_key;\n";
|
||||
if(sort_by_key) {
|
||||
k <<
|
||||
out_values_first[k.var<uint_>("gid")] << " = my_value;\n";
|
||||
}
|
||||
|
||||
k <<
|
||||
"return;\n" <<
|
||||
"}\n" <<
|
||||
|
||||
// lower bound
|
||||
// left_idx - lower bound
|
||||
k.decl<uint_>("left_idx") << " = other_block_start;\n" <<
|
||||
k.decl<uint_>("right_idx") << " = other_block_end;\n" <<
|
||||
"while(left_idx < right_idx) {\n" <<
|
||||
k.decl<uint_>("mid_idx") << " = (left_idx + right_idx) / 2;\n" <<
|
||||
k.decl<key_type>("mid_key") << " = " <<
|
||||
keys_first[k.var<const uint_>("mid_idx")] << ";\n" <<
|
||||
k.decl<bool>("smaller") << " = " <<
|
||||
compare(k.var<key_type>("mid_key"),
|
||||
k.var<key_type>("my_key")) << ";\n" <<
|
||||
"left_idx = smaller ? mid_idx + 1 : left_idx;\n" <<
|
||||
"right_idx = smaller ? right_idx : mid_idx;\n" <<
|
||||
"}\n" <<
|
||||
// left_idx is found position in other block
|
||||
|
||||
// if my_block is odd we need to get the upper bound
|
||||
"right_idx = other_block_end;\n" <<
|
||||
"if(my_block_idx_is_odd && left_idx != right_idx) {\n" <<
|
||||
k.decl<key_type>("upper_key") << " = " <<
|
||||
keys_first[k.var<const uint_>("left_idx")] << ";\n" <<
|
||||
"while(" <<
|
||||
"!(" << compare(k.var<key_type>("upper_key"),
|
||||
k.var<key_type>("my_key")) <<
|
||||
") && " <<
|
||||
"!(" << compare(k.var<key_type>("my_key"),
|
||||
k.var<key_type>("upper_key")) <<
|
||||
") && " <<
|
||||
"left_idx < right_idx" <<
|
||||
")" <<
|
||||
"{\n" <<
|
||||
k.decl<uint_>("mid_idx") << " = (left_idx + right_idx) / 2;\n" <<
|
||||
k.decl<key_type>("mid_key") << " = " <<
|
||||
keys_first[k.var<const uint_>("mid_idx")] << ";\n" <<
|
||||
k.decl<bool>("equal") << " = " <<
|
||||
"!(" << compare(k.var<key_type>("mid_key"),
|
||||
k.var<key_type>("my_key")) <<
|
||||
") && " <<
|
||||
"!(" << compare(k.var<key_type>("my_key"),
|
||||
k.var<key_type>("mid_key")) <<
|
||||
");\n" <<
|
||||
"left_idx = equal ? mid_idx + 1 : left_idx + 1;\n" <<
|
||||
"right_idx = equal ? right_idx : mid_idx;\n" <<
|
||||
"upper_key = equal ? upper_key : " <<
|
||||
keys_first[k.var<const uint_>("left_idx")] << ";\n" <<
|
||||
"}\n" <<
|
||||
"}\n" <<
|
||||
|
||||
k.decl<uint_>("offset") << " = 0;\n" <<
|
||||
"offset += gid - my_block_start;\n" <<
|
||||
"offset += left_idx - other_block_start;\n" <<
|
||||
"offset += min(my_block_start, other_block_start);\n" <<
|
||||
out_keys_first[k.var<uint_>("offset")] << " = my_key;\n";
|
||||
if(sort_by_key) {
|
||||
k <<
|
||||
out_values_first[k.var<uint_>("offset")] << " = my_value;\n";
|
||||
}
|
||||
|
||||
const context &context = queue.get_context();
|
||||
::boost::compute::kernel kernel = k.compile(context);
|
||||
|
||||
const size_t work_group_size = (std::min)(
|
||||
size_t(256),
|
||||
kernel.get_work_group_info<size_t>(
|
||||
queue.get_device(), CL_KERNEL_WORK_GROUP_SIZE
|
||||
)
|
||||
);
|
||||
const size_t global_size =
|
||||
work_group_size * static_cast<size_t>(
|
||||
std::ceil(float(count) / work_group_size)
|
||||
);
|
||||
|
||||
kernel.set_arg(count_arg, static_cast<uint_>(count));
|
||||
kernel.set_arg(block_size_arg, static_cast<uint_>(block_size));
|
||||
queue.enqueue_1d_range_kernel(kernel, 0, global_size, work_group_size);
|
||||
}
|
||||
|
||||
template<class KeyIterator, class ValueIterator, class Compare>
|
||||
inline void merge_sort_by_key_on_gpu(KeyIterator keys_first,
|
||||
KeyIterator keys_last,
|
||||
ValueIterator values_first,
|
||||
Compare compare,
|
||||
bool stable,
|
||||
command_queue &queue)
|
||||
{
|
||||
typedef typename std::iterator_traits<KeyIterator>::value_type key_type;
|
||||
typedef typename std::iterator_traits<ValueIterator>::value_type value_type;
|
||||
|
||||
size_t count = iterator_range_size(keys_first, keys_last);
|
||||
if(count < 2){
|
||||
return;
|
||||
}
|
||||
|
||||
size_t block_size =
|
||||
block_sort(
|
||||
keys_first, values_first,
|
||||
compare, count,
|
||||
true /* sort_by_key */, stable /* stable */,
|
||||
queue
|
||||
);
|
||||
|
||||
// for small input size only block sort is performed
|
||||
if(count <= block_size) {
|
||||
return;
|
||||
}
|
||||
|
||||
const context &context = queue.get_context();
|
||||
|
||||
bool result_in_temporary_buffer = false;
|
||||
::boost::compute::vector<key_type> temp_keys(count, context);
|
||||
::boost::compute::vector<value_type> temp_values(count, context);
|
||||
|
||||
for(; block_size < count; block_size *= 2) {
|
||||
result_in_temporary_buffer = !result_in_temporary_buffer;
|
||||
if(result_in_temporary_buffer) {
|
||||
merge_blocks_on_gpu(keys_first, values_first,
|
||||
temp_keys.begin(), temp_values.begin(),
|
||||
compare, count, block_size,
|
||||
true /* sort_by_key */, queue);
|
||||
} else {
|
||||
merge_blocks_on_gpu(temp_keys.begin(), temp_values.begin(),
|
||||
keys_first, values_first,
|
||||
compare, count, block_size,
|
||||
true /* sort_by_key */, queue);
|
||||
}
|
||||
}
|
||||
|
||||
if(result_in_temporary_buffer) {
|
||||
copy_async(temp_keys.begin(), temp_keys.end(), keys_first, queue);
|
||||
copy_async(temp_values.begin(), temp_values.end(), values_first, queue);
|
||||
}
|
||||
}
|
||||
|
||||
template<class Iterator, class Compare>
|
||||
inline void merge_sort_on_gpu(Iterator first,
|
||||
Iterator last,
|
||||
Compare compare,
|
||||
bool stable,
|
||||
command_queue &queue)
|
||||
{
|
||||
typedef typename std::iterator_traits<Iterator>::value_type key_type;
|
||||
|
||||
size_t count = iterator_range_size(first, last);
|
||||
if(count < 2){
|
||||
return;
|
||||
}
|
||||
|
||||
Iterator dummy;
|
||||
size_t block_size =
|
||||
block_sort(
|
||||
first, dummy,
|
||||
compare, count,
|
||||
false /* sort_by_key */, stable /* stable */,
|
||||
queue
|
||||
);
|
||||
|
||||
// for small input size only block sort is performed
|
||||
if(count <= block_size) {
|
||||
return;
|
||||
}
|
||||
|
||||
const context &context = queue.get_context();
|
||||
|
||||
bool result_in_temporary_buffer = false;
|
||||
::boost::compute::vector<key_type> temp_keys(count, context);
|
||||
|
||||
for(; block_size < count; block_size *= 2) {
|
||||
result_in_temporary_buffer = !result_in_temporary_buffer;
|
||||
if(result_in_temporary_buffer) {
|
||||
merge_blocks_on_gpu(first, dummy, temp_keys.begin(), dummy,
|
||||
compare, count, block_size,
|
||||
false /* sort_by_key */, queue);
|
||||
} else {
|
||||
merge_blocks_on_gpu(temp_keys.begin(), dummy, first, dummy,
|
||||
compare, count, block_size,
|
||||
false /* sort_by_key */, queue);
|
||||
}
|
||||
}
|
||||
|
||||
if(result_in_temporary_buffer) {
|
||||
copy_async(temp_keys.begin(), temp_keys.end(), first, queue);
|
||||
}
|
||||
}
|
||||
|
||||
template<class KeyIterator, class ValueIterator, class Compare>
|
||||
inline void merge_sort_by_key_on_gpu(KeyIterator keys_first,
|
||||
KeyIterator keys_last,
|
||||
ValueIterator values_first,
|
||||
Compare compare,
|
||||
command_queue &queue)
|
||||
{
|
||||
merge_sort_by_key_on_gpu(
|
||||
keys_first, keys_last, values_first,
|
||||
compare, false /* not stable */, queue
|
||||
);
|
||||
}
|
||||
|
||||
template<class Iterator, class Compare>
|
||||
inline void merge_sort_on_gpu(Iterator first,
|
||||
Iterator last,
|
||||
Compare compare,
|
||||
command_queue &queue)
|
||||
{
|
||||
merge_sort_on_gpu(
|
||||
first, last, compare, false /* not stable */, queue
|
||||
);
|
||||
}
|
||||
|
||||
} // end detail namespace
|
||||
} // end compute namespace
|
||||
} // end boost namespace
|
||||
|
||||
#endif /* BOOST_COMPUTE_ALGORITHM_DETAIL_MERGE_SORT_ON_GPU_HPP_ */
|
||||
+203
@@ -0,0 +1,203 @@
|
||||
//---------------------------------------------------------------------------//
|
||||
// Copyright (c) 2014 Roshan <thisisroshansmail@gmail.com>
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0
|
||||
// See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt
|
||||
//
|
||||
// See http://boostorg.github.com/compute for more information.
|
||||
//---------------------------------------------------------------------------//
|
||||
|
||||
#ifndef BOOST_COMPUTE_ALGORITHM_DETAIL_MERGE_WIH_MERGE_PATH_HPP
|
||||
#define BOOST_COMPUTE_ALGORITHM_DETAIL_MERGE_WIH_MERGE_PATH_HPP
|
||||
|
||||
#include <iterator>
|
||||
|
||||
#include <boost/compute/algorithm/detail/merge_path.hpp>
|
||||
#include <boost/compute/algorithm/fill_n.hpp>
|
||||
#include <boost/compute/container/vector.hpp>
|
||||
#include <boost/compute/detail/iterator_range_size.hpp>
|
||||
#include <boost/compute/detail/meta_kernel.hpp>
|
||||
#include <boost/compute/system.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace compute {
|
||||
namespace detail {
|
||||
|
||||
///
|
||||
/// \brief Serial merge kernel class
|
||||
///
|
||||
/// Subclass of meta_kernel to perform serial merge after tiling
|
||||
///
|
||||
class serial_merge_kernel : meta_kernel
|
||||
{
|
||||
public:
|
||||
unsigned int tile_size;
|
||||
|
||||
serial_merge_kernel() : meta_kernel("merge")
|
||||
{
|
||||
tile_size = 4;
|
||||
}
|
||||
|
||||
template<class InputIterator1, class InputIterator2,
|
||||
class InputIterator3, class InputIterator4,
|
||||
class OutputIterator, class Compare>
|
||||
void set_range(InputIterator1 first1,
|
||||
InputIterator2 first2,
|
||||
InputIterator3 tile_first1,
|
||||
InputIterator3 tile_last1,
|
||||
InputIterator4 tile_first2,
|
||||
OutputIterator result,
|
||||
Compare comp)
|
||||
{
|
||||
m_count = iterator_range_size(tile_first1, tile_last1) - 1;
|
||||
|
||||
*this <<
|
||||
"uint i = get_global_id(0);\n" <<
|
||||
"uint start1 = " << tile_first1[expr<uint_>("i")] << ";\n" <<
|
||||
"uint end1 = " << tile_first1[expr<uint_>("i+1")] << ";\n" <<
|
||||
"uint start2 = " << tile_first2[expr<uint_>("i")] << ";\n" <<
|
||||
"uint end2 = " << tile_first2[expr<uint_>("i+1")] << ";\n" <<
|
||||
"uint index = i*" << tile_size << ";\n" <<
|
||||
"while(start1<end1 && start2<end2)\n" <<
|
||||
"{\n" <<
|
||||
" if(!(" << comp(first2[expr<uint_>("start2")],
|
||||
first1[expr<uint_>("start1")]) << "))\n" <<
|
||||
" {\n" <<
|
||||
result[expr<uint_>("index")] <<
|
||||
" = " << first1[expr<uint_>("start1")] << ";\n" <<
|
||||
" index++;\n" <<
|
||||
" start1++;\n" <<
|
||||
" }\n" <<
|
||||
" else\n" <<
|
||||
" {\n" <<
|
||||
result[expr<uint_>("index")] <<
|
||||
" = " << first2[expr<uint_>("start2")] << ";\n" <<
|
||||
" index++;\n" <<
|
||||
" start2++;\n" <<
|
||||
" }\n" <<
|
||||
"}\n" <<
|
||||
"while(start1<end1)\n" <<
|
||||
"{\n" <<
|
||||
result[expr<uint_>("index")] <<
|
||||
" = " << first1[expr<uint_>("start1")] << ";\n" <<
|
||||
" index++;\n" <<
|
||||
" start1++;\n" <<
|
||||
"}\n" <<
|
||||
"while(start2<end2)\n" <<
|
||||
"{\n" <<
|
||||
result[expr<uint_>("index")] <<
|
||||
" = " << first2[expr<uint_>("start2")] << ";\n" <<
|
||||
" index++;\n" <<
|
||||
" start2++;\n" <<
|
||||
"}\n";
|
||||
}
|
||||
|
||||
template<class InputIterator1, class InputIterator2,
|
||||
class InputIterator3, class InputIterator4,
|
||||
class OutputIterator>
|
||||
void set_range(InputIterator1 first1,
|
||||
InputIterator2 first2,
|
||||
InputIterator3 tile_first1,
|
||||
InputIterator3 tile_last1,
|
||||
InputIterator4 tile_first2,
|
||||
OutputIterator result)
|
||||
{
|
||||
typedef typename std::iterator_traits<InputIterator1>::value_type value_type;
|
||||
::boost::compute::less<value_type> less_than;
|
||||
set_range(first1, first2, tile_first1, tile_last1, tile_first2, result, less_than);
|
||||
}
|
||||
|
||||
event exec(command_queue &queue)
|
||||
{
|
||||
if(m_count == 0) {
|
||||
return event();
|
||||
}
|
||||
|
||||
return exec_1d(queue, 0, m_count);
|
||||
}
|
||||
|
||||
private:
|
||||
size_t m_count;
|
||||
};
|
||||
|
||||
///
|
||||
/// \brief Merge algorithm with merge path
|
||||
///
|
||||
/// Merges the sorted values in the range [\p first1, \p last1) with
|
||||
/// the sorted values in the range [\p first2, last2) and stores the
|
||||
/// result in the range beginning at \p result
|
||||
///
|
||||
/// \param first1 Iterator pointing to start of first set
|
||||
/// \param last1 Iterator pointing to end of first set
|
||||
/// \param first2 Iterator pointing to start of second set
|
||||
/// \param last2 Iterator pointing to end of second set
|
||||
/// \param result Iterator pointing to start of range in which the result
|
||||
/// will be stored
|
||||
/// \param comp Comparator which performs less than function
|
||||
/// \param queue Queue on which to execute
|
||||
///
|
||||
template<class InputIterator1, class InputIterator2, class OutputIterator, class Compare>
|
||||
inline OutputIterator
|
||||
merge_with_merge_path(InputIterator1 first1,
|
||||
InputIterator1 last1,
|
||||
InputIterator2 first2,
|
||||
InputIterator2 last2,
|
||||
OutputIterator result,
|
||||
Compare comp,
|
||||
command_queue &queue = system::default_queue())
|
||||
{
|
||||
typedef typename
|
||||
std::iterator_traits<OutputIterator>::difference_type result_difference_type;
|
||||
|
||||
size_t tile_size = 1024;
|
||||
|
||||
size_t count1 = iterator_range_size(first1, last1);
|
||||
size_t count2 = iterator_range_size(first2, last2);
|
||||
|
||||
vector<uint_> tile_a((count1+count2+tile_size-1)/tile_size+1, queue.get_context());
|
||||
vector<uint_> tile_b((count1+count2+tile_size-1)/tile_size+1, queue.get_context());
|
||||
|
||||
// Tile the sets
|
||||
merge_path_kernel tiling_kernel;
|
||||
tiling_kernel.tile_size = static_cast<unsigned int>(tile_size);
|
||||
tiling_kernel.set_range(first1, last1, first2, last2,
|
||||
tile_a.begin()+1, tile_b.begin()+1, comp);
|
||||
fill_n(tile_a.begin(), 1, uint_(0), queue);
|
||||
fill_n(tile_b.begin(), 1, uint_(0), queue);
|
||||
tiling_kernel.exec(queue);
|
||||
|
||||
fill_n(tile_a.end()-1, 1, static_cast<uint_>(count1), queue);
|
||||
fill_n(tile_b.end()-1, 1, static_cast<uint_>(count2), queue);
|
||||
|
||||
// Merge
|
||||
serial_merge_kernel merge_kernel;
|
||||
merge_kernel.tile_size = static_cast<unsigned int>(tile_size);
|
||||
merge_kernel.set_range(first1, first2, tile_a.begin(), tile_a.end(),
|
||||
tile_b.begin(), result, comp);
|
||||
|
||||
merge_kernel.exec(queue);
|
||||
|
||||
return result + static_cast<result_difference_type>(count1 + count2);
|
||||
}
|
||||
|
||||
/// \overload
|
||||
template<class InputIterator1, class InputIterator2, class OutputIterator>
|
||||
inline OutputIterator
|
||||
merge_with_merge_path(InputIterator1 first1,
|
||||
InputIterator1 last1,
|
||||
InputIterator2 first2,
|
||||
InputIterator2 last2,
|
||||
OutputIterator result,
|
||||
command_queue &queue = system::default_queue())
|
||||
{
|
||||
typedef typename std::iterator_traits<InputIterator1>::value_type value_type;
|
||||
::boost::compute::less<value_type> less_than;
|
||||
return merge_with_merge_path(first1, last1, first2, last2, result, less_than, queue);
|
||||
}
|
||||
|
||||
} //end detail namespace
|
||||
} //end compute namespace
|
||||
} //end boost namespace
|
||||
|
||||
#endif // BOOST_COMPUTE_ALGORITHM_DETAIL_MERGE_WIH_MERGE_PATH_HPP
|
||||
@@ -0,0 +1,461 @@
|
||||
//---------------------------------------------------------------------------//
|
||||
// Copyright (c) 2013 Kyle Lutz <kyle.r.lutz@gmail.com>
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0
|
||||
// See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt
|
||||
//
|
||||
// See http://boostorg.github.com/compute for more information.
|
||||
//---------------------------------------------------------------------------//
|
||||
|
||||
#ifndef BOOST_COMPUTE_ALGORITHM_DETAIL_RADIX_SORT_HPP
|
||||
#define BOOST_COMPUTE_ALGORITHM_DETAIL_RADIX_SORT_HPP
|
||||
|
||||
#include <iterator>
|
||||
|
||||
#include <boost/assert.hpp>
|
||||
#include <boost/type_traits/is_signed.hpp>
|
||||
#include <boost/type_traits/is_floating_point.hpp>
|
||||
|
||||
#include <boost/compute/kernel.hpp>
|
||||
#include <boost/compute/program.hpp>
|
||||
#include <boost/compute/command_queue.hpp>
|
||||
#include <boost/compute/algorithm/exclusive_scan.hpp>
|
||||
#include <boost/compute/container/vector.hpp>
|
||||
#include <boost/compute/detail/iterator_range_size.hpp>
|
||||
#include <boost/compute/detail/parameter_cache.hpp>
|
||||
#include <boost/compute/type_traits/type_name.hpp>
|
||||
#include <boost/compute/type_traits/is_fundamental.hpp>
|
||||
#include <boost/compute/type_traits/is_vector_type.hpp>
|
||||
#include <boost/compute/utility/program_cache.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace compute {
|
||||
namespace detail {
|
||||
|
||||
// meta-function returning true if type T is radix-sortable
|
||||
template<class T>
|
||||
struct is_radix_sortable :
|
||||
boost::mpl::and_<
|
||||
typename ::boost::compute::is_fundamental<T>::type,
|
||||
typename boost::mpl::not_<typename is_vector_type<T>::type>::type
|
||||
>
|
||||
{
|
||||
};
|
||||
|
||||
template<size_t N>
|
||||
struct radix_sort_value_type
|
||||
{
|
||||
};
|
||||
|
||||
template<>
|
||||
struct radix_sort_value_type<1>
|
||||
{
|
||||
typedef uchar_ type;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct radix_sort_value_type<2>
|
||||
{
|
||||
typedef ushort_ type;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct radix_sort_value_type<4>
|
||||
{
|
||||
typedef uint_ type;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct radix_sort_value_type<8>
|
||||
{
|
||||
typedef ulong_ type;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
inline const char* enable_double()
|
||||
{
|
||||
return " -DT2_double=0";
|
||||
}
|
||||
|
||||
template<>
|
||||
inline const char* enable_double<double>()
|
||||
{
|
||||
return " -DT2_double=1";
|
||||
}
|
||||
|
||||
const char radix_sort_source[] =
|
||||
"#if T2_double\n"
|
||||
"#pragma OPENCL EXTENSION cl_khr_fp64 : enable\n"
|
||||
"#endif\n"
|
||||
"#define K2_BITS (1 << K_BITS)\n"
|
||||
"#define RADIX_MASK ((((T)(1)) << K_BITS) - 1)\n"
|
||||
"#define SIGN_BIT ((sizeof(T) * CHAR_BIT) - 1)\n"
|
||||
|
||||
"#if defined(ASC)\n" // asc order
|
||||
|
||||
"inline uint radix(const T x, const uint low_bit)\n"
|
||||
"{\n"
|
||||
"#if defined(IS_FLOATING_POINT)\n"
|
||||
" const T mask = -(x >> SIGN_BIT) | (((T)(1)) << SIGN_BIT);\n"
|
||||
" return ((x ^ mask) >> low_bit) & RADIX_MASK;\n"
|
||||
"#elif defined(IS_SIGNED)\n"
|
||||
" return ((x ^ (((T)(1)) << SIGN_BIT)) >> low_bit) & RADIX_MASK;\n"
|
||||
"#else\n"
|
||||
" return (x >> low_bit) & RADIX_MASK;\n"
|
||||
"#endif\n"
|
||||
"}\n"
|
||||
|
||||
"#else\n" // desc order
|
||||
|
||||
// For signed types we just negate the x and for unsigned types we
|
||||
// subtract the x from max value of its type ((T)(-1) is a max value
|
||||
// of type T when T is an unsigned type).
|
||||
"inline uint radix(const T x, const uint low_bit)\n"
|
||||
"{\n"
|
||||
"#if defined(IS_FLOATING_POINT)\n"
|
||||
" const T mask = -(x >> SIGN_BIT) | (((T)(1)) << SIGN_BIT);\n"
|
||||
" return (((-x) ^ mask) >> low_bit) & RADIX_MASK;\n"
|
||||
"#elif defined(IS_SIGNED)\n"
|
||||
" return (((-x) ^ (((T)(1)) << SIGN_BIT)) >> low_bit) & RADIX_MASK;\n"
|
||||
"#else\n"
|
||||
" return (((T)(-1) - x) >> low_bit) & RADIX_MASK;\n"
|
||||
"#endif\n"
|
||||
"}\n"
|
||||
|
||||
"#endif\n" // #if defined(ASC)
|
||||
|
||||
"__kernel void count(__global const T *input,\n"
|
||||
" const uint input_offset,\n"
|
||||
" const uint input_size,\n"
|
||||
" __global uint *global_counts,\n"
|
||||
" __global uint *global_offsets,\n"
|
||||
" __local uint *local_counts,\n"
|
||||
" const uint low_bit)\n"
|
||||
"{\n"
|
||||
// work-item parameters
|
||||
" const uint gid = get_global_id(0);\n"
|
||||
" const uint lid = get_local_id(0);\n"
|
||||
|
||||
// zero local counts
|
||||
" if(lid < K2_BITS){\n"
|
||||
" local_counts[lid] = 0;\n"
|
||||
" }\n"
|
||||
" barrier(CLK_LOCAL_MEM_FENCE);\n"
|
||||
|
||||
// reduce local counts
|
||||
" if(gid < input_size){\n"
|
||||
" T value = input[input_offset+gid];\n"
|
||||
" uint bucket = radix(value, low_bit);\n"
|
||||
" atomic_inc(local_counts + bucket);\n"
|
||||
" }\n"
|
||||
" barrier(CLK_LOCAL_MEM_FENCE);\n"
|
||||
|
||||
// write block-relative offsets
|
||||
" if(lid < K2_BITS){\n"
|
||||
" global_counts[K2_BITS*get_group_id(0) + lid] = local_counts[lid];\n"
|
||||
|
||||
// write global offsets
|
||||
" if(get_group_id(0) == (get_num_groups(0) - 1)){\n"
|
||||
" global_offsets[lid] = local_counts[lid];\n"
|
||||
" }\n"
|
||||
" }\n"
|
||||
"}\n"
|
||||
|
||||
"__kernel void scan(__global const uint *block_offsets,\n"
|
||||
" __global uint *global_offsets,\n"
|
||||
" const uint block_count)\n"
|
||||
"{\n"
|
||||
" __global const uint *last_block_offsets =\n"
|
||||
" block_offsets + K2_BITS * (block_count - 1);\n"
|
||||
|
||||
// calculate and scan global_offsets
|
||||
" uint sum = 0;\n"
|
||||
" for(uint i = 0; i < K2_BITS; i++){\n"
|
||||
" uint x = global_offsets[i] + last_block_offsets[i];\n"
|
||||
" global_offsets[i] = sum;\n"
|
||||
" sum += x;\n"
|
||||
" }\n"
|
||||
"}\n"
|
||||
|
||||
"__kernel void scatter(__global const T *input,\n"
|
||||
" const uint input_offset,\n"
|
||||
" const uint input_size,\n"
|
||||
" const uint low_bit,\n"
|
||||
" __global const uint *counts,\n"
|
||||
" __global const uint *global_offsets,\n"
|
||||
"#ifndef SORT_BY_KEY\n"
|
||||
" __global T *output,\n"
|
||||
" const uint output_offset)\n"
|
||||
"#else\n"
|
||||
" __global T *keys_output,\n"
|
||||
" const uint keys_output_offset,\n"
|
||||
" __global T2 *values_input,\n"
|
||||
" const uint values_input_offset,\n"
|
||||
" __global T2 *values_output,\n"
|
||||
" const uint values_output_offset)\n"
|
||||
"#endif\n"
|
||||
"{\n"
|
||||
// work-item parameters
|
||||
" const uint gid = get_global_id(0);\n"
|
||||
" const uint lid = get_local_id(0);\n"
|
||||
|
||||
// copy input to local memory
|
||||
" T value;\n"
|
||||
" uint bucket;\n"
|
||||
" __local uint local_input[BLOCK_SIZE];\n"
|
||||
" if(gid < input_size){\n"
|
||||
" value = input[input_offset+gid];\n"
|
||||
" bucket = radix(value, low_bit);\n"
|
||||
" local_input[lid] = bucket;\n"
|
||||
" }\n"
|
||||
|
||||
// copy block counts to local memory
|
||||
" __local uint local_counts[(1 << K_BITS)];\n"
|
||||
" if(lid < K2_BITS){\n"
|
||||
" local_counts[lid] = counts[get_group_id(0) * K2_BITS + lid];\n"
|
||||
" }\n"
|
||||
|
||||
// wait until local memory is ready
|
||||
" barrier(CLK_LOCAL_MEM_FENCE);\n"
|
||||
|
||||
" if(gid >= input_size){\n"
|
||||
" return;\n"
|
||||
" }\n"
|
||||
|
||||
// get global offset
|
||||
" uint offset = global_offsets[bucket] + local_counts[bucket];\n"
|
||||
|
||||
// calculate local offset
|
||||
" uint local_offset = 0;\n"
|
||||
" for(uint i = 0; i < lid; i++){\n"
|
||||
" if(local_input[i] == bucket)\n"
|
||||
" local_offset++;\n"
|
||||
" }\n"
|
||||
|
||||
"#ifndef SORT_BY_KEY\n"
|
||||
// write value to output
|
||||
" output[output_offset + offset + local_offset] = value;\n"
|
||||
"#else\n"
|
||||
// write key and value if doing sort_by_key
|
||||
" keys_output[keys_output_offset+offset + local_offset] = value;\n"
|
||||
" values_output[values_output_offset+offset + local_offset] =\n"
|
||||
" values_input[values_input_offset+gid];\n"
|
||||
"#endif\n"
|
||||
"}\n";
|
||||
|
||||
template<class T, class T2>
|
||||
inline void radix_sort_impl(const buffer_iterator<T> first,
|
||||
const buffer_iterator<T> last,
|
||||
const buffer_iterator<T2> values_first,
|
||||
const bool ascending,
|
||||
command_queue &queue)
|
||||
{
|
||||
|
||||
typedef T value_type;
|
||||
typedef typename radix_sort_value_type<sizeof(T)>::type sort_type;
|
||||
|
||||
const device &device = queue.get_device();
|
||||
const context &context = queue.get_context();
|
||||
|
||||
|
||||
// if we have a valid values iterator then we are doing a
|
||||
// sort by key and have to set up the values buffer
|
||||
bool sort_by_key = (values_first.get_buffer().get() != 0);
|
||||
|
||||
// load (or create) radix sort program
|
||||
std::string cache_key =
|
||||
std::string("__boost_radix_sort_") + type_name<value_type>();
|
||||
|
||||
if(sort_by_key){
|
||||
cache_key += std::string("_with_") + type_name<T2>();
|
||||
}
|
||||
|
||||
boost::shared_ptr<program_cache> cache =
|
||||
program_cache::get_global_cache(context);
|
||||
boost::shared_ptr<parameter_cache> parameters =
|
||||
detail::parameter_cache::get_global_cache(device);
|
||||
|
||||
// sort parameters
|
||||
const uint_ k = parameters->get(cache_key, "k", 4);
|
||||
const uint_ k2 = 1 << k;
|
||||
const uint_ block_size = parameters->get(cache_key, "tpb", 128);
|
||||
|
||||
// sort program compiler options
|
||||
std::stringstream options;
|
||||
options << "-DK_BITS=" << k;
|
||||
options << " -DT=" << type_name<sort_type>();
|
||||
options << " -DBLOCK_SIZE=" << block_size;
|
||||
|
||||
if(boost::is_floating_point<value_type>::value){
|
||||
options << " -DIS_FLOATING_POINT";
|
||||
}
|
||||
|
||||
if(boost::is_signed<value_type>::value){
|
||||
options << " -DIS_SIGNED";
|
||||
}
|
||||
|
||||
if(sort_by_key){
|
||||
options << " -DSORT_BY_KEY";
|
||||
options << " -DT2=" << type_name<T2>();
|
||||
options << enable_double<T2>();
|
||||
}
|
||||
|
||||
if(ascending){
|
||||
options << " -DASC";
|
||||
}
|
||||
|
||||
// load radix sort program
|
||||
program radix_sort_program = cache->get_or_build(
|
||||
cache_key, options.str(), radix_sort_source, context
|
||||
);
|
||||
|
||||
kernel count_kernel(radix_sort_program, "count");
|
||||
kernel scan_kernel(radix_sort_program, "scan");
|
||||
kernel scatter_kernel(radix_sort_program, "scatter");
|
||||
|
||||
size_t count = detail::iterator_range_size(first, last);
|
||||
|
||||
uint_ block_count = static_cast<uint_>(count / block_size);
|
||||
if(block_count * block_size != count){
|
||||
block_count++;
|
||||
}
|
||||
|
||||
// setup temporary buffers
|
||||
vector<value_type> output(count, context);
|
||||
vector<T2> values_output(sort_by_key ? count : 0, context);
|
||||
vector<uint_> offsets(k2, context);
|
||||
vector<uint_> counts(block_count * k2, context);
|
||||
|
||||
const buffer *input_buffer = &first.get_buffer();
|
||||
uint_ input_offset = static_cast<uint_>(first.get_index());
|
||||
const buffer *output_buffer = &output.get_buffer();
|
||||
uint_ output_offset = 0;
|
||||
const buffer *values_input_buffer = &values_first.get_buffer();
|
||||
uint_ values_input_offset = static_cast<uint_>(values_first.get_index());
|
||||
const buffer *values_output_buffer = &values_output.get_buffer();
|
||||
uint_ values_output_offset = 0;
|
||||
|
||||
for(uint_ i = 0; i < sizeof(sort_type) * CHAR_BIT / k; i++){
|
||||
// write counts
|
||||
count_kernel.set_arg(0, *input_buffer);
|
||||
count_kernel.set_arg(1, input_offset);
|
||||
count_kernel.set_arg(2, static_cast<uint_>(count));
|
||||
count_kernel.set_arg(3, counts);
|
||||
count_kernel.set_arg(4, offsets);
|
||||
count_kernel.set_arg(5, block_size * sizeof(uint_), 0);
|
||||
count_kernel.set_arg(6, i * k);
|
||||
queue.enqueue_1d_range_kernel(count_kernel,
|
||||
0,
|
||||
block_count * block_size,
|
||||
block_size);
|
||||
|
||||
// scan counts
|
||||
if(k == 1){
|
||||
typedef uint2_ counter_type;
|
||||
::boost::compute::exclusive_scan(
|
||||
make_buffer_iterator<counter_type>(counts.get_buffer(), 0),
|
||||
make_buffer_iterator<counter_type>(counts.get_buffer(), counts.size() / 2),
|
||||
make_buffer_iterator<counter_type>(counts.get_buffer()),
|
||||
queue
|
||||
);
|
||||
}
|
||||
else if(k == 2){
|
||||
typedef uint4_ counter_type;
|
||||
::boost::compute::exclusive_scan(
|
||||
make_buffer_iterator<counter_type>(counts.get_buffer(), 0),
|
||||
make_buffer_iterator<counter_type>(counts.get_buffer(), counts.size() / 4),
|
||||
make_buffer_iterator<counter_type>(counts.get_buffer()),
|
||||
queue
|
||||
);
|
||||
}
|
||||
else if(k == 4){
|
||||
typedef uint16_ counter_type;
|
||||
::boost::compute::exclusive_scan(
|
||||
make_buffer_iterator<counter_type>(counts.get_buffer(), 0),
|
||||
make_buffer_iterator<counter_type>(counts.get_buffer(), counts.size() / 16),
|
||||
make_buffer_iterator<counter_type>(counts.get_buffer()),
|
||||
queue
|
||||
);
|
||||
}
|
||||
else {
|
||||
BOOST_ASSERT(false && "unknown k");
|
||||
break;
|
||||
}
|
||||
|
||||
// scan global offsets
|
||||
scan_kernel.set_arg(0, counts);
|
||||
scan_kernel.set_arg(1, offsets);
|
||||
scan_kernel.set_arg(2, block_count);
|
||||
queue.enqueue_task(scan_kernel);
|
||||
|
||||
// scatter values
|
||||
scatter_kernel.set_arg(0, *input_buffer);
|
||||
scatter_kernel.set_arg(1, input_offset);
|
||||
scatter_kernel.set_arg(2, static_cast<uint_>(count));
|
||||
scatter_kernel.set_arg(3, i * k);
|
||||
scatter_kernel.set_arg(4, counts);
|
||||
scatter_kernel.set_arg(5, offsets);
|
||||
scatter_kernel.set_arg(6, *output_buffer);
|
||||
scatter_kernel.set_arg(7, output_offset);
|
||||
if(sort_by_key){
|
||||
scatter_kernel.set_arg(8, *values_input_buffer);
|
||||
scatter_kernel.set_arg(9, values_input_offset);
|
||||
scatter_kernel.set_arg(10, *values_output_buffer);
|
||||
scatter_kernel.set_arg(11, values_output_offset);
|
||||
}
|
||||
queue.enqueue_1d_range_kernel(scatter_kernel,
|
||||
0,
|
||||
block_count * block_size,
|
||||
block_size);
|
||||
|
||||
// swap buffers
|
||||
std::swap(input_buffer, output_buffer);
|
||||
std::swap(values_input_buffer, values_output_buffer);
|
||||
std::swap(input_offset, output_offset);
|
||||
std::swap(values_input_offset, values_output_offset);
|
||||
}
|
||||
}
|
||||
|
||||
template<class Iterator>
|
||||
inline void radix_sort(Iterator first,
|
||||
Iterator last,
|
||||
command_queue &queue)
|
||||
{
|
||||
radix_sort_impl(first, last, buffer_iterator<int>(), true, queue);
|
||||
}
|
||||
|
||||
template<class KeyIterator, class ValueIterator>
|
||||
inline void radix_sort_by_key(KeyIterator keys_first,
|
||||
KeyIterator keys_last,
|
||||
ValueIterator values_first,
|
||||
command_queue &queue)
|
||||
{
|
||||
radix_sort_impl(keys_first, keys_last, values_first, true, queue);
|
||||
}
|
||||
|
||||
template<class Iterator>
|
||||
inline void radix_sort(Iterator first,
|
||||
Iterator last,
|
||||
const bool ascending,
|
||||
command_queue &queue)
|
||||
{
|
||||
radix_sort_impl(first, last, buffer_iterator<int>(), ascending, queue);
|
||||
}
|
||||
|
||||
template<class KeyIterator, class ValueIterator>
|
||||
inline void radix_sort_by_key(KeyIterator keys_first,
|
||||
KeyIterator keys_last,
|
||||
ValueIterator values_first,
|
||||
const bool ascending,
|
||||
command_queue &queue)
|
||||
{
|
||||
radix_sort_impl(keys_first, keys_last, values_first, ascending, queue);
|
||||
}
|
||||
|
||||
|
||||
} // end detail namespace
|
||||
} // end compute namespace
|
||||
} // end boost namespace
|
||||
|
||||
#endif // BOOST_COMPUTE_ALGORITHM_DETAIL_RADIX_SORT_HPP
|
||||
@@ -0,0 +1,57 @@
|
||||
//---------------------------------------------------------------------------//
|
||||
// Copyright (c) 2013 Kyle Lutz <kyle.r.lutz@gmail.com>
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0
|
||||
// See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt
|
||||
//
|
||||
// See http://boostorg.github.com/compute for more information.
|
||||
//---------------------------------------------------------------------------//
|
||||
|
||||
#ifndef BOOST_COMPUTE_ALGORITHM_DETAIL_RANDOM_FILL_HPP
|
||||
#define BOOST_COMPUTE_ALGORITHM_DETAIL_RANDOM_FILL_HPP
|
||||
|
||||
#include <iterator>
|
||||
|
||||
#include <boost/compute/command_queue.hpp>
|
||||
#include <boost/compute/random/default_random_engine.hpp>
|
||||
#include <boost/compute/random/uniform_real_distribution.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace compute {
|
||||
namespace detail {
|
||||
|
||||
template<class OutputIterator, class Generator>
|
||||
inline void random_fill(OutputIterator first,
|
||||
OutputIterator last,
|
||||
Generator &g,
|
||||
command_queue &queue)
|
||||
{
|
||||
g.fill(first, last, queue);
|
||||
}
|
||||
|
||||
template<class OutputIterator>
|
||||
inline void
|
||||
random_fill(OutputIterator first,
|
||||
OutputIterator last,
|
||||
typename std::iterator_traits<OutputIterator>::value_type lo,
|
||||
typename std::iterator_traits<OutputIterator>::value_type hi,
|
||||
command_queue &queue)
|
||||
{
|
||||
typedef typename
|
||||
std::iterator_traits<OutputIterator>::value_type value_type;
|
||||
typedef typename
|
||||
boost::compute::default_random_engine engine_type;
|
||||
typedef typename
|
||||
boost::compute::uniform_real_distribution<value_type> distribution_type;
|
||||
|
||||
engine_type engine(queue);
|
||||
distribution_type generator(lo, hi);
|
||||
generator.fill(first, last, engine, queue);
|
||||
}
|
||||
|
||||
} // end detail namespace
|
||||
} // end compute namespace
|
||||
} // end boost namespace
|
||||
|
||||
#endif // BOOST_COMPUTE_ALGORITHM_DETAIL_RANDOM_FILL_HPP
|
||||
@@ -0,0 +1,119 @@
|
||||
//---------------------------------------------------------------------------//
|
||||
// Copyright (c) 2015 Jakub Szuppe <j.szuppe@gmail.com>
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0
|
||||
// See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt
|
||||
//
|
||||
// See http://boostorg.github.com/compute for more information.
|
||||
//---------------------------------------------------------------------------//
|
||||
|
||||
#ifndef BOOST_COMPUTE_ALGORITHM_DETAIL_REDUCE_BY_KEY_HPP
|
||||
#define BOOST_COMPUTE_ALGORITHM_DETAIL_REDUCE_BY_KEY_HPP
|
||||
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
|
||||
#include <boost/compute/command_queue.hpp>
|
||||
#include <boost/compute/functional.hpp>
|
||||
#include <boost/compute/container/vector.hpp>
|
||||
#include <boost/compute/detail/iterator_range_size.hpp>
|
||||
#include <boost/compute/algorithm/detail/serial_reduce_by_key.hpp>
|
||||
#include <boost/compute/algorithm/detail/reduce_by_key_with_scan.hpp>
|
||||
#include <boost/compute/type_traits.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace compute {
|
||||
namespace detail {
|
||||
|
||||
template<class InputKeyIterator, class InputValueIterator,
|
||||
class OutputKeyIterator, class OutputValueIterator,
|
||||
class BinaryFunction, class BinaryPredicate>
|
||||
size_t reduce_by_key_on_gpu(InputKeyIterator keys_first,
|
||||
InputKeyIterator keys_last,
|
||||
InputValueIterator values_first,
|
||||
OutputKeyIterator keys_result,
|
||||
OutputValueIterator values_result,
|
||||
BinaryFunction function,
|
||||
BinaryPredicate predicate,
|
||||
command_queue &queue)
|
||||
{
|
||||
return detail::reduce_by_key_with_scan(keys_first, keys_last, values_first,
|
||||
keys_result, values_result, function,
|
||||
predicate, queue);
|
||||
}
|
||||
|
||||
template<class InputKeyIterator, class InputValueIterator,
|
||||
class OutputKeyIterator, class OutputValueIterator>
|
||||
bool reduce_by_key_on_gpu_requirements_met(InputKeyIterator keys_first,
|
||||
InputValueIterator values_first,
|
||||
OutputKeyIterator keys_result,
|
||||
OutputValueIterator values_result,
|
||||
const size_t count,
|
||||
command_queue &queue)
|
||||
{
|
||||
const device &device = queue.get_device();
|
||||
return (count > 256)
|
||||
&& !(device.type() & device::cpu)
|
||||
&& reduce_by_key_with_scan_requirements_met(keys_first, values_first,
|
||||
keys_result,values_result,
|
||||
count, queue);
|
||||
return true;
|
||||
}
|
||||
|
||||
template<class InputKeyIterator, class InputValueIterator,
|
||||
class OutputKeyIterator, class OutputValueIterator,
|
||||
class BinaryFunction, class BinaryPredicate>
|
||||
inline std::pair<OutputKeyIterator, OutputValueIterator>
|
||||
dispatch_reduce_by_key(InputKeyIterator keys_first,
|
||||
InputKeyIterator keys_last,
|
||||
InputValueIterator values_first,
|
||||
OutputKeyIterator keys_result,
|
||||
OutputValueIterator values_result,
|
||||
BinaryFunction function,
|
||||
BinaryPredicate predicate,
|
||||
command_queue &queue)
|
||||
{
|
||||
typedef typename
|
||||
std::iterator_traits<OutputKeyIterator>::difference_type key_difference_type;
|
||||
typedef typename
|
||||
std::iterator_traits<OutputValueIterator>::difference_type value_difference_type;
|
||||
|
||||
const size_t count = detail::iterator_range_size(keys_first, keys_last);
|
||||
if (count < 2) {
|
||||
boost::compute::copy_n(keys_first, count, keys_result, queue);
|
||||
boost::compute::copy_n(values_first, count, values_result, queue);
|
||||
return
|
||||
std::make_pair<OutputKeyIterator, OutputValueIterator>(
|
||||
keys_result + static_cast<key_difference_type>(count),
|
||||
values_result + static_cast<value_difference_type>(count)
|
||||
);
|
||||
}
|
||||
|
||||
size_t result_size = 0;
|
||||
if(reduce_by_key_on_gpu_requirements_met(keys_first, values_first, keys_result,
|
||||
values_result, count, queue)){
|
||||
result_size =
|
||||
detail::reduce_by_key_on_gpu(keys_first, keys_last, values_first,
|
||||
keys_result, values_result, function,
|
||||
predicate, queue);
|
||||
}
|
||||
else {
|
||||
result_size =
|
||||
detail::serial_reduce_by_key(keys_first, keys_last, values_first,
|
||||
keys_result, values_result, function,
|
||||
predicate, queue);
|
||||
}
|
||||
|
||||
return
|
||||
std::make_pair<OutputKeyIterator, OutputValueIterator>(
|
||||
keys_result + static_cast<key_difference_type>(result_size),
|
||||
values_result + static_cast<value_difference_type>(result_size)
|
||||
);
|
||||
}
|
||||
|
||||
} // end detail namespace
|
||||
} // end compute namespace
|
||||
} // end boost namespace
|
||||
|
||||
#endif // BOOST_COMPUTE_ALGORITHM_DETAIL_REDUCE_BY_KEY_HPP
|
||||
+541
@@ -0,0 +1,541 @@
|
||||
//---------------------------------------------------------------------------//
|
||||
// Copyright (c) 2015 Jakub Szuppe <j.szuppe@gmail.com>
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0
|
||||
// See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt
|
||||
//
|
||||
// See http://boostorg.github.com/compute for more information.
|
||||
//---------------------------------------------------------------------------//
|
||||
|
||||
#ifndef BOOST_COMPUTE_ALGORITHM_DETAIL_REDUCE_BY_KEY_WITH_SCAN_HPP
|
||||
#define BOOST_COMPUTE_ALGORITHM_DETAIL_REDUCE_BY_KEY_WITH_SCAN_HPP
|
||||
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
|
||||
#include <boost/compute/command_queue.hpp>
|
||||
#include <boost/compute/functional.hpp>
|
||||
#include <boost/compute/algorithm/inclusive_scan.hpp>
|
||||
#include <boost/compute/container/vector.hpp>
|
||||
#include <boost/compute/container/detail/scalar.hpp>
|
||||
#include <boost/compute/detail/meta_kernel.hpp>
|
||||
#include <boost/compute/detail/iterator_range_size.hpp>
|
||||
#include <boost/compute/detail/read_write_single_value.hpp>
|
||||
#include <boost/compute/type_traits.hpp>
|
||||
#include <boost/compute/utility/program_cache.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace compute {
|
||||
namespace detail {
|
||||
|
||||
/// \internal_
|
||||
///
|
||||
/// Fills \p new_keys_first with unsigned integer keys generated from vector
|
||||
/// of original keys \p keys_first. New keys can be distinguish by simple equality
|
||||
/// predicate.
|
||||
///
|
||||
/// \param keys_first iterator pointing to the first key
|
||||
/// \param number_of_keys number of keys
|
||||
/// \param predicate binary predicate for key comparison
|
||||
/// \param new_keys_first iterator pointing to the new keys vector
|
||||
/// \param preferred_work_group_size preferred work group size
|
||||
/// \param queue command queue to perform the operation
|
||||
///
|
||||
/// Binary function \p predicate must take two keys as arguments and
|
||||
/// return true only if they are considered the same.
|
||||
///
|
||||
/// The first new key equals zero and the last equals number of unique keys
|
||||
/// minus one.
|
||||
///
|
||||
/// No local memory usage.
|
||||
template<class InputKeyIterator, class BinaryPredicate>
|
||||
inline void generate_uint_keys(InputKeyIterator keys_first,
|
||||
size_t number_of_keys,
|
||||
BinaryPredicate predicate,
|
||||
vector<uint_>::iterator new_keys_first,
|
||||
size_t preferred_work_group_size,
|
||||
command_queue &queue)
|
||||
{
|
||||
typedef typename
|
||||
std::iterator_traits<InputKeyIterator>::value_type key_type;
|
||||
|
||||
detail::meta_kernel k("reduce_by_key_new_key_flags");
|
||||
k.add_set_arg<const uint_>("count", uint_(number_of_keys));
|
||||
|
||||
k <<
|
||||
k.decl<const uint_>("gid") << " = get_global_id(0);\n" <<
|
||||
k.decl<uint_>("value") << " = 0;\n" <<
|
||||
"if(gid >= count){\n return;\n}\n" <<
|
||||
"if(gid > 0){ \n" <<
|
||||
k.decl<key_type>("key") << " = " <<
|
||||
keys_first[k.var<const uint_>("gid")] << ";\n" <<
|
||||
k.decl<key_type>("previous_key") << " = " <<
|
||||
keys_first[k.var<const uint_>("gid - 1")] << ";\n" <<
|
||||
" value = " << predicate(k.var<key_type>("previous_key"),
|
||||
k.var<key_type>("key")) <<
|
||||
" ? 0 : 1;\n" <<
|
||||
"}\n else {\n" <<
|
||||
" value = 0;\n" <<
|
||||
"}\n" <<
|
||||
new_keys_first[k.var<const uint_>("gid")] << " = value;\n";
|
||||
|
||||
const context &context = queue.get_context();
|
||||
kernel kernel = k.compile(context);
|
||||
|
||||
size_t work_group_size = preferred_work_group_size;
|
||||
size_t work_groups_no = static_cast<size_t>(
|
||||
std::ceil(float(number_of_keys) / work_group_size)
|
||||
);
|
||||
|
||||
queue.enqueue_1d_range_kernel(kernel,
|
||||
0,
|
||||
work_groups_no * work_group_size,
|
||||
work_group_size);
|
||||
|
||||
inclusive_scan(new_keys_first, new_keys_first + number_of_keys,
|
||||
new_keys_first, queue);
|
||||
}
|
||||
|
||||
/// \internal_
|
||||
/// Calculate carry-out for each work group.
|
||||
/// Carry-out is a pair of the last key processed by a work group and sum of all
|
||||
/// values under this key in this work group.
|
||||
template<class InputValueIterator, class OutputValueIterator, class BinaryFunction>
|
||||
inline void carry_outs(vector<uint_>::iterator keys_first,
|
||||
InputValueIterator values_first,
|
||||
size_t count,
|
||||
vector<uint_>::iterator carry_out_keys_first,
|
||||
OutputValueIterator carry_out_values_first,
|
||||
BinaryFunction function,
|
||||
size_t work_group_size,
|
||||
command_queue &queue)
|
||||
{
|
||||
typedef typename
|
||||
std::iterator_traits<OutputValueIterator>::value_type value_out_type;
|
||||
|
||||
detail::meta_kernel k("reduce_by_key_with_scan_carry_outs");
|
||||
k.add_set_arg<const uint_>("count", uint_(count));
|
||||
size_t local_keys_arg = k.add_arg<uint_ *>(memory_object::local_memory, "lkeys");
|
||||
size_t local_vals_arg = k.add_arg<value_out_type *>(memory_object::local_memory, "lvals");
|
||||
|
||||
k <<
|
||||
k.decl<const uint_>("gid") << " = get_global_id(0);\n" <<
|
||||
k.decl<const uint_>("wg_size") << " = get_local_size(0);\n" <<
|
||||
k.decl<const uint_>("lid") << " = get_local_id(0);\n" <<
|
||||
k.decl<const uint_>("group_id") << " = get_group_id(0);\n" <<
|
||||
|
||||
k.decl<uint_>("key") << ";\n" <<
|
||||
k.decl<value_out_type>("value") << ";\n" <<
|
||||
"if(gid < count){\n" <<
|
||||
k.var<uint_>("key") << " = " <<
|
||||
keys_first[k.var<const uint_>("gid")] << ";\n" <<
|
||||
k.var<value_out_type>("value") << " = " <<
|
||||
values_first[k.var<const uint_>("gid")] << ";\n" <<
|
||||
"lkeys[lid] = key;\n" <<
|
||||
"lvals[lid] = value;\n" <<
|
||||
"}\n" <<
|
||||
|
||||
// Calculate carry out for each work group by performing Hillis/Steele scan
|
||||
// where only last element (key-value pair) is saved
|
||||
k.decl<value_out_type>("result") << " = value;\n" <<
|
||||
k.decl<uint_>("other_key") << ";\n" <<
|
||||
k.decl<value_out_type>("other_value") << ";\n" <<
|
||||
|
||||
"for(" << k.decl<uint_>("offset") << " = 1; " <<
|
||||
"offset < wg_size; offset *= 2){\n"
|
||||
" barrier(CLK_LOCAL_MEM_FENCE);\n" <<
|
||||
" if(lid >= offset){\n"
|
||||
" other_key = lkeys[lid - offset];\n" <<
|
||||
" if(other_key == key){\n" <<
|
||||
" other_value = lvals[lid - offset];\n" <<
|
||||
" result = " << function(k.var<value_out_type>("result"),
|
||||
k.var<value_out_type>("other_value")) << ";\n" <<
|
||||
" }\n" <<
|
||||
" }\n" <<
|
||||
" barrier(CLK_LOCAL_MEM_FENCE);\n" <<
|
||||
" lvals[lid] = result;\n" <<
|
||||
"}\n" <<
|
||||
|
||||
// save carry out
|
||||
"if(lid == (wg_size - 1)){\n" <<
|
||||
carry_out_keys_first[k.var<const uint_>("group_id")] << " = key;\n" <<
|
||||
carry_out_values_first[k.var<const uint_>("group_id")] << " = result;\n" <<
|
||||
"}\n";
|
||||
|
||||
size_t work_groups_no = static_cast<size_t>(
|
||||
std::ceil(float(count) / work_group_size)
|
||||
);
|
||||
|
||||
const context &context = queue.get_context();
|
||||
kernel kernel = k.compile(context);
|
||||
kernel.set_arg(local_keys_arg, local_buffer<uint_>(work_group_size));
|
||||
kernel.set_arg(local_vals_arg, local_buffer<value_out_type>(work_group_size));
|
||||
|
||||
queue.enqueue_1d_range_kernel(kernel,
|
||||
0,
|
||||
work_groups_no * work_group_size,
|
||||
work_group_size);
|
||||
}
|
||||
|
||||
/// \internal_
|
||||
/// Calculate carry-in by performing inclusive scan by key on carry-outs vector.
|
||||
template<class OutputValueIterator, class BinaryFunction>
|
||||
inline void carry_ins(vector<uint_>::iterator carry_out_keys_first,
|
||||
OutputValueIterator carry_out_values_first,
|
||||
OutputValueIterator carry_in_values_first,
|
||||
size_t carry_out_size,
|
||||
BinaryFunction function,
|
||||
size_t work_group_size,
|
||||
command_queue &queue)
|
||||
{
|
||||
typedef typename
|
||||
std::iterator_traits<OutputValueIterator>::value_type value_out_type;
|
||||
|
||||
uint_ values_pre_work_item = static_cast<uint_>(
|
||||
std::ceil(float(carry_out_size) / work_group_size)
|
||||
);
|
||||
|
||||
detail::meta_kernel k("reduce_by_key_with_scan_carry_ins");
|
||||
k.add_set_arg<const uint_>("carry_out_size", uint_(carry_out_size));
|
||||
k.add_set_arg<const uint_>("values_per_work_item", values_pre_work_item);
|
||||
size_t local_keys_arg = k.add_arg<uint_ *>(memory_object::local_memory, "lkeys");
|
||||
size_t local_vals_arg = k.add_arg<value_out_type *>(memory_object::local_memory, "lvals");
|
||||
|
||||
k <<
|
||||
k.decl<uint_>("id") << " = get_global_id(0) * values_per_work_item;\n" <<
|
||||
k.decl<uint_>("idx") << " = id;\n" <<
|
||||
k.decl<const uint_>("wg_size") << " = get_local_size(0);\n" <<
|
||||
k.decl<const uint_>("lid") << " = get_local_id(0);\n" <<
|
||||
k.decl<const uint_>("group_id") << " = get_group_id(0);\n" <<
|
||||
|
||||
k.decl<uint_>("key") << ";\n" <<
|
||||
k.decl<value_out_type>("value") << ";\n" <<
|
||||
k.decl<uint_>("previous_key") << ";\n" <<
|
||||
k.decl<value_out_type>("result") << ";\n" <<
|
||||
|
||||
"if(id < carry_out_size){\n" <<
|
||||
k.var<uint_>("previous_key") << " = " <<
|
||||
carry_out_keys_first[k.var<const uint_>("id")] << ";\n" <<
|
||||
k.var<value_out_type>("result") << " = " <<
|
||||
carry_out_values_first[k.var<const uint_>("id")] << ";\n" <<
|
||||
carry_in_values_first[k.var<const uint_>("id")] << " = result;\n" <<
|
||||
"}\n" <<
|
||||
|
||||
k.decl<const uint_>("end") << " = (id + values_per_work_item) <= carry_out_size" <<
|
||||
" ? (values_per_work_item + id) : carry_out_size;\n" <<
|
||||
|
||||
"for(idx = idx + 1; idx < end; idx += 1){\n" <<
|
||||
" key = " << carry_out_keys_first[k.var<const uint_>("idx")] << ";\n" <<
|
||||
" value = " << carry_out_values_first[k.var<const uint_>("idx")] << ";\n" <<
|
||||
" if(previous_key == key){\n" <<
|
||||
" result = " << function(k.var<value_out_type>("result"),
|
||||
k.var<value_out_type>("value")) << ";\n" <<
|
||||
" }\n else { \n" <<
|
||||
" result = value;\n"
|
||||
" }\n" <<
|
||||
" " << carry_in_values_first[k.var<const uint_>("idx")] << " = result;\n" <<
|
||||
" previous_key = key;\n"
|
||||
"}\n" <<
|
||||
|
||||
// save the last key and result to local memory
|
||||
"lkeys[lid] = previous_key;\n" <<
|
||||
"lvals[lid] = result;\n" <<
|
||||
|
||||
// Hillis/Steele scan
|
||||
"for(" << k.decl<uint_>("offset") << " = 1; " <<
|
||||
"offset < wg_size; offset *= 2){\n"
|
||||
" barrier(CLK_LOCAL_MEM_FENCE);\n" <<
|
||||
" if(lid >= offset){\n"
|
||||
" key = lkeys[lid - offset];\n" <<
|
||||
" if(previous_key == key){\n" <<
|
||||
" value = lvals[lid - offset];\n" <<
|
||||
" result = " << function(k.var<value_out_type>("result"),
|
||||
k.var<value_out_type>("value")) << ";\n" <<
|
||||
" }\n" <<
|
||||
" }\n" <<
|
||||
" barrier(CLK_LOCAL_MEM_FENCE);\n" <<
|
||||
" lvals[lid] = result;\n" <<
|
||||
"}\n" <<
|
||||
"barrier(CLK_LOCAL_MEM_FENCE);\n" <<
|
||||
|
||||
"if(lid > 0){\n" <<
|
||||
// load key-value reduced by previous work item
|
||||
" previous_key = lkeys[lid - 1];\n" <<
|
||||
" result = lvals[lid - 1];\n" <<
|
||||
"}\n" <<
|
||||
|
||||
// add key-value reduced by previous work item
|
||||
"for(idx = id; idx < id + values_per_work_item; idx += 1){\n" <<
|
||||
// make sure all carry-ins are saved in global memory
|
||||
" barrier( CLK_GLOBAL_MEM_FENCE );\n" <<
|
||||
" if(lid > 0 && idx < carry_out_size) {\n"
|
||||
" key = " << carry_out_keys_first[k.var<const uint_>("idx")] << ";\n" <<
|
||||
" value = " << carry_in_values_first[k.var<const uint_>("idx")] << ";\n" <<
|
||||
" if(previous_key == key){\n" <<
|
||||
" value = " << function(k.var<value_out_type>("result"),
|
||||
k.var<value_out_type>("value")) << ";\n" <<
|
||||
" }\n" <<
|
||||
" " << carry_in_values_first[k.var<const uint_>("idx")] << " = value;\n" <<
|
||||
" }\n" <<
|
||||
"}\n";
|
||||
|
||||
|
||||
const context &context = queue.get_context();
|
||||
kernel kernel = k.compile(context);
|
||||
kernel.set_arg(local_keys_arg, local_buffer<uint_>(work_group_size));
|
||||
kernel.set_arg(local_vals_arg, local_buffer<value_out_type>(work_group_size));
|
||||
|
||||
queue.enqueue_1d_range_kernel(kernel,
|
||||
0,
|
||||
work_group_size,
|
||||
work_group_size);
|
||||
}
|
||||
|
||||
/// \internal_
|
||||
///
|
||||
/// Perform final reduction by key. Each work item:
|
||||
/// 1. Perform local work-group reduction (Hillis/Steele scan)
|
||||
/// 2. Add carry-in (if keys are right)
|
||||
/// 3. Save reduced value if next key is different than processed one
|
||||
template<class InputKeyIterator, class InputValueIterator,
|
||||
class OutputKeyIterator, class OutputValueIterator,
|
||||
class BinaryFunction>
|
||||
inline void final_reduction(InputKeyIterator keys_first,
|
||||
InputValueIterator values_first,
|
||||
OutputKeyIterator keys_result,
|
||||
OutputValueIterator values_result,
|
||||
size_t count,
|
||||
BinaryFunction function,
|
||||
vector<uint_>::iterator new_keys_first,
|
||||
vector<uint_>::iterator carry_in_keys_first,
|
||||
OutputValueIterator carry_in_values_first,
|
||||
size_t carry_in_size,
|
||||
size_t work_group_size,
|
||||
command_queue &queue)
|
||||
{
|
||||
typedef typename
|
||||
std::iterator_traits<OutputValueIterator>::value_type value_out_type;
|
||||
|
||||
detail::meta_kernel k("reduce_by_key_with_scan_final_reduction");
|
||||
k.add_set_arg<const uint_>("count", uint_(count));
|
||||
size_t local_keys_arg = k.add_arg<uint_ *>(memory_object::local_memory, "lkeys");
|
||||
size_t local_vals_arg = k.add_arg<value_out_type *>(memory_object::local_memory, "lvals");
|
||||
|
||||
k <<
|
||||
k.decl<const uint_>("gid") << " = get_global_id(0);\n" <<
|
||||
k.decl<const uint_>("wg_size") << " = get_local_size(0);\n" <<
|
||||
k.decl<const uint_>("lid") << " = get_local_id(0);\n" <<
|
||||
k.decl<const uint_>("group_id") << " = get_group_id(0);\n" <<
|
||||
|
||||
k.decl<uint_>("key") << ";\n" <<
|
||||
k.decl<value_out_type>("value") << ";\n"
|
||||
|
||||
"if(gid < count){\n" <<
|
||||
k.var<uint_>("key") << " = " <<
|
||||
new_keys_first[k.var<const uint_>("gid")] << ";\n" <<
|
||||
k.var<value_out_type>("value") << " = " <<
|
||||
values_first[k.var<const uint_>("gid")] << ";\n" <<
|
||||
"lkeys[lid] = key;\n" <<
|
||||
"lvals[lid] = value;\n" <<
|
||||
"}\n" <<
|
||||
|
||||
// Hillis/Steele scan
|
||||
k.decl<value_out_type>("result") << " = value;\n" <<
|
||||
k.decl<uint_>("other_key") << ";\n" <<
|
||||
k.decl<value_out_type>("other_value") << ";\n" <<
|
||||
|
||||
"for(" << k.decl<uint_>("offset") << " = 1; " <<
|
||||
"offset < wg_size ; offset *= 2){\n"
|
||||
" barrier(CLK_LOCAL_MEM_FENCE);\n" <<
|
||||
" if(lid >= offset) {\n" <<
|
||||
" other_key = lkeys[lid - offset];\n" <<
|
||||
" if(other_key == key){\n" <<
|
||||
" other_value = lvals[lid - offset];\n" <<
|
||||
" result = " << function(k.var<value_out_type>("result"),
|
||||
k.var<value_out_type>("other_value")) << ";\n" <<
|
||||
" }\n" <<
|
||||
" }\n" <<
|
||||
" barrier(CLK_LOCAL_MEM_FENCE);\n" <<
|
||||
" lvals[lid] = result;\n" <<
|
||||
"}\n" <<
|
||||
|
||||
"if(gid >= count) {\n return;\n};\n" <<
|
||||
|
||||
k.decl<const bool>("save") << " = (gid < (count - 1)) ?"
|
||||
<< new_keys_first[k.var<const uint_>("gid + 1")] << " != key" <<
|
||||
": true;\n" <<
|
||||
|
||||
// Add carry in
|
||||
k.decl<uint_>("carry_in_key") << ";\n" <<
|
||||
"if(group_id > 0 && save) {\n" <<
|
||||
" carry_in_key = " << carry_in_keys_first[k.var<const uint_>("group_id - 1")] << ";\n" <<
|
||||
" if(key == carry_in_key){\n" <<
|
||||
" other_value = " << carry_in_values_first[k.var<const uint_>("group_id - 1")] << ";\n" <<
|
||||
" result = " << function(k.var<value_out_type>("result"),
|
||||
k.var<value_out_type>("other_value")) << ";\n" <<
|
||||
" }\n" <<
|
||||
"}\n" <<
|
||||
|
||||
// Save result only if the next key is different or it's the last element.
|
||||
"if(save){\n" <<
|
||||
keys_result[k.var<uint_>("key")] << " = " << keys_first[k.var<const uint_>("gid")] << ";\n" <<
|
||||
values_result[k.var<uint_>("key")] << " = result;\n" <<
|
||||
"}\n"
|
||||
;
|
||||
|
||||
size_t work_groups_no = static_cast<size_t>(
|
||||
std::ceil(float(count) / work_group_size)
|
||||
);
|
||||
|
||||
const context &context = queue.get_context();
|
||||
kernel kernel = k.compile(context);
|
||||
kernel.set_arg(local_keys_arg, local_buffer<uint_>(work_group_size));
|
||||
kernel.set_arg(local_vals_arg, local_buffer<value_out_type>(work_group_size));
|
||||
|
||||
queue.enqueue_1d_range_kernel(kernel,
|
||||
0,
|
||||
work_groups_no * work_group_size,
|
||||
work_group_size);
|
||||
}
|
||||
|
||||
/// \internal_
|
||||
/// Returns preferred work group size for reduce by key with scan algorithm.
|
||||
template<class KeyType, class ValueType>
|
||||
inline size_t get_work_group_size(const device& device)
|
||||
{
|
||||
std::string cache_key = std::string("__boost_reduce_by_key_with_scan")
|
||||
+ "k_" + type_name<KeyType>() + "_v_" + type_name<ValueType>();
|
||||
|
||||
// load parameters
|
||||
boost::shared_ptr<parameter_cache> parameters =
|
||||
detail::parameter_cache::get_global_cache(device);
|
||||
|
||||
return (std::max)(
|
||||
static_cast<size_t>(parameters->get(cache_key, "wgsize", 256)),
|
||||
static_cast<size_t>(device.get_info<CL_DEVICE_MAX_WORK_GROUP_SIZE>())
|
||||
);
|
||||
}
|
||||
|
||||
/// \internal_
|
||||
///
|
||||
/// 1. For each work group carry-out value is calculated (it's done by key-oriented
|
||||
/// Hillis/Steele scan). Carry-out is a pair of the last key processed by work
|
||||
/// group and sum of all values under this key in work group.
|
||||
/// 2. From every carry-out carry-in is calculated by performing inclusive scan
|
||||
/// by key.
|
||||
/// 3. Final reduction by key is performed (key-oriented Hillis/Steele scan),
|
||||
/// carry-in values are added where needed.
|
||||
template<class InputKeyIterator, class InputValueIterator,
|
||||
class OutputKeyIterator, class OutputValueIterator,
|
||||
class BinaryFunction, class BinaryPredicate>
|
||||
inline size_t reduce_by_key_with_scan(InputKeyIterator keys_first,
|
||||
InputKeyIterator keys_last,
|
||||
InputValueIterator values_first,
|
||||
OutputKeyIterator keys_result,
|
||||
OutputValueIterator values_result,
|
||||
BinaryFunction function,
|
||||
BinaryPredicate predicate,
|
||||
command_queue &queue)
|
||||
{
|
||||
typedef typename
|
||||
std::iterator_traits<InputValueIterator>::value_type value_type;
|
||||
typedef typename
|
||||
std::iterator_traits<InputKeyIterator>::value_type key_type;
|
||||
typedef typename
|
||||
std::iterator_traits<OutputValueIterator>::value_type value_out_type;
|
||||
|
||||
const context &context = queue.get_context();
|
||||
size_t count = detail::iterator_range_size(keys_first, keys_last);
|
||||
|
||||
if(count == 0){
|
||||
return size_t(0);
|
||||
}
|
||||
|
||||
const device &device = queue.get_device();
|
||||
size_t work_group_size = get_work_group_size<value_type, key_type>(device);
|
||||
|
||||
// Replace original key with unsigned integer keys generated based on given
|
||||
// predicate. New key is also an index for keys_result and values_result vectors,
|
||||
// which points to place where reduced value should be saved.
|
||||
vector<uint_> new_keys(count, context);
|
||||
vector<uint_>::iterator new_keys_first = new_keys.begin();
|
||||
generate_uint_keys(keys_first, count, predicate, new_keys_first,
|
||||
work_group_size, queue);
|
||||
|
||||
// Calculate carry-out and carry-in vectors size
|
||||
const size_t carry_out_size = static_cast<size_t>(
|
||||
std::ceil(float(count) / work_group_size)
|
||||
);
|
||||
vector<uint_> carry_out_keys(carry_out_size, context);
|
||||
vector<value_out_type> carry_out_values(carry_out_size, context);
|
||||
carry_outs(new_keys_first, values_first, count, carry_out_keys.begin(),
|
||||
carry_out_values.begin(), function, work_group_size, queue);
|
||||
|
||||
vector<value_out_type> carry_in_values(carry_out_size, context);
|
||||
carry_ins(carry_out_keys.begin(), carry_out_values.begin(),
|
||||
carry_in_values.begin(), carry_out_size, function, work_group_size,
|
||||
queue);
|
||||
|
||||
final_reduction(keys_first, values_first, keys_result, values_result,
|
||||
count, function, new_keys_first, carry_out_keys.begin(),
|
||||
carry_in_values.begin(), carry_out_size, work_group_size,
|
||||
queue);
|
||||
|
||||
const size_t result = read_single_value<uint_>(new_keys.get_buffer(),
|
||||
count - 1, queue);
|
||||
return result + 1;
|
||||
}
|
||||
|
||||
/// \internal_
|
||||
/// Return true if requirements for running reduce by key with scan on given
|
||||
/// device are met (at least one work group of preferred size can be run).
|
||||
template<class InputKeyIterator, class InputValueIterator,
|
||||
class OutputKeyIterator, class OutputValueIterator>
|
||||
bool reduce_by_key_with_scan_requirements_met(InputKeyIterator keys_first,
|
||||
InputValueIterator values_first,
|
||||
OutputKeyIterator keys_result,
|
||||
OutputValueIterator values_result,
|
||||
const size_t count,
|
||||
command_queue &queue)
|
||||
{
|
||||
typedef typename
|
||||
std::iterator_traits<InputValueIterator>::value_type value_type;
|
||||
typedef typename
|
||||
std::iterator_traits<InputKeyIterator>::value_type key_type;
|
||||
typedef typename
|
||||
std::iterator_traits<OutputValueIterator>::value_type value_out_type;
|
||||
|
||||
(void) keys_first;
|
||||
(void) values_first;
|
||||
(void) keys_result;
|
||||
(void) values_result;
|
||||
|
||||
const device &device = queue.get_device();
|
||||
// device must have dedicated local memory storage
|
||||
if(device.get_info<CL_DEVICE_LOCAL_MEM_TYPE>() != CL_LOCAL)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// local memory size in bytes (per compute unit)
|
||||
const size_t local_mem_size = device.get_info<CL_DEVICE_LOCAL_MEM_SIZE>();
|
||||
|
||||
// preferred work group size
|
||||
size_t work_group_size = get_work_group_size<key_type, value_type>(device);
|
||||
|
||||
// local memory size needed to perform parallel reduction
|
||||
size_t required_local_mem_size = 0;
|
||||
// keys size
|
||||
required_local_mem_size += sizeof(uint_) * work_group_size;
|
||||
// reduced values size
|
||||
required_local_mem_size += sizeof(value_out_type) * work_group_size;
|
||||
|
||||
return (required_local_mem_size <= local_mem_size);
|
||||
}
|
||||
|
||||
} // end detail namespace
|
||||
} // end compute namespace
|
||||
} // end boost namespace
|
||||
|
||||
#endif // BOOST_COMPUTE_ALGORITHM_DETAIL_REDUCE_BY_KEY_WITH_SCAN_HPP
|
||||
@@ -0,0 +1,110 @@
|
||||
//---------------------------------------------------------------------------//
|
||||
// Copyright (c) 2016 Jakub Szuppe <j.szuppe@gmail.com>
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0
|
||||
// See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt
|
||||
//
|
||||
// See http://boostorg.github.com/compute for more information.
|
||||
//---------------------------------------------------------------------------//
|
||||
|
||||
#ifndef BOOST_COMPUTE_ALGORITHM_DETAIL_REDUCE_ON_CPU_HPP
|
||||
#define BOOST_COMPUTE_ALGORITHM_DETAIL_REDUCE_ON_CPU_HPP
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include <boost/compute/buffer.hpp>
|
||||
#include <boost/compute/command_queue.hpp>
|
||||
#include <boost/compute/detail/meta_kernel.hpp>
|
||||
#include <boost/compute/detail/iterator_range_size.hpp>
|
||||
#include <boost/compute/detail/parameter_cache.hpp>
|
||||
#include <boost/compute/iterator/buffer_iterator.hpp>
|
||||
#include <boost/compute/type_traits/result_of.hpp>
|
||||
#include <boost/compute/algorithm/detail/serial_reduce.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace compute {
|
||||
namespace detail {
|
||||
|
||||
template<class InputIterator, class OutputIterator, class BinaryFunction>
|
||||
inline void reduce_on_cpu(InputIterator first,
|
||||
InputIterator last,
|
||||
OutputIterator result,
|
||||
BinaryFunction function,
|
||||
command_queue &queue)
|
||||
{
|
||||
typedef typename
|
||||
std::iterator_traits<InputIterator>::value_type T;
|
||||
typedef typename
|
||||
::boost::compute::result_of<BinaryFunction(T, T)>::type result_type;
|
||||
|
||||
const device &device = queue.get_device();
|
||||
const uint_ compute_units = queue.get_device().compute_units();
|
||||
|
||||
boost::shared_ptr<parameter_cache> parameters =
|
||||
detail::parameter_cache::get_global_cache(device);
|
||||
|
||||
std::string cache_key =
|
||||
"__boost_reduce_cpu_" + boost::lexical_cast<std::string>(sizeof(T));
|
||||
|
||||
// for inputs smaller than serial_reduce_threshold
|
||||
// serial_reduce algorithm is used
|
||||
uint_ serial_reduce_threshold =
|
||||
parameters->get(cache_key, "serial_reduce_threshold", 16384 * sizeof(T));
|
||||
serial_reduce_threshold =
|
||||
(std::max)(serial_reduce_threshold, uint_(compute_units));
|
||||
|
||||
const context &context = queue.get_context();
|
||||
size_t count = detail::iterator_range_size(first, last);
|
||||
if(count == 0){
|
||||
return;
|
||||
}
|
||||
else if(count < serial_reduce_threshold) {
|
||||
return serial_reduce(first, last, result, function, queue);
|
||||
}
|
||||
|
||||
meta_kernel k("reduce_on_cpu");
|
||||
buffer output(context, sizeof(result_type) * compute_units);
|
||||
|
||||
size_t count_arg = k.add_arg<uint_>("count");
|
||||
size_t output_arg =
|
||||
k.add_arg<result_type *>(memory_object::global_memory, "output");
|
||||
|
||||
k <<
|
||||
"uint block = " <<
|
||||
"(uint)ceil(((float)count)/get_global_size(0));\n" <<
|
||||
"uint index = get_global_id(0) * block;\n" <<
|
||||
"uint end = min(count, index + block);\n" <<
|
||||
|
||||
k.decl<result_type>("result") << " = " << first[k.var<uint_>("index")] << ";\n" <<
|
||||
"index++;\n" <<
|
||||
"while(index < end){\n" <<
|
||||
"result = " << function(k.var<T>("result"),
|
||||
first[k.var<uint_>("index")]) << ";\n" <<
|
||||
"index++;\n" <<
|
||||
"}\n" <<
|
||||
"output[get_global_id(0)] = result;\n";
|
||||
|
||||
size_t global_work_size = compute_units;
|
||||
kernel kernel = k.compile(context);
|
||||
|
||||
// reduction to global_work_size elements
|
||||
kernel.set_arg(count_arg, static_cast<uint_>(count));
|
||||
kernel.set_arg(output_arg, output);
|
||||
queue.enqueue_1d_range_kernel(kernel, 0, global_work_size, 0);
|
||||
|
||||
// final reduction
|
||||
reduce_on_cpu(
|
||||
make_buffer_iterator<result_type>(output),
|
||||
make_buffer_iterator<result_type>(output, global_work_size),
|
||||
result,
|
||||
function,
|
||||
queue
|
||||
);
|
||||
}
|
||||
|
||||
} // end detail namespace
|
||||
} // end compute namespace
|
||||
} // end boost namespace
|
||||
|
||||
#endif // BOOST_COMPUTE_ALGORITHM_DETAIL_REDUCE_ON_CPU_HPP
|
||||
@@ -0,0 +1,286 @@
|
||||
//---------------------------------------------------------------------------//
|
||||
// Copyright (c) 2013 Kyle Lutz <kyle.r.lutz@gmail.com>
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0
|
||||
// See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt
|
||||
//
|
||||
// See http://boostorg.github.com/compute for more information.
|
||||
//---------------------------------------------------------------------------//
|
||||
|
||||
#ifndef BOOST_COMPUTE_ALGORITHM_DETAIL_REDUCE_ON_GPU_HPP
|
||||
#define BOOST_COMPUTE_ALGORITHM_DETAIL_REDUCE_ON_GPU_HPP
|
||||
|
||||
#include <iterator>
|
||||
|
||||
#include <boost/compute/utility/source.hpp>
|
||||
#include <boost/compute/program.hpp>
|
||||
#include <boost/compute/command_queue.hpp>
|
||||
#include <boost/compute/detail/vendor.hpp>
|
||||
#include <boost/compute/detail/parameter_cache.hpp>
|
||||
#include <boost/compute/detail/work_size.hpp>
|
||||
#include <boost/compute/detail/meta_kernel.hpp>
|
||||
#include <boost/compute/type_traits/type_name.hpp>
|
||||
#include <boost/compute/utility/program_cache.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace compute {
|
||||
namespace detail {
|
||||
|
||||
/// \internal
|
||||
/// body reduction inside a warp
|
||||
template<typename T,bool isNvidiaDevice>
|
||||
struct ReduceBody
|
||||
{
|
||||
static std::string body()
|
||||
{
|
||||
std::stringstream k;
|
||||
// local reduction
|
||||
k << "for(int i = 1; i < TPB; i <<= 1){\n" <<
|
||||
" barrier(CLK_LOCAL_MEM_FENCE);\n" <<
|
||||
" uint mask = (i << 1) - 1;\n" <<
|
||||
" if((lid & mask) == 0){\n" <<
|
||||
" scratch[lid] += scratch[lid+i];\n" <<
|
||||
" }\n" <<
|
||||
"}\n";
|
||||
return k.str();
|
||||
}
|
||||
};
|
||||
|
||||
/// \internal
|
||||
/// body reduction inside a warp
|
||||
/// for nvidia device we can use the "unsafe"
|
||||
/// memory optimisation
|
||||
template<typename T>
|
||||
struct ReduceBody<T,true>
|
||||
{
|
||||
static std::string body()
|
||||
{
|
||||
std::stringstream k;
|
||||
// local reduction
|
||||
// we use TPB to compile only useful instruction
|
||||
// local reduction when size is greater than warp size
|
||||
k << "barrier(CLK_LOCAL_MEM_FENCE);\n" <<
|
||||
"if(TPB >= 1024){\n" <<
|
||||
"if(lid < 512) { sum += scratch[lid + 512]; scratch[lid] = sum;} barrier(CLK_LOCAL_MEM_FENCE);}\n" <<
|
||||
"if(TPB >= 512){\n" <<
|
||||
"if(lid < 256) { sum += scratch[lid + 256]; scratch[lid] = sum;} barrier(CLK_LOCAL_MEM_FENCE);}\n" <<
|
||||
"if(TPB >= 256){\n" <<
|
||||
"if(lid < 128) { sum += scratch[lid + 128]; scratch[lid] = sum;} barrier(CLK_LOCAL_MEM_FENCE);}\n" <<
|
||||
"if(TPB >= 128){\n" <<
|
||||
"if(lid < 64) { sum += scratch[lid + 64]; scratch[lid] = sum;} barrier(CLK_LOCAL_MEM_FENCE);} \n" <<
|
||||
|
||||
// warp reduction
|
||||
"if(lid < 32){\n" <<
|
||||
// volatile this way we don't need any barrier
|
||||
"volatile __local " << type_name<T>() << " *lmem = scratch;\n" <<
|
||||
"if(TPB >= 64) { lmem[lid] = sum = sum + lmem[lid+32];} \n" <<
|
||||
"if(TPB >= 32) { lmem[lid] = sum = sum + lmem[lid+16];} \n" <<
|
||||
"if(TPB >= 16) { lmem[lid] = sum = sum + lmem[lid+ 8];} \n" <<
|
||||
"if(TPB >= 8) { lmem[lid] = sum = sum + lmem[lid+ 4];} \n" <<
|
||||
"if(TPB >= 4) { lmem[lid] = sum = sum + lmem[lid+ 2];} \n" <<
|
||||
"if(TPB >= 2) { lmem[lid] = sum = sum + lmem[lid+ 1];} \n" <<
|
||||
"}\n";
|
||||
return k.str();
|
||||
}
|
||||
};
|
||||
|
||||
template<class InputIterator, class Function>
|
||||
inline void initial_reduce(InputIterator first,
|
||||
InputIterator last,
|
||||
buffer result,
|
||||
const Function &function,
|
||||
kernel &reduce_kernel,
|
||||
const uint_ vpt,
|
||||
const uint_ tpb,
|
||||
command_queue &queue)
|
||||
{
|
||||
(void) function;
|
||||
(void) reduce_kernel;
|
||||
|
||||
typedef typename std::iterator_traits<InputIterator>::value_type Arg;
|
||||
typedef typename boost::tr1_result_of<Function(Arg, Arg)>::type T;
|
||||
|
||||
size_t count = std::distance(first, last);
|
||||
detail::meta_kernel k("initial_reduce");
|
||||
k.add_set_arg<const uint_>("count", uint_(count));
|
||||
size_t output_arg = k.add_arg<T *>(memory_object::global_memory, "output");
|
||||
|
||||
k <<
|
||||
k.decl<const uint_>("offset") << " = get_group_id(0) * VPT * TPB;\n" <<
|
||||
k.decl<const uint_>("lid") << " = get_local_id(0);\n" <<
|
||||
|
||||
"__local " << type_name<T>() << " scratch[TPB];\n" <<
|
||||
|
||||
// private reduction
|
||||
k.decl<T>("sum") << " = 0;\n" <<
|
||||
"for(uint i = 0; i < VPT; i++){\n" <<
|
||||
" if(offset + lid + i*TPB < count){\n" <<
|
||||
" sum = sum + " << first[k.var<uint_>("offset+lid+i*TPB")] << ";\n" <<
|
||||
" }\n" <<
|
||||
"}\n" <<
|
||||
|
||||
"scratch[lid] = sum;\n" <<
|
||||
|
||||
// local reduction
|
||||
ReduceBody<T,false>::body() <<
|
||||
|
||||
// write sum to output
|
||||
"if(lid == 0){\n" <<
|
||||
" output[get_group_id(0)] = scratch[0];\n" <<
|
||||
"}\n";
|
||||
|
||||
const context &context = queue.get_context();
|
||||
std::stringstream options;
|
||||
options << "-DVPT=" << vpt << " -DTPB=" << tpb;
|
||||
kernel generic_reduce_kernel = k.compile(context, options.str());
|
||||
generic_reduce_kernel.set_arg(output_arg, result);
|
||||
|
||||
size_t work_size = calculate_work_size(count, vpt, tpb);
|
||||
|
||||
queue.enqueue_1d_range_kernel(generic_reduce_kernel, 0, work_size, tpb);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
inline void initial_reduce(const buffer_iterator<T> &first,
|
||||
const buffer_iterator<T> &last,
|
||||
const buffer &result,
|
||||
const plus<T> &function,
|
||||
kernel &reduce_kernel,
|
||||
const uint_ vpt,
|
||||
const uint_ tpb,
|
||||
command_queue &queue)
|
||||
{
|
||||
(void) function;
|
||||
|
||||
size_t count = std::distance(first, last);
|
||||
|
||||
reduce_kernel.set_arg(0, first.get_buffer());
|
||||
reduce_kernel.set_arg(1, uint_(first.get_index()));
|
||||
reduce_kernel.set_arg(2, uint_(count));
|
||||
reduce_kernel.set_arg(3, result);
|
||||
reduce_kernel.set_arg(4, uint_(0));
|
||||
|
||||
size_t work_size = calculate_work_size(count, vpt, tpb);
|
||||
|
||||
queue.enqueue_1d_range_kernel(reduce_kernel, 0, work_size, tpb);
|
||||
}
|
||||
|
||||
template<class InputIterator, class T, class Function>
|
||||
inline void reduce_on_gpu(InputIterator first,
|
||||
InputIterator last,
|
||||
buffer_iterator<T> result,
|
||||
Function function,
|
||||
command_queue &queue)
|
||||
{
|
||||
const device &device = queue.get_device();
|
||||
const context &context = queue.get_context();
|
||||
|
||||
detail::meta_kernel k("reduce");
|
||||
k.add_arg<const T*>(memory_object::global_memory, "input");
|
||||
k.add_arg<const uint_>("offset");
|
||||
k.add_arg<const uint_>("count");
|
||||
k.add_arg<T*>(memory_object::global_memory, "output");
|
||||
k.add_arg<const uint_>("output_offset");
|
||||
|
||||
k <<
|
||||
k.decl<const uint_>("block_offset") << " = get_group_id(0) * VPT * TPB;\n" <<
|
||||
"__global const " << type_name<T>() << " *block = input + offset + block_offset;\n" <<
|
||||
k.decl<const uint_>("lid") << " = get_local_id(0);\n" <<
|
||||
|
||||
"__local " << type_name<T>() << " scratch[TPB];\n" <<
|
||||
// private reduction
|
||||
k.decl<T>("sum") << " = 0;\n" <<
|
||||
"for(uint i = 0; i < VPT; i++){\n" <<
|
||||
" if(block_offset + lid + i*TPB < count){\n" <<
|
||||
" sum = sum + block[lid+i*TPB]; \n" <<
|
||||
" }\n" <<
|
||||
"}\n" <<
|
||||
|
||||
"scratch[lid] = sum;\n";
|
||||
|
||||
// discrimination on vendor name
|
||||
if(is_nvidia_device(device))
|
||||
k << ReduceBody<T,true>::body();
|
||||
else
|
||||
k << ReduceBody<T,false>::body();
|
||||
|
||||
k <<
|
||||
// write sum to output
|
||||
"if(lid == 0){\n" <<
|
||||
" output[output_offset + get_group_id(0)] = scratch[0];\n" <<
|
||||
"}\n";
|
||||
|
||||
std::string cache_key = std::string("__boost_reduce_on_gpu_") + type_name<T>();
|
||||
|
||||
// load parameters
|
||||
boost::shared_ptr<parameter_cache> parameters =
|
||||
detail::parameter_cache::get_global_cache(device);
|
||||
|
||||
uint_ vpt = parameters->get(cache_key, "vpt", 8);
|
||||
uint_ tpb = parameters->get(cache_key, "tpb", 128);
|
||||
|
||||
// reduce program compiler flags
|
||||
std::stringstream options;
|
||||
options << "-DT=" << type_name<T>()
|
||||
<< " -DVPT=" << vpt
|
||||
<< " -DTPB=" << tpb;
|
||||
|
||||
// load program
|
||||
boost::shared_ptr<program_cache> cache =
|
||||
program_cache::get_global_cache(context);
|
||||
|
||||
program reduce_program = cache->get_or_build(
|
||||
cache_key, options.str(), k.source(), context
|
||||
);
|
||||
|
||||
// create reduce kernel
|
||||
kernel reduce_kernel(reduce_program, "reduce");
|
||||
|
||||
size_t count = std::distance(first, last);
|
||||
|
||||
// first pass, reduce from input to ping
|
||||
buffer ping(context, std::ceil(float(count) / vpt / tpb) * sizeof(T));
|
||||
initial_reduce(first, last, ping, function, reduce_kernel, vpt, tpb, queue);
|
||||
|
||||
// update count after initial reduce
|
||||
count = static_cast<size_t>(std::ceil(float(count) / vpt / tpb));
|
||||
|
||||
// middle pass(es), reduce between ping and pong
|
||||
const buffer *input_buffer = &ping;
|
||||
buffer pong(context, static_cast<size_t>(count / vpt / tpb * sizeof(T)));
|
||||
const buffer *output_buffer = &pong;
|
||||
if(count > vpt * tpb){
|
||||
while(count > vpt * tpb){
|
||||
reduce_kernel.set_arg(0, *input_buffer);
|
||||
reduce_kernel.set_arg(1, uint_(0));
|
||||
reduce_kernel.set_arg(2, uint_(count));
|
||||
reduce_kernel.set_arg(3, *output_buffer);
|
||||
reduce_kernel.set_arg(4, uint_(0));
|
||||
|
||||
size_t work_size = static_cast<size_t>(std::ceil(float(count) / vpt));
|
||||
if(work_size % tpb != 0){
|
||||
work_size += tpb - work_size % tpb;
|
||||
}
|
||||
queue.enqueue_1d_range_kernel(reduce_kernel, 0, work_size, tpb);
|
||||
|
||||
std::swap(input_buffer, output_buffer);
|
||||
count = static_cast<size_t>(std::ceil(float(count) / vpt / tpb));
|
||||
}
|
||||
}
|
||||
|
||||
// final pass, reduce from ping/pong to result
|
||||
reduce_kernel.set_arg(0, *input_buffer);
|
||||
reduce_kernel.set_arg(1, uint_(0));
|
||||
reduce_kernel.set_arg(2, uint_(count));
|
||||
reduce_kernel.set_arg(3, result.get_buffer());
|
||||
reduce_kernel.set_arg(4, uint_(result.get_index()));
|
||||
|
||||
queue.enqueue_1d_range_kernel(reduce_kernel, 0, tpb, tpb);
|
||||
}
|
||||
|
||||
} // end detail namespace
|
||||
} // end compute namespace
|
||||
} // end boost namespace
|
||||
|
||||
#endif // BOOST_COMPUTE_ALGORITHM_DETAIL_REDUCE_ON_GPU_HPP
|
||||
@@ -0,0 +1,45 @@
|
||||
//---------------------------------------------------------------------------//
|
||||
// Copyright (c) 2013 Kyle Lutz <kyle.r.lutz@gmail.com>
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0
|
||||
// See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt
|
||||
//
|
||||
// See http://boostorg.github.com/compute for more information.
|
||||
//---------------------------------------------------------------------------//
|
||||
|
||||
#ifndef BOOST_COMPUTE_ALGORITHM_DETAIL_SCAN_HPP
|
||||
#define BOOST_COMPUTE_ALGORITHM_DETAIL_SCAN_HPP
|
||||
|
||||
#include <boost/compute/device.hpp>
|
||||
#include <boost/compute/algorithm/detail/scan_on_cpu.hpp>
|
||||
#include <boost/compute/algorithm/detail/scan_on_gpu.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace compute {
|
||||
namespace detail {
|
||||
|
||||
template<class InputIterator, class OutputIterator, class T, class BinaryOperator>
|
||||
inline OutputIterator scan(InputIterator first,
|
||||
InputIterator last,
|
||||
OutputIterator result,
|
||||
bool exclusive,
|
||||
T init,
|
||||
BinaryOperator op,
|
||||
command_queue &queue)
|
||||
{
|
||||
const device &device = queue.get_device();
|
||||
|
||||
if(device.type() & device::cpu){
|
||||
return scan_on_cpu(first, last, result, exclusive, init, op, queue);
|
||||
}
|
||||
else {
|
||||
return scan_on_gpu(first, last, result, exclusive, init, op, queue);
|
||||
}
|
||||
}
|
||||
|
||||
} // end detail namespace
|
||||
} // end compute namespace
|
||||
} // end boost namespace
|
||||
|
||||
#endif // BOOST_COMPUTE_ALGORITHM_DETAIL_SCAN_HPP
|
||||
@@ -0,0 +1,207 @@
|
||||
//---------------------------------------------------------------------------//
|
||||
// Copyright (c) 2016 Jakub Szuppe <j.szuppe@gmail.com>
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0
|
||||
// See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt
|
||||
//
|
||||
// See http://boostorg.github.com/compute for more information.
|
||||
//---------------------------------------------------------------------------//
|
||||
|
||||
#ifndef BOOST_COMPUTE_ALGORITHM_DETAIL_SCAN_ON_CPU_HPP
|
||||
#define BOOST_COMPUTE_ALGORITHM_DETAIL_SCAN_ON_CPU_HPP
|
||||
|
||||
#include <iterator>
|
||||
|
||||
#include <boost/compute/device.hpp>
|
||||
#include <boost/compute/kernel.hpp>
|
||||
#include <boost/compute/command_queue.hpp>
|
||||
#include <boost/compute/algorithm/detail/serial_scan.hpp>
|
||||
#include <boost/compute/detail/meta_kernel.hpp>
|
||||
#include <boost/compute/detail/iterator_range_size.hpp>
|
||||
#include <boost/compute/detail/parameter_cache.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace compute {
|
||||
namespace detail {
|
||||
|
||||
template<class InputIterator, class OutputIterator, class T, class BinaryOperator>
|
||||
inline OutputIterator scan_on_cpu(InputIterator first,
|
||||
InputIterator last,
|
||||
OutputIterator result,
|
||||
bool exclusive,
|
||||
T init,
|
||||
BinaryOperator op,
|
||||
command_queue &queue)
|
||||
{
|
||||
typedef typename
|
||||
std::iterator_traits<InputIterator>::value_type input_type;
|
||||
typedef typename
|
||||
std::iterator_traits<OutputIterator>::value_type output_type;
|
||||
|
||||
const context &context = queue.get_context();
|
||||
const device &device = queue.get_device();
|
||||
const size_t compute_units = queue.get_device().compute_units();
|
||||
|
||||
boost::shared_ptr<parameter_cache> parameters =
|
||||
detail::parameter_cache::get_global_cache(device);
|
||||
|
||||
std::string cache_key =
|
||||
"__boost_scan_cpu_" + boost::lexical_cast<std::string>(sizeof(T));
|
||||
|
||||
// for inputs smaller than serial_scan_threshold
|
||||
// serial_scan algorithm is used
|
||||
uint_ serial_scan_threshold =
|
||||
parameters->get(cache_key, "serial_scan_threshold", 16384 * sizeof(T));
|
||||
serial_scan_threshold =
|
||||
(std::max)(serial_scan_threshold, uint_(compute_units));
|
||||
|
||||
size_t count = detail::iterator_range_size(first, last);
|
||||
if(count == 0){
|
||||
return result;
|
||||
}
|
||||
else if(count < serial_scan_threshold) {
|
||||
return serial_scan(first, last, result, exclusive, init, op, queue);
|
||||
}
|
||||
|
||||
buffer block_partial_sums(context, sizeof(output_type) * compute_units );
|
||||
|
||||
// create scan kernel
|
||||
meta_kernel k("scan_on_cpu_block_scan");
|
||||
|
||||
// Arguments
|
||||
size_t count_arg = k.add_arg<uint_>("count");
|
||||
size_t init_arg = k.add_arg<output_type>("initial_value");
|
||||
size_t block_partial_sums_arg =
|
||||
k.add_arg<output_type *>(memory_object::global_memory, "block_partial_sums");
|
||||
|
||||
k <<
|
||||
"uint block = " <<
|
||||
"(uint)ceil(((float)count)/(get_global_size(0) + 1));\n" <<
|
||||
"uint index = get_global_id(0) * block;\n" <<
|
||||
"uint end = min(count, index + block);\n";
|
||||
|
||||
if(!exclusive){
|
||||
k <<
|
||||
k.decl<output_type>("sum") << " = " <<
|
||||
first[k.var<uint_>("index")] << ";\n" <<
|
||||
result[k.var<uint_>("index")] << " = sum;\n" <<
|
||||
"index++;\n";
|
||||
}
|
||||
else {
|
||||
k <<
|
||||
k.decl<output_type>("sum") << ";\n" <<
|
||||
"if(index == 0){\n" <<
|
||||
"sum = initial_value;\n" <<
|
||||
"}\n" <<
|
||||
"else {\n" <<
|
||||
"sum = " << first[k.var<uint_>("index")] << ";\n" <<
|
||||
"index++;\n" <<
|
||||
"}\n";
|
||||
}
|
||||
|
||||
k <<
|
||||
"while(index < end){\n" <<
|
||||
// load next value
|
||||
k.decl<const input_type>("value") << " = "
|
||||
<< first[k.var<uint_>("index")] << ";\n";
|
||||
|
||||
if(exclusive){
|
||||
k <<
|
||||
"if(get_global_id(0) == 0){\n" <<
|
||||
result[k.var<uint_>("index")] << " = sum;\n" <<
|
||||
"}\n";
|
||||
}
|
||||
k <<
|
||||
"sum = " << op(k.var<output_type>("sum"),
|
||||
k.var<output_type>("value")) << ";\n";
|
||||
|
||||
if(!exclusive){
|
||||
k <<
|
||||
"if(get_global_id(0) == 0){\n" <<
|
||||
result[k.var<uint_>("index")] << " = sum;\n" <<
|
||||
"}\n";
|
||||
}
|
||||
|
||||
k <<
|
||||
"index++;\n" <<
|
||||
"}\n" << // end while
|
||||
"block_partial_sums[get_global_id(0)] = sum;\n";
|
||||
|
||||
// compile scan kernel
|
||||
kernel block_scan_kernel = k.compile(context);
|
||||
|
||||
// setup kernel arguments
|
||||
block_scan_kernel.set_arg(count_arg, static_cast<uint_>(count));
|
||||
block_scan_kernel.set_arg(init_arg, static_cast<output_type>(init));
|
||||
block_scan_kernel.set_arg(block_partial_sums_arg, block_partial_sums);
|
||||
|
||||
// execute the kernel
|
||||
size_t global_work_size = compute_units;
|
||||
queue.enqueue_1d_range_kernel(block_scan_kernel, 0, global_work_size, 0);
|
||||
|
||||
// scan is done
|
||||
if(compute_units < 2) {
|
||||
return result + count;
|
||||
}
|
||||
|
||||
// final scan kernel
|
||||
meta_kernel l("scan_on_cpu_final_scan");
|
||||
|
||||
// Arguments
|
||||
count_arg = l.add_arg<uint_>("count");
|
||||
block_partial_sums_arg =
|
||||
l.add_arg<output_type *>(memory_object::global_memory, "block_partial_sums");
|
||||
|
||||
l <<
|
||||
"uint block = " <<
|
||||
"(uint)ceil(((float)count)/(get_global_size(0) + 1));\n" <<
|
||||
"uint index = block + get_global_id(0) * block;\n" <<
|
||||
"uint end = min(count, index + block);\n" <<
|
||||
|
||||
k.decl<output_type>("sum") << " = block_partial_sums[0];\n" <<
|
||||
"for(uint i = 0; i < get_global_id(0); i++) {\n" <<
|
||||
"sum = " << op(k.var<output_type>("sum"),
|
||||
k.var<output_type>("block_partial_sums[i + 1]")) << ";\n" <<
|
||||
"}\n" <<
|
||||
|
||||
"while(index < end){\n";
|
||||
if(exclusive){
|
||||
l <<
|
||||
l.decl<output_type>("value") << " = "
|
||||
<< first[k.var<uint_>("index")] << ";\n" <<
|
||||
result[k.var<uint_>("index")] << " = sum;\n" <<
|
||||
"sum = " << op(k.var<output_type>("sum"),
|
||||
k.var<output_type>("value")) << ";\n";
|
||||
}
|
||||
else {
|
||||
l <<
|
||||
"sum = " << op(k.var<output_type>("sum"),
|
||||
first[k.var<uint_>("index")]) << ";\n" <<
|
||||
result[k.var<uint_>("index")] << " = sum;\n";
|
||||
}
|
||||
l <<
|
||||
"index++;\n" <<
|
||||
"}\n";
|
||||
|
||||
|
||||
// compile scan kernel
|
||||
kernel final_scan_kernel = l.compile(context);
|
||||
|
||||
// setup kernel arguments
|
||||
final_scan_kernel.set_arg(count_arg, static_cast<uint_>(count));
|
||||
final_scan_kernel.set_arg(block_partial_sums_arg, block_partial_sums);
|
||||
|
||||
// execute the kernel
|
||||
global_work_size = compute_units;
|
||||
queue.enqueue_1d_range_kernel(final_scan_kernel, 0, global_work_size, 0);
|
||||
|
||||
// return iterator pointing to the end of the result range
|
||||
return result + count;
|
||||
}
|
||||
|
||||
} // end detail namespace
|
||||
} // end compute namespace
|
||||
} // end boost namespace
|
||||
|
||||
#endif // BOOST_COMPUTE_ALGORITHM_DETAIL_SCAN_ON_CPU_HPP
|
||||
@@ -0,0 +1,330 @@
|
||||
//---------------------------------------------------------------------------//
|
||||
// Copyright (c) 2013 Kyle Lutz <kyle.r.lutz@gmail.com>
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0
|
||||
// See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt
|
||||
//
|
||||
// See http://boostorg.github.com/compute for more information.
|
||||
//---------------------------------------------------------------------------//
|
||||
|
||||
#ifndef BOOST_COMPUTE_ALGORITHM_DETAIL_SCAN_ON_GPU_HPP
|
||||
#define BOOST_COMPUTE_ALGORITHM_DETAIL_SCAN_ON_GPU_HPP
|
||||
|
||||
#include <boost/compute/kernel.hpp>
|
||||
#include <boost/compute/detail/meta_kernel.hpp>
|
||||
#include <boost/compute/command_queue.hpp>
|
||||
#include <boost/compute/container/vector.hpp>
|
||||
#include <boost/compute/detail/iterator_range_size.hpp>
|
||||
#include <boost/compute/memory/local_buffer.hpp>
|
||||
#include <boost/compute/iterator/buffer_iterator.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace compute {
|
||||
namespace detail {
|
||||
|
||||
template<class InputIterator, class OutputIterator, class BinaryOperator>
|
||||
class local_scan_kernel : public meta_kernel
|
||||
{
|
||||
public:
|
||||
local_scan_kernel(InputIterator first,
|
||||
InputIterator last,
|
||||
OutputIterator result,
|
||||
bool exclusive,
|
||||
BinaryOperator op)
|
||||
: meta_kernel("local_scan")
|
||||
{
|
||||
typedef typename std::iterator_traits<InputIterator>::value_type T;
|
||||
|
||||
(void) last;
|
||||
|
||||
bool checked = true;
|
||||
|
||||
m_block_sums_arg = add_arg<T *>(memory_object::global_memory, "block_sums");
|
||||
m_scratch_arg = add_arg<T *>(memory_object::local_memory, "scratch");
|
||||
m_block_size_arg = add_arg<const cl_uint>("block_size");
|
||||
m_count_arg = add_arg<const cl_uint>("count");
|
||||
m_init_value_arg = add_arg<const T>("init");
|
||||
|
||||
// work-item parameters
|
||||
*this <<
|
||||
"const uint gid = get_global_id(0);\n" <<
|
||||
"const uint lid = get_local_id(0);\n";
|
||||
|
||||
// check against data size
|
||||
if(checked){
|
||||
*this <<
|
||||
"if(gid < count){\n";
|
||||
}
|
||||
|
||||
// copy values from input to local memory
|
||||
if(exclusive){
|
||||
*this <<
|
||||
decl<const T>("local_init") << "= (gid == 0) ? init : 0;\n" <<
|
||||
"if(lid == 0){ scratch[lid] = local_init; }\n" <<
|
||||
"else { scratch[lid] = " << first[expr<cl_uint>("gid-1")] << "; }\n";
|
||||
}
|
||||
else{
|
||||
*this <<
|
||||
"scratch[lid] = " << first[expr<cl_uint>("gid")] << ";\n";
|
||||
}
|
||||
|
||||
if(checked){
|
||||
*this <<
|
||||
"}\n"
|
||||
"else {\n" <<
|
||||
" scratch[lid] = 0;\n" <<
|
||||
"}\n";
|
||||
}
|
||||
|
||||
// wait for all threads to read from input
|
||||
*this <<
|
||||
"barrier(CLK_LOCAL_MEM_FENCE);\n";
|
||||
|
||||
// perform scan
|
||||
*this <<
|
||||
"for(uint i = 1; i < block_size; i <<= 1){\n" <<
|
||||
" " << decl<const T>("x") << " = lid >= i ? scratch[lid-i] : 0;\n" <<
|
||||
" barrier(CLK_LOCAL_MEM_FENCE);\n" <<
|
||||
" if(lid >= i){\n" <<
|
||||
" scratch[lid] = " << op(var<T>("scratch[lid]"), var<T>("x")) << ";\n" <<
|
||||
" }\n" <<
|
||||
" barrier(CLK_LOCAL_MEM_FENCE);\n" <<
|
||||
"}\n";
|
||||
|
||||
// copy results to output
|
||||
if(checked){
|
||||
*this <<
|
||||
"if(gid < count){\n";
|
||||
}
|
||||
|
||||
*this <<
|
||||
result[expr<cl_uint>("gid")] << " = scratch[lid];\n";
|
||||
|
||||
if(checked){
|
||||
*this << "}\n";
|
||||
}
|
||||
|
||||
// store sum for the block
|
||||
if(exclusive){
|
||||
*this <<
|
||||
"if(lid == block_size - 1){\n" <<
|
||||
" block_sums[get_group_id(0)] = " <<
|
||||
op(first[expr<cl_uint>("gid")], var<T>("scratch[lid]")) <<
|
||||
";\n" <<
|
||||
"}\n";
|
||||
}
|
||||
else {
|
||||
*this <<
|
||||
"if(lid == block_size - 1){\n" <<
|
||||
" block_sums[get_group_id(0)] = scratch[lid];\n" <<
|
||||
"}\n";
|
||||
}
|
||||
}
|
||||
|
||||
size_t m_block_sums_arg;
|
||||
size_t m_scratch_arg;
|
||||
size_t m_block_size_arg;
|
||||
size_t m_count_arg;
|
||||
size_t m_init_value_arg;
|
||||
};
|
||||
|
||||
template<class T, class BinaryOperator>
|
||||
class write_scanned_output_kernel : public meta_kernel
|
||||
{
|
||||
public:
|
||||
write_scanned_output_kernel(BinaryOperator op)
|
||||
: meta_kernel("write_scanned_output")
|
||||
{
|
||||
bool checked = true;
|
||||
|
||||
m_output_arg = add_arg<T *>(memory_object::global_memory, "output");
|
||||
m_block_sums_arg = add_arg<const T *>(memory_object::global_memory, "block_sums");
|
||||
m_count_arg = add_arg<const cl_uint>("count");
|
||||
|
||||
// work-item parameters
|
||||
*this <<
|
||||
"const uint gid = get_global_id(0);\n" <<
|
||||
"const uint block_id = get_group_id(0);\n";
|
||||
|
||||
// check against data size
|
||||
if(checked){
|
||||
*this << "if(gid < count){\n";
|
||||
}
|
||||
|
||||
// write output
|
||||
*this <<
|
||||
"output[gid] = " <<
|
||||
op(var<T>("block_sums[block_id]"), var<T>("output[gid] ")) << ";\n";
|
||||
|
||||
if(checked){
|
||||
*this << "}\n";
|
||||
}
|
||||
}
|
||||
|
||||
size_t m_output_arg;
|
||||
size_t m_block_sums_arg;
|
||||
size_t m_count_arg;
|
||||
};
|
||||
|
||||
template<class InputIterator>
|
||||
inline size_t pick_scan_block_size(InputIterator first, InputIterator last)
|
||||
{
|
||||
size_t count = iterator_range_size(first, last);
|
||||
|
||||
if(count == 0) { return 0; }
|
||||
else if(count <= 1) { return 1; }
|
||||
else if(count <= 2) { return 2; }
|
||||
else if(count <= 4) { return 4; }
|
||||
else if(count <= 8) { return 8; }
|
||||
else if(count <= 16) { return 16; }
|
||||
else if(count <= 32) { return 32; }
|
||||
else if(count <= 64) { return 64; }
|
||||
else if(count <= 128) { return 128; }
|
||||
else { return 256; }
|
||||
}
|
||||
|
||||
template<class InputIterator, class OutputIterator, class T, class BinaryOperator>
|
||||
inline OutputIterator scan_impl(InputIterator first,
|
||||
InputIterator last,
|
||||
OutputIterator result,
|
||||
bool exclusive,
|
||||
T init,
|
||||
BinaryOperator op,
|
||||
command_queue &queue)
|
||||
{
|
||||
typedef typename
|
||||
std::iterator_traits<InputIterator>::value_type
|
||||
input_type;
|
||||
typedef typename
|
||||
std::iterator_traits<InputIterator>::difference_type
|
||||
difference_type;
|
||||
typedef typename
|
||||
std::iterator_traits<OutputIterator>::value_type
|
||||
output_type;
|
||||
|
||||
const context &context = queue.get_context();
|
||||
const size_t count = detail::iterator_range_size(first, last);
|
||||
|
||||
size_t block_size = pick_scan_block_size(first, last);
|
||||
size_t block_count = count / block_size;
|
||||
|
||||
if(block_count * block_size < count){
|
||||
block_count++;
|
||||
}
|
||||
|
||||
::boost::compute::vector<input_type> block_sums(block_count, context);
|
||||
|
||||
// zero block sums
|
||||
input_type zero;
|
||||
std::memset(&zero, 0, sizeof(input_type));
|
||||
::boost::compute::fill(block_sums.begin(), block_sums.end(), zero, queue);
|
||||
|
||||
// local scan
|
||||
local_scan_kernel<InputIterator, OutputIterator, BinaryOperator>
|
||||
local_scan_kernel(first, last, result, exclusive, op);
|
||||
|
||||
::boost::compute::kernel kernel = local_scan_kernel.compile(context);
|
||||
kernel.set_arg(local_scan_kernel.m_scratch_arg, local_buffer<input_type>(block_size));
|
||||
kernel.set_arg(local_scan_kernel.m_block_sums_arg, block_sums);
|
||||
kernel.set_arg(local_scan_kernel.m_block_size_arg, static_cast<cl_uint>(block_size));
|
||||
kernel.set_arg(local_scan_kernel.m_count_arg, static_cast<cl_uint>(count));
|
||||
kernel.set_arg(local_scan_kernel.m_init_value_arg, static_cast<output_type>(init));
|
||||
|
||||
queue.enqueue_1d_range_kernel(kernel,
|
||||
0,
|
||||
block_count * block_size,
|
||||
block_size);
|
||||
|
||||
// inclusive scan block sums
|
||||
if(block_count > 1){
|
||||
scan_impl(block_sums.begin(),
|
||||
block_sums.end(),
|
||||
block_sums.begin(),
|
||||
false,
|
||||
init,
|
||||
op,
|
||||
queue
|
||||
);
|
||||
}
|
||||
|
||||
// add block sums to each block
|
||||
if(block_count > 1){
|
||||
write_scanned_output_kernel<input_type, BinaryOperator>
|
||||
write_output_kernel(op);
|
||||
kernel = write_output_kernel.compile(context);
|
||||
kernel.set_arg(write_output_kernel.m_output_arg, result.get_buffer());
|
||||
kernel.set_arg(write_output_kernel.m_block_sums_arg, block_sums);
|
||||
kernel.set_arg(write_output_kernel.m_count_arg, static_cast<cl_uint>(count));
|
||||
|
||||
queue.enqueue_1d_range_kernel(kernel,
|
||||
block_size,
|
||||
block_count * block_size,
|
||||
block_size);
|
||||
}
|
||||
|
||||
return result + static_cast<difference_type>(count);
|
||||
}
|
||||
|
||||
template<class InputIterator, class OutputIterator, class T, class BinaryOperator>
|
||||
inline OutputIterator dispatch_scan(InputIterator first,
|
||||
InputIterator last,
|
||||
OutputIterator result,
|
||||
bool exclusive,
|
||||
T init,
|
||||
BinaryOperator op,
|
||||
command_queue &queue)
|
||||
{
|
||||
return scan_impl(first, last, result, exclusive, init, op, queue);
|
||||
}
|
||||
|
||||
template<class InputIterator, class T, class BinaryOperator>
|
||||
inline InputIterator dispatch_scan(InputIterator first,
|
||||
InputIterator last,
|
||||
InputIterator result,
|
||||
bool exclusive,
|
||||
T init,
|
||||
BinaryOperator op,
|
||||
command_queue &queue)
|
||||
{
|
||||
typedef typename std::iterator_traits<InputIterator>::value_type value_type;
|
||||
|
||||
if(first == result){
|
||||
// scan input in-place
|
||||
const context &context = queue.get_context();
|
||||
|
||||
// make a temporary copy the input
|
||||
size_t count = iterator_range_size(first, last);
|
||||
vector<value_type> tmp(count, context);
|
||||
copy(first, last, tmp.begin(), queue);
|
||||
|
||||
// scan from temporary values
|
||||
return scan_impl(tmp.begin(), tmp.end(), first, exclusive, init, op, queue);
|
||||
}
|
||||
else {
|
||||
// scan input to output
|
||||
return scan_impl(first, last, result, exclusive, init, op, queue);
|
||||
}
|
||||
}
|
||||
|
||||
template<class InputIterator, class OutputIterator, class T, class BinaryOperator>
|
||||
inline OutputIterator scan_on_gpu(InputIterator first,
|
||||
InputIterator last,
|
||||
OutputIterator result,
|
||||
bool exclusive,
|
||||
T init,
|
||||
BinaryOperator op,
|
||||
command_queue &queue)
|
||||
{
|
||||
if(first == last){
|
||||
return result;
|
||||
}
|
||||
|
||||
return dispatch_scan(first, last, result, exclusive, init, op, queue);
|
||||
}
|
||||
|
||||
} // end detail namespace
|
||||
} // end compute namespace
|
||||
} // end boost namespace
|
||||
|
||||
#endif // BOOST_COMPUTE_ALGORITHM_DETAIL_SCAN_ON_GPU_HPP
|
||||
@@ -0,0 +1,86 @@
|
||||
//---------------------------------------------------------------------------//
|
||||
// Copyright (c) 2014 Roshan <thisisroshansmail@gmail.com>
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0
|
||||
// See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt
|
||||
//
|
||||
// See http://boostorg.github.com/compute for more information.
|
||||
//---------------------------------------------------------------------------//
|
||||
|
||||
#ifndef BOOST_COMPUTE_ALGORITHM_DETAIL_SEARCH_ALL_HPP
|
||||
#define BOOST_COMPUTE_ALGORITHM_DETAIL_SEARCH_ALL_HPP
|
||||
|
||||
#include <boost/compute/algorithm/copy.hpp>
|
||||
#include <boost/compute/container/vector.hpp>
|
||||
#include <boost/compute/detail/iterator_range_size.hpp>
|
||||
#include <boost/compute/detail/meta_kernel.hpp>
|
||||
#include <boost/compute/lambda.hpp>
|
||||
#include <boost/compute/system.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace compute {
|
||||
namespace detail {
|
||||
|
||||
///
|
||||
/// \brief Search kernel class
|
||||
///
|
||||
/// Subclass of meta_kernel which is capable of performing pattern matching
|
||||
///
|
||||
template<class PatternIterator, class TextIterator, class OutputIterator>
|
||||
class search_kernel : public meta_kernel
|
||||
{
|
||||
public:
|
||||
search_kernel() : meta_kernel("search")
|
||||
{}
|
||||
|
||||
void set_range(PatternIterator p_first,
|
||||
PatternIterator p_last,
|
||||
TextIterator t_first,
|
||||
TextIterator t_last,
|
||||
OutputIterator result)
|
||||
{
|
||||
m_p_count = iterator_range_size(p_first, p_last);
|
||||
m_p_count_arg = add_arg<uint_>("p_count");
|
||||
|
||||
m_count = iterator_range_size(t_first, t_last);
|
||||
m_count = m_count + 1 - m_p_count;
|
||||
|
||||
*this <<
|
||||
"uint i = get_global_id(0);\n" <<
|
||||
"const uint i1 = i;\n" <<
|
||||
"uint j;\n" <<
|
||||
"for(j = 0; j<p_count; j++,i++)\n" <<
|
||||
"{\n" <<
|
||||
" if(" << p_first[expr<uint_>("j")] << " != " <<
|
||||
t_first[expr<uint_>("i")] << ")\n" <<
|
||||
" j = p_count + 1;\n" <<
|
||||
"}\n" <<
|
||||
"if(j == p_count)\n" <<
|
||||
result[expr<uint_>("i1")] << " = 1;\n" <<
|
||||
"else\n" <<
|
||||
result[expr<uint_>("i1")] << " = 0;\n";
|
||||
}
|
||||
|
||||
event exec(command_queue &queue)
|
||||
{
|
||||
if(m_count == 0) {
|
||||
return event();
|
||||
}
|
||||
|
||||
set_arg(m_p_count_arg, uint_(m_p_count));
|
||||
|
||||
return exec_1d(queue, 0, m_count);
|
||||
}
|
||||
|
||||
private:
|
||||
size_t m_p_count;
|
||||
size_t m_p_count_arg;
|
||||
size_t m_count;
|
||||
};
|
||||
|
||||
} //end detail namespace
|
||||
} //end compute namespace
|
||||
} //end boost namespace
|
||||
|
||||
#endif // BOOST_COMPUTE_ALGORITHM_DETAIL_SEARCH_ALL_HPP
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
//---------------------------------------------------------------------------//
|
||||
// Copyright (c) 2013 Kyle Lutz <kyle.r.lutz@gmail.com>
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0
|
||||
// See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt
|
||||
//
|
||||
// See http://boostorg.github.com/compute for more information.
|
||||
//---------------------------------------------------------------------------//
|
||||
|
||||
#ifndef BOOST_COMPUTE_ALGORITHM_DETAIL_SERIAL_ACCUMULATE_HPP
|
||||
#define BOOST_COMPUTE_ALGORITHM_DETAIL_SERIAL_ACCUMULATE_HPP
|
||||
|
||||
#include <boost/compute/command_queue.hpp>
|
||||
#include <boost/compute/detail/meta_kernel.hpp>
|
||||
#include <boost/compute/detail/iterator_range_size.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace compute {
|
||||
namespace detail {
|
||||
|
||||
template<class InputIterator, class OutputIterator, class T, class BinaryFunction>
|
||||
inline void serial_accumulate(InputIterator first,
|
||||
InputIterator last,
|
||||
OutputIterator result,
|
||||
T init,
|
||||
BinaryFunction function,
|
||||
command_queue &queue)
|
||||
{
|
||||
const context &context = queue.get_context();
|
||||
size_t count = detail::iterator_range_size(first, last);
|
||||
|
||||
meta_kernel k("serial_accumulate");
|
||||
size_t init_arg = k.add_arg<T>("init");
|
||||
size_t count_arg = k.add_arg<cl_uint>("count");
|
||||
|
||||
k <<
|
||||
k.decl<T>("result") << " = init;\n" <<
|
||||
"for(uint i = 0; i < count; i++)\n" <<
|
||||
" result = " << function(k.var<T>("result"),
|
||||
first[k.var<cl_uint>("i")]) << ";\n" <<
|
||||
result[0] << " = result;\n";
|
||||
|
||||
kernel kernel = k.compile(context);
|
||||
|
||||
kernel.set_arg(init_arg, init);
|
||||
kernel.set_arg(count_arg, static_cast<cl_uint>(count));
|
||||
|
||||
queue.enqueue_task(kernel);
|
||||
}
|
||||
|
||||
} // end detail namespace
|
||||
} // end compute namespace
|
||||
} // end boost namespace
|
||||
|
||||
#endif // BOOST_COMPUTE_ALGORITHM_DETAIL_SERIAL_ACCUMULATE_HPP
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
//---------------------------------------------------------------------------//
|
||||
// Copyright (c) 2013 Kyle Lutz <kyle.r.lutz@gmail.com>
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0
|
||||
// See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt
|
||||
//
|
||||
// See http://boostorg.github.com/compute for more information.
|
||||
//---------------------------------------------------------------------------//
|
||||
|
||||
#ifndef BOOST_COMPUTE_ALGORITHM_DETAIL_SERIAL_COUNT_IF_HPP
|
||||
#define BOOST_COMPUTE_ALGORITHM_DETAIL_SERIAL_COUNT_IF_HPP
|
||||
|
||||
#include <iterator>
|
||||
|
||||
#include <boost/compute/container/detail/scalar.hpp>
|
||||
#include <boost/compute/detail/meta_kernel.hpp>
|
||||
#include <boost/compute/detail/iterator_range_size.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace compute {
|
||||
namespace detail {
|
||||
|
||||
// counts values that match the predicate using a single thread
|
||||
template<class InputIterator, class Predicate>
|
||||
inline size_t serial_count_if(InputIterator first,
|
||||
InputIterator last,
|
||||
Predicate predicate,
|
||||
command_queue &queue)
|
||||
{
|
||||
typedef typename std::iterator_traits<InputIterator>::value_type value_type;
|
||||
|
||||
const context &context = queue.get_context();
|
||||
size_t size = iterator_range_size(first, last);
|
||||
|
||||
meta_kernel k("serial_count_if");
|
||||
k.add_set_arg("size", static_cast<uint_>(size));
|
||||
size_t result_arg = k.add_arg<uint_ *>(memory_object::global_memory, "result");
|
||||
|
||||
k <<
|
||||
"uint count = 0;\n" <<
|
||||
"for(uint i = 0; i < size; i++){\n" <<
|
||||
k.decl<const value_type>("value") << "="
|
||||
<< first[k.var<uint_>("i")] << ";\n" <<
|
||||
"if(" << predicate(k.var<const value_type>("value")) << "){\n" <<
|
||||
"count++;\n" <<
|
||||
"}\n"
|
||||
"}\n"
|
||||
"*result = count;\n";
|
||||
|
||||
kernel kernel = k.compile(context);
|
||||
|
||||
// setup result buffer
|
||||
scalar<uint_> result(context);
|
||||
kernel.set_arg(result_arg, result.get_buffer());
|
||||
|
||||
// run kernel
|
||||
queue.enqueue_task(kernel);
|
||||
|
||||
// read index
|
||||
return result.read(queue);
|
||||
}
|
||||
|
||||
} // end detail namespace
|
||||
} // end compute namespace
|
||||
} // end boost namespace
|
||||
|
||||
#endif // BOOST_COMPUTE_ALGORITHM_DETAIL_SERIAL_COUNT_IF_HPP
|
||||
+87
@@ -0,0 +1,87 @@
|
||||
//---------------------------------------------------------------------------//
|
||||
// Copyright (c) 2013 Kyle Lutz <kyle.r.lutz@gmail.com>
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0
|
||||
// See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt
|
||||
//
|
||||
// See http://boostorg.github.com/compute for more information.
|
||||
//---------------------------------------------------------------------------//
|
||||
|
||||
#ifndef BOOST_COMPUTE_ALGORITHM_DETAIL_SERIAL_FIND_EXTREMA_HPP
|
||||
#define BOOST_COMPUTE_ALGORITHM_DETAIL_SERIAL_FIND_EXTREMA_HPP
|
||||
|
||||
#include <boost/compute/command_queue.hpp>
|
||||
#include <boost/compute/types/fundamental.hpp>
|
||||
#include <boost/compute/detail/meta_kernel.hpp>
|
||||
#include <boost/compute/detail/iterator_range_size.hpp>
|
||||
#include <boost/compute/container/detail/scalar.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace compute {
|
||||
namespace detail {
|
||||
|
||||
template<class InputIterator, class Compare>
|
||||
inline InputIterator serial_find_extrema(InputIterator first,
|
||||
InputIterator last,
|
||||
Compare compare,
|
||||
const bool find_minimum,
|
||||
command_queue &queue)
|
||||
{
|
||||
typedef typename std::iterator_traits<InputIterator>::value_type value_type;
|
||||
typedef typename std::iterator_traits<InputIterator>::difference_type difference_type;
|
||||
|
||||
const context &context = queue.get_context();
|
||||
|
||||
meta_kernel k("serial_find_extrema");
|
||||
|
||||
k <<
|
||||
k.decl<value_type>("value") << " = " << first[k.expr<uint_>("0")] << ";\n" <<
|
||||
k.decl<uint_>("value_index") << " = 0;\n" <<
|
||||
"for(uint i = 1; i < size; i++){\n" <<
|
||||
" " << k.decl<value_type>("candidate") << "="
|
||||
<< first[k.expr<uint_>("i")] << ";\n" <<
|
||||
|
||||
"#ifndef BOOST_COMPUTE_FIND_MAXIMUM\n" <<
|
||||
" if(" << compare(k.var<value_type>("candidate"),
|
||||
k.var<value_type>("value")) << "){\n" <<
|
||||
"#else\n" <<
|
||||
" if(" << compare(k.var<value_type>("value"),
|
||||
k.var<value_type>("candidate")) << "){\n" <<
|
||||
"#endif\n" <<
|
||||
|
||||
" value = candidate;\n" <<
|
||||
" value_index = i;\n" <<
|
||||
" }\n" <<
|
||||
"}\n" <<
|
||||
"*index = value_index;\n";
|
||||
|
||||
size_t index_arg_index = k.add_arg<uint_ *>(memory_object::global_memory, "index");
|
||||
size_t size_arg_index = k.add_arg<uint_>("size");
|
||||
|
||||
std::string options;
|
||||
if(!find_minimum){
|
||||
options = "-DBOOST_COMPUTE_FIND_MAXIMUM";
|
||||
}
|
||||
kernel kernel = k.compile(context, options);
|
||||
|
||||
// setup index buffer
|
||||
scalar<uint_> index(context);
|
||||
kernel.set_arg(index_arg_index, index.get_buffer());
|
||||
|
||||
// setup count
|
||||
size_t count = iterator_range_size(first, last);
|
||||
kernel.set_arg(size_arg_index, static_cast<uint_>(count));
|
||||
|
||||
// run kernel
|
||||
queue.enqueue_task(kernel);
|
||||
|
||||
// read index and return iterator
|
||||
return first + static_cast<difference_type>(index.read(queue));
|
||||
}
|
||||
|
||||
} // end detail namespace
|
||||
} // end compute namespace
|
||||
} // end boost namespace
|
||||
|
||||
#endif // BOOST_COMPUTE_ALGORITHM_DETAIL_SERIAL_FIND_EXTREMA_HPP
|
||||
@@ -0,0 +1,97 @@
|
||||
//---------------------------------------------------------------------------//
|
||||
// Copyright (c) 2013 Kyle Lutz <kyle.r.lutz@gmail.com>
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0
|
||||
// See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt
|
||||
//
|
||||
// See http://boostorg.github.com/compute for more information.
|
||||
//---------------------------------------------------------------------------//
|
||||
|
||||
#ifndef BOOST_COMPUTE_ALGORITHM_SERIAL_MERGE_HPP
|
||||
#define BOOST_COMPUTE_ALGORITHM_SERIAL_MERGE_HPP
|
||||
|
||||
#include <iterator>
|
||||
|
||||
#include <boost/compute/command_queue.hpp>
|
||||
#include <boost/compute/detail/meta_kernel.hpp>
|
||||
#include <boost/compute/detail/iterator_range_size.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace compute {
|
||||
namespace detail {
|
||||
|
||||
template<class InputIterator1,
|
||||
class InputIterator2,
|
||||
class OutputIterator,
|
||||
class Compare>
|
||||
inline OutputIterator serial_merge(InputIterator1 first1,
|
||||
InputIterator1 last1,
|
||||
InputIterator2 first2,
|
||||
InputIterator2 last2,
|
||||
OutputIterator result,
|
||||
Compare comp,
|
||||
command_queue &queue)
|
||||
{
|
||||
typedef typename
|
||||
std::iterator_traits<InputIterator1>::value_type
|
||||
input_type1;
|
||||
typedef typename
|
||||
std::iterator_traits<InputIterator2>::value_type
|
||||
input_type2;
|
||||
typedef typename
|
||||
std::iterator_traits<OutputIterator>::difference_type
|
||||
result_difference_type;
|
||||
|
||||
std::ptrdiff_t size1 = std::distance(first1, last1);
|
||||
std::ptrdiff_t size2 = std::distance(first2, last2);
|
||||
|
||||
meta_kernel k("serial_merge");
|
||||
k.add_set_arg<uint_>("size1", static_cast<uint_>(size1));
|
||||
k.add_set_arg<uint_>("size2", static_cast<uint_>(size2));
|
||||
|
||||
k <<
|
||||
"uint i = 0;\n" << // index in result range
|
||||
"uint j = 0;\n" << // index in first input range
|
||||
"uint k = 0;\n" << // index in second input range
|
||||
|
||||
// fetch initial values from each range
|
||||
k.decl<input_type1>("j_value") << " = " << first1[0] << ";\n" <<
|
||||
k.decl<input_type2>("k_value") << " = " << first2[0] << ";\n" <<
|
||||
|
||||
// merge values from both input ranges to the result range
|
||||
"while(j < size1 && k < size2){\n" <<
|
||||
" if(" << comp(k.var<input_type1>("j_value"),
|
||||
k.var<input_type2>("k_value")) << "){\n" <<
|
||||
" " << result[k.var<uint_>("i++")] << " = j_value;\n" <<
|
||||
" j_value = " << first1[k.var<uint_>("++j")] << ";\n" <<
|
||||
" }\n" <<
|
||||
" else{\n"
|
||||
" " << result[k.var<uint_>("i++")] << " = k_value;\n"
|
||||
" k_value = " << first2[k.var<uint_>("++k")] << ";\n" <<
|
||||
" }\n"
|
||||
"}\n"
|
||||
|
||||
// copy any remaining values from first range
|
||||
"while(j < size1){\n" <<
|
||||
result[k.var<uint_>("i++")] << " = " <<
|
||||
first1[k.var<uint_>("j++")] << ";\n" <<
|
||||
"}\n"
|
||||
|
||||
// copy any remaining values from second range
|
||||
"while(k < size2){\n" <<
|
||||
result[k.var<uint_>("i++")] << " = " <<
|
||||
first2[k.var<uint_>("k++")] << ";\n" <<
|
||||
"}\n";
|
||||
|
||||
// run kernel
|
||||
k.exec(queue);
|
||||
|
||||
return result + static_cast<result_difference_type>(size1 + size2);
|
||||
}
|
||||
|
||||
} // end detail namespace
|
||||
} // end compute namespace
|
||||
} // end boost namespace
|
||||
|
||||
#endif // BOOST_COMPUTE_ALGORITHM_SERIAL_MERGE_HPP
|
||||
@@ -0,0 +1,62 @@
|
||||
//---------------------------------------------------------------------------//
|
||||
// Copyright (c) 2013 Kyle Lutz <kyle.r.lutz@gmail.com>
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0
|
||||
// See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt
|
||||
//
|
||||
// See http://boostorg.github.com/compute for more information.
|
||||
//---------------------------------------------------------------------------//
|
||||
|
||||
#ifndef BOOST_COMPUTE_ALGORITHM_DETAIL_SERIAL_REDUCE_HPP
|
||||
#define BOOST_COMPUTE_ALGORITHM_DETAIL_SERIAL_REDUCE_HPP
|
||||
|
||||
#include <boost/compute/command_queue.hpp>
|
||||
#include <boost/compute/detail/meta_kernel.hpp>
|
||||
#include <boost/compute/detail/iterator_range_size.hpp>
|
||||
#include <boost/compute/type_traits/result_of.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace compute {
|
||||
namespace detail {
|
||||
|
||||
template<class InputIterator, class OutputIterator, class BinaryFunction>
|
||||
inline void serial_reduce(InputIterator first,
|
||||
InputIterator last,
|
||||
OutputIterator result,
|
||||
BinaryFunction function,
|
||||
command_queue &queue)
|
||||
{
|
||||
typedef typename
|
||||
std::iterator_traits<InputIterator>::value_type T;
|
||||
typedef typename
|
||||
::boost::compute::result_of<BinaryFunction(T, T)>::type result_type;
|
||||
|
||||
const context &context = queue.get_context();
|
||||
size_t count = detail::iterator_range_size(first, last);
|
||||
if(count == 0){
|
||||
return;
|
||||
}
|
||||
|
||||
meta_kernel k("serial_reduce");
|
||||
size_t count_arg = k.add_arg<cl_uint>("count");
|
||||
|
||||
k <<
|
||||
k.decl<result_type>("result") << " = " << first[0] << ";\n" <<
|
||||
"for(uint i = 1; i < count; i++)\n" <<
|
||||
" result = " << function(k.var<T>("result"),
|
||||
first[k.var<uint_>("i")]) << ";\n" <<
|
||||
result[0] << " = result;\n";
|
||||
|
||||
kernel kernel = k.compile(context);
|
||||
|
||||
kernel.set_arg(count_arg, static_cast<uint_>(count));
|
||||
|
||||
queue.enqueue_task(kernel);
|
||||
}
|
||||
|
||||
} // end detail namespace
|
||||
} // end compute namespace
|
||||
} // end boost namespace
|
||||
|
||||
#endif // BOOST_COMPUTE_ALGORITHM_DETAIL_SERIAL_REDUCE_HPP
|
||||
+108
@@ -0,0 +1,108 @@
|
||||
//---------------------------------------------------------------------------//
|
||||
// Copyright (c) 2015 Jakub Szuppe <j.szuppe@gmail.com>
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0
|
||||
// See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt
|
||||
//
|
||||
// See http://boostorg.github.com/compute for more information.
|
||||
//---------------------------------------------------------------------------//
|
||||
|
||||
#ifndef BOOST_COMPUTE_ALGORITHM_DETAIL_SERIAL_REDUCE_BY_KEY_HPP
|
||||
#define BOOST_COMPUTE_ALGORITHM_DETAIL_SERIAL_REDUCE_BY_KEY_HPP
|
||||
|
||||
#include <iterator>
|
||||
|
||||
#include <boost/compute/command_queue.hpp>
|
||||
#include <boost/compute/functional.hpp>
|
||||
#include <boost/compute/container/vector.hpp>
|
||||
#include <boost/compute/container/detail/scalar.hpp>
|
||||
#include <boost/compute/detail/meta_kernel.hpp>
|
||||
#include <boost/compute/detail/iterator_range_size.hpp>
|
||||
#include <boost/compute/type_traits/result_of.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace compute {
|
||||
namespace detail {
|
||||
|
||||
template<class InputKeyIterator, class InputValueIterator,
|
||||
class OutputKeyIterator, class OutputValueIterator,
|
||||
class BinaryFunction, class BinaryPredicate>
|
||||
inline size_t serial_reduce_by_key(InputKeyIterator keys_first,
|
||||
InputKeyIterator keys_last,
|
||||
InputValueIterator values_first,
|
||||
OutputKeyIterator keys_result,
|
||||
OutputValueIterator values_result,
|
||||
BinaryFunction function,
|
||||
BinaryPredicate predicate,
|
||||
command_queue &queue)
|
||||
{
|
||||
typedef typename
|
||||
std::iterator_traits<InputValueIterator>::value_type value_type;
|
||||
typedef typename
|
||||
std::iterator_traits<InputKeyIterator>::value_type key_type;
|
||||
typedef typename
|
||||
::boost::compute::result_of<BinaryFunction(value_type, value_type)>::type result_type;
|
||||
|
||||
const context &context = queue.get_context();
|
||||
size_t count = detail::iterator_range_size(keys_first, keys_last);
|
||||
if(count < 1){
|
||||
return count;
|
||||
}
|
||||
|
||||
meta_kernel k("serial_reduce_by_key");
|
||||
size_t count_arg = k.add_arg<uint_>("count");
|
||||
size_t result_size_arg = k.add_arg<uint_ *>(memory_object::global_memory,
|
||||
"result_size");
|
||||
|
||||
convert<result_type> to_result_type;
|
||||
|
||||
k <<
|
||||
k.decl<result_type>("result") <<
|
||||
" = " << to_result_type(values_first[0]) << ";\n" <<
|
||||
k.decl<key_type>("previous_key") << " = " << keys_first[0] << ";\n" <<
|
||||
k.decl<result_type>("value") << ";\n" <<
|
||||
k.decl<key_type>("key") << ";\n" <<
|
||||
|
||||
k.decl<uint_>("size") << " = 1;\n" <<
|
||||
|
||||
keys_result[0] << " = previous_key;\n" <<
|
||||
values_result[0] << " = result;\n" <<
|
||||
|
||||
"for(ulong i = 1; i < count; i++) {\n" <<
|
||||
" value = " << to_result_type(values_first[k.var<uint_>("i")]) << ";\n" <<
|
||||
" key = " << keys_first[k.var<uint_>("i")] << ";\n" <<
|
||||
" if (" << predicate(k.var<key_type>("previous_key"),
|
||||
k.var<key_type>("key")) << ") {\n" <<
|
||||
|
||||
" result = " << function(k.var<result_type>("result"),
|
||||
k.var<result_type>("value")) << ";\n" <<
|
||||
" }\n " <<
|
||||
" else { \n" <<
|
||||
keys_result[k.var<uint_>("size - 1")] << " = previous_key;\n" <<
|
||||
values_result[k.var<uint_>("size - 1")] << " = result;\n" <<
|
||||
" result = value;\n" <<
|
||||
" size++;\n" <<
|
||||
" } \n" <<
|
||||
" previous_key = key;\n" <<
|
||||
"}\n" <<
|
||||
keys_result[k.var<uint_>("size - 1")] << " = previous_key;\n" <<
|
||||
values_result[k.var<uint_>("size - 1")] << " = result;\n" <<
|
||||
"*result_size = size;";
|
||||
|
||||
kernel kernel = k.compile(context);
|
||||
|
||||
scalar<uint_> result_size(context);
|
||||
kernel.set_arg(result_size_arg, result_size.get_buffer());
|
||||
kernel.set_arg(count_arg, static_cast<uint_>(count));
|
||||
|
||||
queue.enqueue_task(kernel);
|
||||
|
||||
return static_cast<size_t>(result_size.read(queue));
|
||||
}
|
||||
|
||||
} // end detail namespace
|
||||
} // end compute namespace
|
||||
} // end boost namespace
|
||||
|
||||
#endif // BOOST_COMPUTE_ALGORITHM_DETAIL_SERIAL_REDUCE_BY_KEY_HPP
|
||||
@@ -0,0 +1,103 @@
|
||||
//---------------------------------------------------------------------------//
|
||||
// Copyright (c) 2013 Kyle Lutz <kyle.r.lutz@gmail.com>
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0
|
||||
// See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt
|
||||
//
|
||||
// See http://boostorg.github.com/compute for more information.
|
||||
//---------------------------------------------------------------------------//
|
||||
|
||||
#ifndef BOOST_COMPUTE_ALGORITHM_DETAIL_SERIAL_SCAN_HPP
|
||||
#define BOOST_COMPUTE_ALGORITHM_DETAIL_SERIAL_SCAN_HPP
|
||||
|
||||
#include <iterator>
|
||||
|
||||
#include <boost/compute/device.hpp>
|
||||
#include <boost/compute/kernel.hpp>
|
||||
#include <boost/compute/command_queue.hpp>
|
||||
#include <boost/compute/detail/meta_kernel.hpp>
|
||||
#include <boost/compute/detail/iterator_range_size.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace compute {
|
||||
namespace detail {
|
||||
|
||||
template<class InputIterator, class OutputIterator, class T, class BinaryOperator>
|
||||
inline OutputIterator serial_scan(InputIterator first,
|
||||
InputIterator last,
|
||||
OutputIterator result,
|
||||
bool exclusive,
|
||||
T init,
|
||||
BinaryOperator op,
|
||||
command_queue &queue)
|
||||
{
|
||||
if(first == last){
|
||||
return result;
|
||||
}
|
||||
|
||||
typedef typename
|
||||
std::iterator_traits<InputIterator>::value_type input_type;
|
||||
typedef typename
|
||||
std::iterator_traits<OutputIterator>::value_type output_type;
|
||||
|
||||
const context &context = queue.get_context();
|
||||
|
||||
// create scan kernel
|
||||
meta_kernel k("serial_scan");
|
||||
|
||||
// Arguments
|
||||
size_t n_arg = k.add_arg<ulong_>("n");
|
||||
size_t init_arg = k.add_arg<output_type>("initial_value");
|
||||
|
||||
if(!exclusive){
|
||||
k <<
|
||||
k.decl<const ulong_>("start_idx") << " = 1;\n" <<
|
||||
k.decl<output_type>("sum") << " = " << first[0] << ";\n" <<
|
||||
result[0] << " = sum;\n";
|
||||
}
|
||||
else {
|
||||
k <<
|
||||
k.decl<const ulong_>("start_idx") << " = 0;\n" <<
|
||||
k.decl<output_type>("sum") << " = initial_value;\n";
|
||||
}
|
||||
|
||||
k <<
|
||||
"for(ulong i = start_idx; i < n; i++){\n" <<
|
||||
k.decl<const input_type>("x") << " = "
|
||||
<< first[k.var<ulong_>("i")] << ";\n";
|
||||
|
||||
if(exclusive){
|
||||
k << result[k.var<ulong_>("i")] << " = sum;\n";
|
||||
}
|
||||
|
||||
k << " sum = "
|
||||
<< op(k.var<output_type>("sum"), k.var<output_type>("x"))
|
||||
<< ";\n";
|
||||
|
||||
if(!exclusive){
|
||||
k << result[k.var<ulong_>("i")] << " = sum;\n";
|
||||
}
|
||||
|
||||
k << "}\n";
|
||||
|
||||
// compile scan kernel
|
||||
kernel scan_kernel = k.compile(context);
|
||||
|
||||
// setup kernel arguments
|
||||
size_t n = detail::iterator_range_size(first, last);
|
||||
scan_kernel.set_arg<ulong_>(n_arg, n);
|
||||
scan_kernel.set_arg<output_type>(init_arg, static_cast<output_type>(init));
|
||||
|
||||
// execute the kernel
|
||||
queue.enqueue_1d_range_kernel(scan_kernel, 0, 1, 1);
|
||||
|
||||
// return iterator pointing to the end of the result range
|
||||
return result + n;
|
||||
}
|
||||
|
||||
} // end detail namespace
|
||||
} // end compute namespace
|
||||
} // end boost namespace
|
||||
|
||||
#endif // BOOST_COMPUTE_ALGORITHM_DETAIL_SERIAL_SCAN_HPP
|
||||
Reference in New Issue
Block a user