fizz_buzz.cpp 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. //---------------------------------------------------------------------------//
  2. // Copyright (c) 2013-2014 Kyle Lutz <kyle.r.lutz@gmail.com>
  3. //
  4. // Distributed under the Boost Software License, Version 1.0
  5. // See accompanying file LICENSE_1_0.txt or copy at
  6. // http://www.boost.org/LICENSE_1_0.txt
  7. //
  8. // See http://boostorg.github.com/compute for more information.
  9. //---------------------------------------------------------------------------//
  10. #include <iostream>
  11. #include <boost/compute/system.hpp>
  12. #include <boost/compute/algorithm/copy.hpp>
  13. #include <boost/compute/algorithm/accumulate.hpp>
  14. #include <boost/compute/algorithm/exclusive_scan.hpp>
  15. #include <boost/compute/container/vector.hpp>
  16. #include <boost/compute/utility/dim.hpp>
  17. #include <boost/compute/utility/source.hpp>
  18. namespace compute = boost::compute;
  19. const char fizz_buzz_source[] = BOOST_COMPUTE_STRINGIZE_SOURCE(
  20. // returns the length of the string for the number 'n'. This is used
  21. // during the first pass when we calculate the amount of space needed
  22. // for each string in the fizz-buzz sequence.
  23. inline uint fizz_buzz_string_length(uint n)
  24. {
  25. if((n % 5 == 0) && (n % 3 == 0)){
  26. return sizeof("fizzbuzz");
  27. }
  28. else if(n % 5 == 0){
  29. return sizeof("fizz");
  30. }
  31. else if(n % 3 == 0){
  32. return sizeof("buzz");
  33. }
  34. else {
  35. uint digits = 0;
  36. while(n){
  37. n /= 10;
  38. digits++;
  39. }
  40. return digits + 1;
  41. }
  42. }
  43. // first-pass kernel which calculates the string length for each number
  44. // and writes it to the string_lengths array. these will then be passed
  45. // to exclusive_scan() to calculate the output offsets for each string.
  46. __kernel void fizz_buzz_allocate_strings(__global uint *string_lengths)
  47. {
  48. const uint i = get_global_id(0);
  49. const uint n = i + 1;
  50. string_lengths[i] = fizz_buzz_string_length(n);
  51. }
  52. // copy the string 's' with length 'n' to 'result' (just like strncpy())
  53. inline void copy_string(__constant const char *s, uint n, __global char *result)
  54. {
  55. while(n--){
  56. result[n] = s[n];
  57. }
  58. }
  59. // reverse the string [start, end).
  60. inline void reverse_string(__global char *start, __global char *end)
  61. {
  62. while(start < end){
  63. char tmp = *end;
  64. *end = *start;
  65. *start = tmp;
  66. start++;
  67. end--;
  68. }
  69. }
  70. // second-pass kernel which copies the fizz-buzz string for each number to
  71. // buffer using the previously calculated offsets.
  72. __kernel void fizz_buzz_copy_strings(__global const uint *offsets, __global char *buffer)
  73. {
  74. const uint i = get_global_id(0);
  75. const uint n = i + 1;
  76. const uint offset = offsets[i];
  77. if((n % 5 == 0) && (n % 3 == 0)){
  78. copy_string("fizzbuzz\n", 9, buffer + offset);
  79. }
  80. else if(n % 5 == 0){
  81. copy_string("fizz\n", 5, buffer + offset);
  82. }
  83. else if(n % 3 == 0){
  84. copy_string("buzz\n", 5, buffer + offset);
  85. }
  86. else {
  87. // convert number to string and write it to the output
  88. __global char *number = buffer + offset;
  89. uint n_ = n;
  90. while(n_){
  91. *number++ = (n_%10) + '0';
  92. n_ /= 10;
  93. }
  94. reverse_string(buffer + offset, number - 1);
  95. *number = '\n';
  96. }
  97. }
  98. );
  99. int main()
  100. {
  101. using compute::dim;
  102. using compute::uint_;
  103. // fizz-buzz up to 100
  104. size_t n = 100;
  105. // get the default device
  106. compute::device device = compute::system::default_device();
  107. compute::context ctx(device);
  108. compute::command_queue queue(ctx, device);
  109. // compile the fizz-buzz program
  110. compute::program fizz_buzz_program =
  111. compute::program::create_with_source(fizz_buzz_source, ctx);
  112. fizz_buzz_program.build();
  113. // create a vector for the output string and computing offsets
  114. compute::vector<char> output(ctx);
  115. compute::vector<uint_> offsets(n, ctx);
  116. // run the allocate kernel to calculate string lengths
  117. compute::kernel allocate_kernel(fizz_buzz_program, "fizz_buzz_allocate_strings");
  118. allocate_kernel.set_arg(0, offsets);
  119. queue.enqueue_nd_range_kernel(allocate_kernel, dim(0), dim(n), dim(1));
  120. // allocate space for the output string
  121. output.resize(
  122. compute::accumulate(offsets.begin(), offsets.end(), 0, queue)
  123. );
  124. // scan string lengths for each number to calculate the output offsets
  125. compute::exclusive_scan(
  126. offsets.begin(), offsets.end(), offsets.begin(), queue
  127. );
  128. // run the copy kernel to fill the output buffer
  129. compute::kernel copy_kernel(fizz_buzz_program, "fizz_buzz_copy_strings");
  130. copy_kernel.set_arg(0, offsets);
  131. copy_kernel.set_arg(1, output);
  132. queue.enqueue_nd_range_kernel(copy_kernel, dim(0), dim(n), dim(1));
  133. // copy the string to the host and print it to stdout
  134. std::string str;
  135. str.resize(output.size());
  136. compute::copy(output.begin(), output.end(), str.begin(), queue);
  137. std::cout << str;
  138. return 0;
  139. }