{"id":2951,"date":"2018-05-22T20:00:32","date_gmt":"2018-05-22T18:00:32","guid":{"rendered":"https:\/\/mariusbancila.ro\/blog\/?p=2951"},"modified":"2018-05-24T11:17:09","modified_gmt":"2018-05-24T09:17:09","slug":"transform-and-reduce-alternatives","status":"publish","type":"post","link":"https:\/\/mariusbancila.ro\/blog\/2018\/05\/22\/transform-and-reduce-alternatives\/","title":{"rendered":"Transform and reduce alternatives"},"content":{"rendered":"<p>Transform-reduce is a pattern in which a set of data is first modified by applying a transformation on each of the elements and then it is reduced to a single value. In C++, this can be implemented straightforwardly with <tt>std::transform<\/tt> and <tt>std::accumulate<\/tt>. In C++17, an alternative for <tt>std::accumulate<\/tt> is available; <tt>std::reduce<\/tt> sums a range of elements just like <tt>std::accumulate<\/tt>, except that it does so out of order. That means you cannot use it with operators that are not communicative or associative (including overloads of <tt>operator+<\/tt> that don&#8217;t exhibit these properties). On the other hand, there is yet another algorithm called <tt>std::transform_reduce<\/tt> that applies a functor to all the elements of a range and then reduces them, all in an out of order manner. And then, there are also parallel versions of these algorithms. In this post, I will try to compare the performance of these possible alternatives for implementing transform-reduce.<\/p>\n<p>Let&#8217;s start with a problem: we have a list of order prices on which we need to apply a discount; if the order price is greater then 100, then a 20% discount applies. We need to compute the total sum of all orders after discounts have been applied.<\/p>\n<p>For convenience, we will use the following function to apply a discount on an order price:<\/p>\n<pre class=\"lang:c++ decode:true \" >inline double apply_discount(double const value)\r\n{\r\n   return value &gt; 100 ? 0.8 * value : value;\r\n}<\/pre>\n<p>The traditional way to solve this problem is to first use <tt>std::transform<\/tt> to modify the elements of the range by applying the discount (with <tt>apply_discount<\/tt>) and then summing all the resulted values with <tt>std::accumulate<\/tt>. That should look like the following:<\/p>\n<pre class=\"lang:c++ decode:true \" >template &lt;typename C&gt;\r\nlong double transform_and_accummulate(C&amp;&amp; c)\r\n{\r\n   std::transform(std::begin(c), std::end(c),\r\n                  std::begin(c),\r\n                  apply_discount);\r\n\r\n   return std::accumulate(std::cbegin(c), std::cend(c), 0.0L);\r\n}<\/pre>\n<p>In C++17, we can replace <tt>std::accumulate<\/tt> with <tt>std::reduce<\/tt>, since summing the elements of the prices range can be done in any order; the result would be the same. <tt>std::reduce<\/tt> has various <a href=\"http:\/\/en.cppreference.com\/w\/cpp\/algorithm\/reduce\">overloads<\/a> but for this problem we need one that takes the range bound iterators and an initial value (and implicitly uses <tt>std::plus&lt;&gt;<\/tt> to sum the elements).<\/p>\n<pre class=\"lang:c++ decode:true \" >template &lt;typename C&gt;\r\nlong double transform_and_reduce(C&amp;&amp; c)\r\n{\r\n   std::transform(std::begin(c), std::end(c),\r\n                  std::begin(c),\r\n                  apply_discount);\r\n\r\n   return std::reduce(std::cbegin(c), std::cend(c), 0.0L);\r\n}<\/pre>\n<p>C++17 also provides a parallel version of tens of algorithms, including <tt>std::transform<\/tt> and <tt>std::reduce<\/tt> but not for <tt>std::accumulate<\/tt>. The algorithms that do have parallel versions have overloads that take an <a href=\"http:\/\/en.cppreference.com\/w\/cpp\/algorithm\/execution_policy_tag_t\">execution policy<\/a>. This can be one of:<\/p>\n<ul>\n<li><tt>std::execution::seq<\/tt>: execution of the algorithm is sequential;<\/li>\n<li><tt>std::execution::par<\/tt>: execution of the algorithm may be parallelized on the calling thread or on another thread;<\/li>\n<li><tt>std::execution::par_unseq<\/tt>: execution of the algorithm may be parallelized, vectorized, or migrated across threads.<\/li>\n<\/ul>\n<p>When using <tt>std::execution::seq<\/tt>, the execution is the same as when using an overload without an execution policy. On the other hand, <tt>std::execution::par<\/tt> and <tt>std::execution::par_unseq<\/tt> may execute the algorithm in parallel. <tt>par_unseq<\/tt> requires stronger quarantees than <tt>par<\/tt>; the function calls are unsequenced with respect for each other. Because of that, it is not possible to perform vectorization unsafe operations, such as allocate or deallocate memory, acquire mutexes, use non-lockfree <tt>std::atomic<\/tt> specializations, when using this policy.<\/p>\n<p>With this in mind, we can rewrite the <tt>transform_and_reduce<\/tt> function to also specify an execution policy, as follows:<\/p>\n<pre class=\"lang:c++ decode:true \" >template &lt;typename C, class ExecutionPolicy&gt;\r\nlong double transform_and_reduce(C&amp;&amp; c, ExecutionPolicy&amp;&amp; policy)\r\n{\r\n   std::transform(std::forward&lt;ExecutionPolicy&gt;(policy),\r\n                  std::begin(c), std::end(c),\r\n                  std::begin(c),\r\n                  apply_discount);\r\n\r\n   return std::reduce(std::forward&lt;ExecutionPolicy&gt;(policy),\r\n                      std::cbegin(c), std::cend(c), 0.0L);\r\n}<\/pre>\n<p>This can be, however, replaced with the C++17 new standard algorithm <tt>std::transform_reduce<\/tt>. This again has <a href=\"http:\/\/en.cppreference.com\/w\/cpp\/algorithm\/transform_reduce\">multiple overloads<\/a> to support different scenarios and needs but the one we are interested in takes a policy, range bound iterators, an initial value, a binary functor to reduce the values (we can use <tt>std::plus&lt;&gt;<\/tt>) and a unary functor to transform the range elements.<\/p>\n<pre class=\"lang:c++ decode:true \" >template &lt;typename C, class ExecutionPolicy&gt;\r\nlong double transform_reduce(C&amp;&amp; c, ExecutionPolicy&amp;&amp; policy)\r\n{\r\n   return std::transform_reduce(\r\n      std::forward&lt;ExecutionPolicy&gt;(policy),\r\n      std::begin(c), std::end(c),\r\n      0.0L,\r\n      std::plus&lt;&gt;(),\r\n      apply_discount);\r\n}<\/pre>\n<p>The question is, how do these perform compared to each other for various range sizes. To check that, I have written the following testing program. It generates vectors of random values, from 100 to 100 million elements, calls all these functions, will all the three possible execution policies, and prints the results.<\/p>\n<pre class=\"lang:c++ decode:true \" >\r\ninline void assert_are_equal(long double const d1, long double const d2, long double diff = 0.001)\r\n{\r\n   if (std::abs(d1 - d2) &gt; diff)\r\n      std::terminate();\r\n}\r\n\r\nint main()\r\n{\r\n   std::random_device rd;\r\n   auto seed_data = std::array&lt;int, std::mt19937::state_size&gt; {};\r\n   std::generate(std::begin(seed_data), std::end(seed_data), std::ref(rd));\r\n   std::seed_seq seq(std::begin(seed_data), std::end(seed_data));\r\n   auto eng = std::mt19937{ seq };\r\n   auto dist = std::uniform_real_distribution&lt;&gt;{ 1, 250 };\r\n\r\n   std::cout\r\n      &lt;&lt; std::left &lt;&lt; std::setw(15) &lt;&lt; \"size\"\r\n      &lt;&lt; std::left &lt;&lt; std::setw(10) &lt;&lt; \"t+a\/seq\"\r\n      &lt;&lt; std::left &lt;&lt; std::setw(10) &lt;&lt; \"t+r\/seq\"\r\n      &lt;&lt; std::left &lt;&lt; std::setw(10) &lt;&lt; \"t+r\/par\"\r\n      &lt;&lt; std::left &lt;&lt; std::setw(10) &lt;&lt; \"t+r\/vec\"\r\n      &lt;&lt; std::left &lt;&lt; std::setw(10) &lt;&lt; \"t_r\/sec\"\r\n      &lt;&lt; std::left &lt;&lt; std::setw(10) &lt;&lt; \"t_r\/par\"\r\n      &lt;&lt; std::left &lt;&lt; std::setw(10) &lt;&lt; \"t_r\/vec\"\r\n      &lt;&lt; '\\n';\r\n\r\n   size_t size = 100;\r\n   for (size_t i = 0; i &lt; 13; ++i)\r\n   {\r\n      std::vector&lt;double&gt; v(size);\r\n      std::generate_n(v.data(), size, [&amp;eng, &amp;dist]() {return dist(eng); });\r\n\r\n      auto v1 = v;\r\n      auto s1 = std::chrono::high_resolution_clock::now();\r\n      auto r1 = transform_and_accummulate(v1);\r\n      auto e1 = std::chrono::high_resolution_clock::now();\r\n\r\n      auto v2 = v;\r\n      auto s2 = std::chrono::high_resolution_clock::now();\r\n      auto r2 = transform_and_reduce(v2, std::execution::seq);\r\n      auto e2 = std::chrono::high_resolution_clock::now();\r\n\r\n      auto v3 = v;\r\n      auto s3 = std::chrono::high_resolution_clock::now();\r\n      auto r3 = transform_and_reduce(v3, std::execution::par);\r\n      auto e3 = std::chrono::high_resolution_clock::now();\r\n\r\n      auto v4 = v;\r\n      auto s4 = std::chrono::high_resolution_clock::now();\r\n      auto r4 = transform_and_reduce(v4, std::execution::par_unseq);\r\n      auto e4 = std::chrono::high_resolution_clock::now();\r\n\r\n      auto v5 = v;\r\n      auto s5 = std::chrono::high_resolution_clock::now();\r\n      auto r5 = transform_reduce(v5, std::execution::seq);\r\n      auto e5 = std::chrono::high_resolution_clock::now();\r\n\r\n      auto v6 = v;\r\n      auto s6 = std::chrono::high_resolution_clock::now();\r\n      auto r6 = transform_reduce(v6, std::execution::par);\r\n      auto e6 = std::chrono::high_resolution_clock::now();\r\n\r\n      auto v7 = v;\r\n      auto s7 = std::chrono::high_resolution_clock::now();\r\n      auto r7 = transform_reduce(v7, std::execution::par_unseq);\r\n      auto e7 = std::chrono::high_resolution_clock::now();\r\n\r\n      auto t1 = std::chrono::duration_cast&lt;std::chrono::microseconds&gt;(e1 - s1);\r\n      auto t2 = std::chrono::duration_cast&lt;std::chrono::microseconds&gt;(e2 - s2);\r\n      auto t3 = std::chrono::duration_cast&lt;std::chrono::microseconds&gt;(e3 - s3);\r\n      auto t4 = std::chrono::duration_cast&lt;std::chrono::microseconds&gt;(e4 - s4);\r\n      auto t5 = std::chrono::duration_cast&lt;std::chrono::microseconds&gt;(e5 - s5);\r\n      auto t6 = std::chrono::duration_cast&lt;std::chrono::microseconds&gt;(e6 - s6);\r\n      auto t7 = std::chrono::duration_cast&lt;std::chrono::microseconds&gt;(e7 - s7);\r\n\r\n      assert_are_equal(r1, r2);\r\n      assert_are_equal(r1, r3);\r\n      assert_are_equal(r1, r4);\r\n      assert_are_equal(r1, r5);\r\n      assert_are_equal(r1, r6);\r\n      assert_are_equal(r1, r7);\r\n\r\n      std::cout\r\n         &lt;&lt; std::left &lt;&lt; std::setw(15) &lt;&lt; size\r\n         &lt;&lt; std::left &lt;&lt; std::setw(10) &lt;&lt; t1.count()\r\n         &lt;&lt; std::left &lt;&lt; std::setw(10) &lt;&lt; t2.count()\r\n         &lt;&lt; std::left &lt;&lt; std::setw(10) &lt;&lt; t3.count()\r\n         &lt;&lt; std::left &lt;&lt; std::setw(10) &lt;&lt; t4.count()\r\n         &lt;&lt; std::left &lt;&lt; std::setw(10) &lt;&lt; t5.count()\r\n         &lt;&lt; std::left &lt;&lt; std::setw(10) &lt;&lt; t6.count()\r\n         &lt;&lt; std::left &lt;&lt; std::setw(10) &lt;&lt; t7.count()\r\n         &lt;&lt; '\\n';\r\n\r\n      size *= (i % 2 == 0 ? 5 : 2);\r\n   }\r\n}<\/pre>\n<p>Here is an output example (Visual Studio 2017 15.6, release built for x64):<\/p>\n<pre class=\"lang:default decode:true \" >\r\nsize           t+a\/seq   t+r\/seq   t+r\/par   t+r\/vec   t_r\/sec   t_r\/par   t_r\/vec\r\n100            0         0         89        6         0         3         2\r\n500            2         1         37        20        1         7         6\r\n1000           4         4         37        21        4         14        20\r\n5000           22        23        52        64        22        32        36\r\n10000          47        46        76        90        45        47        49\r\n50000          229       238       170       177       220       132       136\r\n100000         456       473       296       300       491       194       188\r\n500000         2250      2688      922       890       2922      829       977\r\n1000000        5200      5117      1618      1662      4604      1457      1461\r\n5000000        24074     27139     9195      12550     24576     7296      7768\r\n10000000       46948     52491     17609     26444     46819     23617     15381\r\n50000000       237774    260770    92045     91120     234912    86378     101222\r\n100000000      475621    526457    205715    184198    475033    180723    208145\r\n<\/pre>\n<p>What I can see from here is that:<\/p>\n<ul>\n<li>until 50,000 elements <tt>std::transform<\/tt> + <tt>std::accumulate<\/tt>, sequential <tt>std::transform<\/tt> + <tt>std::reduce<\/tt> and <tt>std::transform_reduce<\/tt> have similar times<\/li>\n<li>after 50,000 elements the parallel version of <tt>std::transform_reduce<\/tt> is performing the best, with parallel <tt>std::transform<\/tt> + <tt>std::reduce<\/tt> comming close<\/li>\n<li>the <tt>par_unseq<\/tt> version of <tt>std::transform<\/tt> + <tt>std::reduce<\/tt> is slightly better than the <tt>par<\/tt> version after more than 10 million elements; that is not the case for <tt>std::transform_reduce<\/tt>, whose vectorized version is only better at around 10 million elements.<\/li>\n<\/ul>\n<p>Of course, you could argue that the call to <tt>std::transform<\/tt> is not actually needed here and the discount can be applied while computing the sum. Although this has different semantics, these functions could be simply rewritten as follows:<\/p>\n<pre class=\"lang:c++ decode:true \" >\r\ntemplate &lt;typename C&gt;\r\nlong double transform_and_accummulate(C&amp;&amp; c)\r\n{\r\n   return std::accumulate(\r\n      std::cbegin(c), std::cend(c),\r\n      0.0L,\r\n      [](auto const sum, auto const price) {\r\n         return sum + apply_discount(price); });\r\n}\r\n\r\ntemplate &lt;typename C, class ExecutionPolicy&gt;\r\nlong double transform_and_reduce(C&amp;&amp; c, ExecutionPolicy&amp;&amp; policy)\r\n{\r\n   return std::reduce(\r\n      std::forward&lt;ExecutionPolicy&gt;(policy),\r\n      std::cbegin(c), std::cend(c),\r\n      0.0L,\r\n      [](auto const sum, auto const price) {\r\n         return sum + apply_discount(price); });\r\n}<\/pre>\n<p>In this case, however, you can not execute <tt>transform_and_reduce<\/tt> with the <tt>par<\/tt> or <tt>par_unseq<\/tt> policies because they would not yield correct results.<\/p>\n<pre class=\"lang:default decode:true \" >\r\nsize           t+a\/seq   t+r\/seq\r\n100            0         0      \r\n500            2         2      \r\n1000           4         4      \r\n5000           21        21     \r\n10000          43        42     \r\n50000          223       226    \r\n100000         433       449    \r\n500000         2455      2264   \r\n1000000        4774      4605   \r\n5000000        23687     23554  \r\n10000000       45510     45672  \r\n50000000       229429    229375 \r\n100000000      459254    460425 <\/pre>\n<p>Let us complicate the problem a bit and consider a list of orders, each order having a quantity and price per item. We should again calculate the total orders price, by summing individual order prices (quantity * price) after applying a discount the same way we did earlier. We could use the following order structure:<\/p>\n<pre class=\"lang:c++ decode:true \" >struct order\r\n{\r\n   int    quantity;\r\n   double price;\r\n};<\/pre>\n<p>The implementation using <tt>std::transform<\/tt> + <tt>std::accumulate<\/tt> could look as follows:<\/p>\n<pre class=\"lang:c++ decode:true \" >template &lt;typename C&gt;\r\nlong double transform_and_accumulate(C&amp;&amp; c)\r\n{\r\n   std::transform(\r\n      std::begin(c), std::end(c),\r\n      std::begin(c),\r\n      [](order const &amp; o) {\r\n         return order{ o.quantity, apply_discount(o.price) };  });\r\n\r\n   return std::accumulate(\r\n      std::cbegin(c), std::cend(c),\r\n      0.0L,\r\n      [](auto const &amp; sum, auto const &amp; o) {\r\n         return sum + (o.quantity * o.price); });\r\n}<\/pre>\n<p>The alternative with <tt>std::transform<\/tt> + <tt>std::reduce<\/tt> is not as straight forward as it may seem. First of all, <tt>std::reduce<\/tt> cannot use the same binary functor as <tt>std::accumulate<\/tt> because of some key requirements:<\/p>\n<ul>\n<li><tt>T<\/tt>, the type of the initial value, must meet the requirements of <a href=\"http:\/\/en.cppreference.com\/w\/cpp\/concept\/MoveConstructible\">MoveConstructible<\/a>, and<\/li>\n<li><tt>binary_op(init, *first)<\/tt>, <tt>binary_op(*first, init)<\/tt>, <tt>binary_op(init, init)<\/tt>, and <tt>binary_op(*first, *first)<\/tt> must be convertible to <tt>T<\/tt>.<\/li>\n<\/ul>\n<p>That means we must perform a certain trick to make <tt>std::reduce<\/tt> work:<\/p>\n<ul>\n<li>the type of the initial value should be <tt>order<\/tt> and not <tt>long double<\/tt>, and<\/li>\n<li>the return type of the binary operation should also be <tt>order<\/tt>; this functor would actually return a new <tt>order<\/tt> value with the quantity being irrelevant (and set to zero) and the price being the accumulated total price.<\/li>\n<\/ul>\n<p>However, this no longer makes it possible for <tt>std::reduce<\/tt> to execute in parallel and yield correct results. Therefore, the implementation in this case could be as follows:<\/p>\n<pre class=\"lang:c++ decode:true \" >template &lt;typename C, class ExecutionPolicy&gt;\r\nlong double transform_and_reduce(C&amp;&amp; c, ExecutionPolicy&amp;&amp; policy)\r\n{\r\n   std::transform(\r\n      std::forward&lt;ExecutionPolicy&gt;(policy),\r\n      std::begin(c), std::end(c),\r\n      std::begin(c),\r\n      [](order const &amp; o) {\r\n         return order{ o.quantity, apply_discount(o.price) };  });\r\n\r\n   return std::reduce(   \r\n      std::cbegin(c), std::cend(c),\r\n      order{ 0, 0.0L },\r\n      [](order const &amp; sum, order const &amp; o) {\r\n         return order{ 0, sum.price + (o.quantity * o.price) }; }).price;\r\n}<\/pre>\n<p>This does not look great and it&#8217;s exactly where <tt>std::transform_reduce<\/tt> comes to the rescue. This standard algorithm allows us to supply a unary operation to transform each element of the input range; we can use a lambda that returns <tt>quantity * discount(price)<\/tt>. Then, we can use the binary functor <tt>std::plus&lt;&gt;<\/tt> to sum the results of the unary operation on an initial value. And this can all be done in parallel or even parallel unsequenced.<\/p>\n<pre class=\"lang:c++ decode:true \" >template &lt;typename C, class ExecutionPolicy&gt;\r\nlong double transform_reduce(C&amp;&amp; c, ExecutionPolicy&amp;&amp; policy)\r\n{\r\n   return std::transform_reduce(\r\n      std::forward&lt;ExecutionPolicy&gt;(policy),\r\n      std::cbegin(c), std::cend(c),\r\n      0.0L,\r\n      std::plus&lt;&gt;(),\r\n      [](order const &amp; o) {\r\n         return o.quantity * apply_discount(o.price); });\r\n}<\/pre>\n<p>The results in this case look like the following:<\/p>\n<pre class=\"lang:default decode:true \" >\r\nsize           t+a\/seq   t+r\/seq   t+r\/par   t+r\/vec   t_r\/sec   t_r\/par   t_r\/vec\r\n100            0         1         66        8         0         3         2\r\n500            2         6         14        20        2         11        11\r\n1000           5         12        54        27        4         20        18\r\n5000           29        60        80        86        22        37        26\r\n10000          60        128       135       205       45        56        51\r\n50000          392       612       495       552       227       114       107\r\n100000         590       1214      1786      1238      478       193       186\r\n500000         3155      6279      4706      4187      2526      813       704\r\n1000000        6709      12746     9185      9654      5522      1750      1374\r\n5000000        31477     63989     42310     41512     26201     8015      7190\r\n10000000       62533     128311    92530     81102     51302     16070     14188\r\n50000000       313559    643133    427432    485140    270360    101922    81585\r\n100000000      632420    1282368   877781    941224    599787    244742    284056\r\n<\/pre>\n<p>What we can see from here is that:<\/p>\n<ul>\n<li><tt>std::transform<\/tt> + <tt>std::accumulate<\/tt> performs much better than <tt>std::transform<\/tt> + <tt>std::reduce<\/tt><\/li>\n<li><tt>std::transform_reduce<\/tt> performs better than any of the two, regardless it is sequential or parallel, after about 10000 elements<\/li>\n<li>the parallel unsequenced version is better or much better comparing to sequential and parallel runs when the size of the input range is between 50,000 elements.<\/li>\n<\/ul>\n<h3>Conclusion<\/h3>\n<p>In C++17, there are various alternatives for implementing the transform-reduce pattern, sequentially, in parallel or even parallel and vectorized. These can accommodate different needs, but the performance may differ depending on the actual problem you are solving and the size of the input datasets. Therefore, you should use the one that fits your needs the best.<\/p>\n<h3>See also<\/h3>\n<ul>\n<li><a href=\"https:\/\/blog.tartanllama.xyz\/accumulate-vs-reduce\/\">std::accumulate vs. std::reduce<\/a><\/li>\n<li><a href=\"http:\/\/cpptrivia.blogspot.ro\/2017\/11\/c17-stdtransformreduce.html\">C++17: std::transform_reduce()<\/a><\/li>\n<li><a href=\"http:\/\/www.modernescpp.com\/index.php\/parallel-algorithm-of-the-standard-template-library\">Parallel Algorithms of the Standard Template Library<\/a><\/li>\n<li><a href=\"https:\/\/www.bfilipek.com\/2017\/08\/cpp17-details-parallel.html\">C++17 in details: Parallel Algorithms<\/a><\/li>\n<li><a href=\"http:\/\/www.open-std.org\/jtc1\/sc22\/wg21\/docs\/papers\/2014\/n4167.pdf\">Transform Reduce, an Additional Algorithm for C++ Extensions for Parallelism<\/a><\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>Transform-reduce is a pattern in which a set of data is first modified by applying a transformation on each of the elements and then it is reduced to a single value. In C++, this can be implemented straightforwardly with <tt>std::transform<\/tt> and <tt>std::accumulate<\/tt>. In C++17, an alternative for <tt>std::accumulate<\/tt> is available; <tt>std::reduce<\/tt> sums a range of elements just like <tt>std::accumulate<\/tt>, except that it does so out of order. That means you cannot use it with operators that are not communicative or associative (including overloads of <tt>operator+<\/tt> that don&#8217;t exhibit these properties). On the other hand, there is yet another algorithm called <tt>std::transform_reduce<\/tt> that applies a functor to all the elements of a range and then reduces them, all in an out of order manner. And then, there are also parallel versions of these algorithms. In this post, I will try to compare the performance of these possible alternatives for implementing transform-reduce.<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_uag_custom_page_level_css":"","advgb_blocks_editor_width":"","advgb_blocks_columns_visual_guide":"","_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_feature_clip_id":0,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2},"jetpack_post_was_ever_published":false},"categories":[7],"tags":[557,451,475,559,558,487],"class_list":["post-2951","post","type-post","status-publish","format-standard","hentry","category-c","tag-algorithms","tag-c","tag-c17","tag-perf","tag-reduce","tag-transform"],"aioseo_notices":[],"aioseo_head":"\n\t\t<!-- All in One SEO 5.0.1.1 - aioseo.com -->\n\t<meta name=\"robots\" content=\"max-image-preview:large\" \/>\n\t<meta name=\"author\" content=\"Marius Bancila\"\/>\n\t<meta name=\"keywords\" content=\"algorithms,c++,c++17,perf,reduce,transform\" \/>\n\t<link rel=\"canonical\" href=\"https:\/\/mariusbancila.ro\/blog\/2018\/05\/22\/transform-and-reduce-alternatives\/\" \/>\n\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 5.0.1.1\" \/>\n\t\t<meta property=\"og:locale\" content=\"en_US\" \/>\n\t\t<meta property=\"og:site_name\" content=\"Marius Bancila&#039;s Blog | About code. Mostly on C++\" \/>\n\t\t<meta property=\"og:type\" content=\"article\" \/>\n\t\t<meta property=\"og:title\" content=\"Transform and reduce alternatives\" \/>\n\t\t<meta property=\"og:url\" content=\"https:\/\/mariusbancila.ro\/blog\/2018\/05\/22\/transform-and-reduce-alternatives\/\" \/>\n\t\t<meta property=\"article:published_time\" content=\"2018-05-22T18:00:32+00:00\" \/>\n\t\t<meta property=\"article:modified_time\" content=\"2018-05-24T09:17:09+00:00\" \/>\n\t\t<meta name=\"twitter:card\" content=\"summary\" \/>\n\t\t<meta name=\"twitter:title\" content=\"Transform and reduce alternatives\" \/>\n\t\t<script type=\"application\/ld+json\" class=\"aioseo-schema\">\n\t\t\t{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/2018\\\/05\\\/22\\\/transform-and-reduce-alternatives\\\/#article\",\"name\":\"Transform and reduce alternatives\",\"headline\":\"Transform and reduce alternatives\",\"author\":{\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/author\\\/admin\\\/#author\"},\"publisher\":{\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/#organization\"},\"datePublished\":\"2018-05-22T20:00:32+02:00\",\"dateModified\":\"2018-05-24T11:17:09+02:00\",\"inLanguage\":\"en-US\",\"commentCount\":2,\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/2018\\\/05\\\/22\\\/transform-and-reduce-alternatives\\\/#webpage\"},\"isPartOf\":{\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/2018\\\/05\\\/22\\\/transform-and-reduce-alternatives\\\/#webpage\"},\"articleSection\":\"C++, algorithms, C++, C++17, perf, reduce, transform\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/2018\\\/05\\\/22\\\/transform-and-reduce-alternatives\\\/#breadcrumblist\",\"itemListElement\":[{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog#listItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\",\"nextItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/category\\\/it\\\/#listItem\",\"name\":\"IT\"}},{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/category\\\/it\\\/#listItem\",\"position\":2,\"name\":\"IT\",\"item\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/category\\\/it\\\/\",\"nextItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/category\\\/it\\\/software\\\/#listItem\",\"name\":\"Software\"},\"previousItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog#listItem\",\"name\":\"Home\"}},{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/category\\\/it\\\/software\\\/#listItem\",\"position\":3,\"name\":\"Software\",\"item\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/category\\\/it\\\/software\\\/\",\"nextItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/category\\\/it\\\/software\\\/c\\\/#listItem\",\"name\":\"C++\"},\"previousItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/category\\\/it\\\/#listItem\",\"name\":\"IT\"}},{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/category\\\/it\\\/software\\\/c\\\/#listItem\",\"position\":4,\"name\":\"C++\",\"item\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/category\\\/it\\\/software\\\/c\\\/\",\"nextItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/2018\\\/05\\\/22\\\/transform-and-reduce-alternatives\\\/#listItem\",\"name\":\"Transform and reduce alternatives\"},\"previousItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/category\\\/it\\\/software\\\/#listItem\",\"name\":\"Software\"}},{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/2018\\\/05\\\/22\\\/transform-and-reduce-alternatives\\\/#listItem\",\"position\":5,\"name\":\"Transform and reduce alternatives\",\"previousItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/category\\\/it\\\/software\\\/c\\\/#listItem\",\"name\":\"C++\"}}]},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/#organization\",\"name\":\"Marius Bancila's Blog\",\"description\":\"About code. Mostly on C++\",\"url\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/\"},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/author\\\/admin\\\/#author\",\"url\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/author\\\/admin\\\/\",\"name\":\"Marius Bancila\",\"image\":{\"@type\":\"ImageObject\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/2018\\\/05\\\/22\\\/transform-and-reduce-alternatives\\\/#authorImage\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/a84dd2831d955c38355ddea55df4df260809b88f36408bc14fd4eab8f7f131c9?s=96&d=mm&r=g\",\"width\":96,\"height\":96,\"caption\":\"Marius Bancila\"}},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/2018\\\/05\\\/22\\\/transform-and-reduce-alternatives\\\/#webpage\",\"url\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/2018\\\/05\\\/22\\\/transform-and-reduce-alternatives\\\/\",\"name\":\"Transform and reduce alternatives\",\"inLanguage\":\"en-US\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/#website\"},\"breadcrumb\":{\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/2018\\\/05\\\/22\\\/transform-and-reduce-alternatives\\\/#breadcrumblist\"},\"author\":{\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/author\\\/admin\\\/#author\"},\"creator\":{\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/author\\\/admin\\\/#author\"},\"datePublished\":\"2018-05-22T20:00:32+02:00\",\"dateModified\":\"2018-05-24T11:17:09+02:00\"},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/#website\",\"url\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/\",\"name\":\"Marius Bancila's Blog\",\"description\":\"About code. Mostly on C++\",\"inLanguage\":\"en-US\",\"publisher\":{\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/#organization\"}}]}\n\t\t<\/script>\n\t\t<!-- All in One SEO -->\n\n","aioseo_head_json":{"title":"Transform and reduce alternatives","description":"","canonical_url":"https:\/\/mariusbancila.ro\/blog\/2018\/05\/22\/transform-and-reduce-alternatives\/","robots":"max-image-preview:large","keywords":"algorithms,c++,c++17,perf,reduce,transform","webmasterTools":{"miscellaneous":""},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/mariusbancila.ro\/blog\/2018\/05\/22\/transform-and-reduce-alternatives\/#article","name":"Transform and reduce alternatives","headline":"Transform and reduce alternatives","author":{"@id":"https:\/\/mariusbancila.ro\/blog\/author\/admin\/#author"},"publisher":{"@id":"https:\/\/mariusbancila.ro\/blog\/#organization"},"datePublished":"2018-05-22T20:00:32+02:00","dateModified":"2018-05-24T11:17:09+02:00","inLanguage":"en-US","commentCount":2,"mainEntityOfPage":{"@id":"https:\/\/mariusbancila.ro\/blog\/2018\/05\/22\/transform-and-reduce-alternatives\/#webpage"},"isPartOf":{"@id":"https:\/\/mariusbancila.ro\/blog\/2018\/05\/22\/transform-and-reduce-alternatives\/#webpage"},"articleSection":"C++, algorithms, C++, C++17, perf, reduce, transform"},{"@type":"BreadcrumbList","@id":"https:\/\/mariusbancila.ro\/blog\/2018\/05\/22\/transform-and-reduce-alternatives\/#breadcrumblist","itemListElement":[{"@type":"ListItem","@id":"https:\/\/mariusbancila.ro\/blog#listItem","position":1,"name":"Home","item":"https:\/\/mariusbancila.ro\/blog","nextItem":{"@type":"ListItem","@id":"https:\/\/mariusbancila.ro\/blog\/category\/it\/#listItem","name":"IT"}},{"@type":"ListItem","@id":"https:\/\/mariusbancila.ro\/blog\/category\/it\/#listItem","position":2,"name":"IT","item":"https:\/\/mariusbancila.ro\/blog\/category\/it\/","nextItem":{"@type":"ListItem","@id":"https:\/\/mariusbancila.ro\/blog\/category\/it\/software\/#listItem","name":"Software"},"previousItem":{"@type":"ListItem","@id":"https:\/\/mariusbancila.ro\/blog#listItem","name":"Home"}},{"@type":"ListItem","@id":"https:\/\/mariusbancila.ro\/blog\/category\/it\/software\/#listItem","position":3,"name":"Software","item":"https:\/\/mariusbancila.ro\/blog\/category\/it\/software\/","nextItem":{"@type":"ListItem","@id":"https:\/\/mariusbancila.ro\/blog\/category\/it\/software\/c\/#listItem","name":"C++"},"previousItem":{"@type":"ListItem","@id":"https:\/\/mariusbancila.ro\/blog\/category\/it\/#listItem","name":"IT"}},{"@type":"ListItem","@id":"https:\/\/mariusbancila.ro\/blog\/category\/it\/software\/c\/#listItem","position":4,"name":"C++","item":"https:\/\/mariusbancila.ro\/blog\/category\/it\/software\/c\/","nextItem":{"@type":"ListItem","@id":"https:\/\/mariusbancila.ro\/blog\/2018\/05\/22\/transform-and-reduce-alternatives\/#listItem","name":"Transform and reduce alternatives"},"previousItem":{"@type":"ListItem","@id":"https:\/\/mariusbancila.ro\/blog\/category\/it\/software\/#listItem","name":"Software"}},{"@type":"ListItem","@id":"https:\/\/mariusbancila.ro\/blog\/2018\/05\/22\/transform-and-reduce-alternatives\/#listItem","position":5,"name":"Transform and reduce alternatives","previousItem":{"@type":"ListItem","@id":"https:\/\/mariusbancila.ro\/blog\/category\/it\/software\/c\/#listItem","name":"C++"}}]},{"@type":"Organization","@id":"https:\/\/mariusbancila.ro\/blog\/#organization","name":"Marius Bancila's Blog","description":"About code. Mostly on C++","url":"https:\/\/mariusbancila.ro\/blog\/"},{"@type":"Person","@id":"https:\/\/mariusbancila.ro\/blog\/author\/admin\/#author","url":"https:\/\/mariusbancila.ro\/blog\/author\/admin\/","name":"Marius Bancila","image":{"@type":"ImageObject","@id":"https:\/\/mariusbancila.ro\/blog\/2018\/05\/22\/transform-and-reduce-alternatives\/#authorImage","url":"https:\/\/secure.gravatar.com\/avatar\/a84dd2831d955c38355ddea55df4df260809b88f36408bc14fd4eab8f7f131c9?s=96&d=mm&r=g","width":96,"height":96,"caption":"Marius Bancila"}},{"@type":"WebPage","@id":"https:\/\/mariusbancila.ro\/blog\/2018\/05\/22\/transform-and-reduce-alternatives\/#webpage","url":"https:\/\/mariusbancila.ro\/blog\/2018\/05\/22\/transform-and-reduce-alternatives\/","name":"Transform and reduce alternatives","inLanguage":"en-US","isPartOf":{"@id":"https:\/\/mariusbancila.ro\/blog\/#website"},"breadcrumb":{"@id":"https:\/\/mariusbancila.ro\/blog\/2018\/05\/22\/transform-and-reduce-alternatives\/#breadcrumblist"},"author":{"@id":"https:\/\/mariusbancila.ro\/blog\/author\/admin\/#author"},"creator":{"@id":"https:\/\/mariusbancila.ro\/blog\/author\/admin\/#author"},"datePublished":"2018-05-22T20:00:32+02:00","dateModified":"2018-05-24T11:17:09+02:00"},{"@type":"WebSite","@id":"https:\/\/mariusbancila.ro\/blog\/#website","url":"https:\/\/mariusbancila.ro\/blog\/","name":"Marius Bancila's Blog","description":"About code. Mostly on C++","inLanguage":"en-US","publisher":{"@id":"https:\/\/mariusbancila.ro\/blog\/#organization"}}]},"og:locale":"en_US","og:site_name":"Marius Bancila's Blog | About code. Mostly on C++","og:type":"article","og:title":"Transform and reduce alternatives","og:url":"https:\/\/mariusbancila.ro\/blog\/2018\/05\/22\/transform-and-reduce-alternatives\/","article:published_time":"2018-05-22T18:00:32+00:00","article:modified_time":"2018-05-24T09:17:09+00:00","twitter:card":"summary","twitter:title":"Transform and reduce alternatives"},"aioseo_meta_data":{"post_id":"2951","title":null,"description":null,"keywords":null,"keyphrases":null,"primary_term":null,"canonical_url":null,"og_title":null,"og_description":null,"og_object_type":"default","og_image_type":"default","og_image_url":null,"og_image_width":null,"og_image_height":null,"og_image_custom_url":null,"og_image_custom_fields":null,"og_video":null,"og_custom_url":null,"og_article_section":null,"og_article_tags":null,"twitter_use_og":false,"twitter_card":"default","twitter_image_type":"default","twitter_image_url":null,"twitter_image_custom_url":null,"twitter_image_custom_fields":null,"twitter_title":null,"twitter_description":null,"schema":{"blockGraphs":[],"customGraphs":[],"default":{"data":{"Article":[],"Course":[],"Dataset":[],"FAQPage":[],"Movie":[],"Person":[],"Product":[],"ProductReview":[],"Car":[],"Recipe":[],"Service":[],"SoftwareApplication":[],"WebPage":[]},"graphName":"","isEnabled":true},"graphs":[]},"schema_type":null,"schema_type_options":null,"pillar_content":false,"robots_default":true,"robots_noindex":false,"robots_noarchive":false,"robots_nosnippet":false,"robots_nofollow":false,"robots_noimageindex":false,"robots_noodp":false,"robots_notranslate":false,"robots_max_snippet":null,"robots_max_videopreview":null,"robots_max_imagepreview":"large","priority":null,"frequency":null,"location":null,"local_seo":null,"breadcrumb_settings":null,"limit_modified_date":false,"ai":null,"created":"2021-03-18 21:04:51","updated":"2025-12-12 07:49:13","seo_analyzer_scan_date":null,"focus_keyword":null,"additional_keywords":null,"truseo_locale":null},"author_meta":{"display_name":"Marius Bancila","author_link":"https:\/\/mariusbancila.ro\/blog\/author\/admin\/"},"featured_img":null,"jetpack_publicize_connections":[],"uagb_featured_image_src":{"full":false,"thumbnail":false,"medium":false,"medium_large":false,"large":false,"1536x1536":false,"2048x2048":false},"uagb_author_info":{"display_name":"Marius Bancila","author_link":"https:\/\/mariusbancila.ro\/blog\/author\/admin\/"},"uagb_comment_info":2,"uagb_excerpt":"Transform-reduce is a pattern in which a set of data is first modified by applying a transformation on each of the elements and then it is reduced to a single value. In C++, this can be implemented straightforwardly with std::transform and std::accumulate. In C++17, an alternative for std::accumulate is available; std::reduce sums a range of&hellip;","coauthors":[],"tax_additional":{"categories":{"linked":["<a href=\"https:\/\/mariusbancila.ro\/blog\/category\/it\/software\/c\/\" class=\"advgb-post-tax-term\">C++<\/a>"],"unlinked":["<span class=\"advgb-post-tax-term\">C++<\/span>"]},"tags":{"linked":["<a href=\"https:\/\/mariusbancila.ro\/blog\/category\/it\/software\/c\/\" class=\"advgb-post-tax-term\">algorithms<\/a>","<a href=\"https:\/\/mariusbancila.ro\/blog\/category\/it\/software\/c\/\" class=\"advgb-post-tax-term\">C++<\/a>","<a href=\"https:\/\/mariusbancila.ro\/blog\/category\/it\/software\/c\/\" class=\"advgb-post-tax-term\">C++17<\/a>","<a href=\"https:\/\/mariusbancila.ro\/blog\/category\/it\/software\/c\/\" class=\"advgb-post-tax-term\">perf<\/a>","<a href=\"https:\/\/mariusbancila.ro\/blog\/category\/it\/software\/c\/\" class=\"advgb-post-tax-term\">reduce<\/a>","<a href=\"https:\/\/mariusbancila.ro\/blog\/category\/it\/software\/c\/\" class=\"advgb-post-tax-term\">transform<\/a>"],"unlinked":["<span class=\"advgb-post-tax-term\">algorithms<\/span>","<span class=\"advgb-post-tax-term\">C++<\/span>","<span class=\"advgb-post-tax-term\">C++17<\/span>","<span class=\"advgb-post-tax-term\">perf<\/span>","<span class=\"advgb-post-tax-term\">reduce<\/span>","<span class=\"advgb-post-tax-term\">transform<\/span>"]}},"comment_count":"2","relative_dates":{"created":"Posted 8 years ago","modified":"Updated 8 years ago"},"absolute_dates":{"created":"Posted on May 22, 2018","modified":"Updated on May 24, 2018"},"absolute_dates_time":{"created":"Posted on May 22, 2018 8:00 pm","modified":"Updated on May 24, 2018 11:17 am"},"featured_img_caption":"","series_order":"","jetpack_shortlink":"https:\/\/wp.me\/pYNdv-LB","jetpack_sharing_enabled":true,"jetpack_likes_enabled":true,"jetpack_featured_media_url":"","_links":{"self":[{"href":"https:\/\/mariusbancila.ro\/blog\/wp-json\/wp\/v2\/posts\/2951","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/mariusbancila.ro\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/mariusbancila.ro\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/mariusbancila.ro\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/mariusbancila.ro\/blog\/wp-json\/wp\/v2\/comments?post=2951"}],"version-history":[{"count":13,"href":"https:\/\/mariusbancila.ro\/blog\/wp-json\/wp\/v2\/posts\/2951\/revisions"}],"predecessor-version":[{"id":2966,"href":"https:\/\/mariusbancila.ro\/blog\/wp-json\/wp\/v2\/posts\/2951\/revisions\/2966"}],"wp:attachment":[{"href":"https:\/\/mariusbancila.ro\/blog\/wp-json\/wp\/v2\/media?parent=2951"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/mariusbancila.ro\/blog\/wp-json\/wp\/v2\/categories?post=2951"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/mariusbancila.ro\/blog\/wp-json\/wp\/v2\/tags?post=2951"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}