//---------------------------------------------------------------------------// // Copyright (c) 2013-2014 Kyle Lutz // // 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. //---------------------------------------------------------------------------// #include #include #include #include #include #include #include #include namespace compute = boost::compute; const char fizz_buzz_source[] = BOOST_COMPUTE_STRINGIZE_SOURCE( // returns the length of the string for the number 'n'. This is used // during the first pass when we calculate the amount of space needed // for each string in the fizz-buzz sequence. inline uint fizz_buzz_string_length(uint n) { if((n % 5 == 0) && (n % 3 == 0)){ return sizeof("fizzbuzz"); } else if(n % 5 == 0){ return sizeof("fizz"); } else if(n % 3 == 0){ return sizeof("buzz"); } else { uint digits = 0; while(n){ n /= 10; digits++; } return digits + 1; } } // first-pass kernel which calculates the string length for each number // and writes it to the string_lengths array. these will then be passed // to exclusive_scan() to calculate the output offsets for each string. __kernel void fizz_buzz_allocate_strings(__global uint *string_lengths) { const uint i = get_global_id(0); const uint n = i + 1; string_lengths[i] = fizz_buzz_string_length(n); } // copy the string 's' with length 'n' to 'result' (just like strncpy()) inline void copy_string(__constant const char *s, uint n, __global char *result) { while(n--){ result[n] = s[n]; } } // reverse the string [start, end). inline void reverse_string(__global char *start, __global char *end) { while(start < end){ char tmp = *end; *end = *start; *start = tmp; start++; end--; } } // second-pass kernel which copies the fizz-buzz string for each number to // buffer using the previously calculated offsets. __kernel void fizz_buzz_copy_strings(__global const uint *offsets, __global char *buffer) { const uint i = get_global_id(0); const uint n = i + 1; const uint offset = offsets[i]; if((n % 5 == 0) && (n % 3 == 0)){ copy_string("fizzbuzz\n", 9, buffer + offset); } else if(n % 5 == 0){ copy_string("fizz\n", 5, buffer + offset); } else if(n % 3 == 0){ copy_string("buzz\n", 5, buffer + offset); } else { // convert number to string and write it to the output __global char *number = buffer + offset; uint n_ = n; while(n_){ *number++ = (n_%10) + '0'; n_ /= 10; } reverse_string(buffer + offset, number - 1); *number = '\n'; } } ); int main() { using compute::dim; using compute::uint_; // fizz-buzz up to 100 size_t n = 100; // get the default device compute::device device = compute::system::default_device(); compute::context ctx(device); compute::command_queue queue(ctx, device); // compile the fizz-buzz program compute::program fizz_buzz_program = compute::program::create_with_source(fizz_buzz_source, ctx); fizz_buzz_program.build(); // create a vector for the output string and computing offsets compute::vector output(ctx); compute::vector offsets(n, ctx); // run the allocate kernel to calculate string lengths compute::kernel allocate_kernel(fizz_buzz_program, "fizz_buzz_allocate_strings"); allocate_kernel.set_arg(0, offsets); queue.enqueue_nd_range_kernel(allocate_kernel, dim(0), dim(n), dim(1)); // allocate space for the output string output.resize( compute::accumulate(offsets.begin(), offsets.end(), 0, queue) ); // scan string lengths for each number to calculate the output offsets compute::exclusive_scan( offsets.begin(), offsets.end(), offsets.begin(), queue ); // run the copy kernel to fill the output buffer compute::kernel copy_kernel(fizz_buzz_program, "fizz_buzz_copy_strings"); copy_kernel.set_arg(0, offsets); copy_kernel.set_arg(1, output); queue.enqueue_nd_range_kernel(copy_kernel, dim(0), dim(n), dim(1)); // copy the string to the host and print it to stdout std::string str; str.resize(output.size()); compute::copy(output.begin(), output.end(), str.begin(), queue); std::cout << str; return 0; }