std::reduce

 Leveraging std::reduce for Custom Operations in C++

Introduction: In modern C++ (C++17 and later), the Standard Template Library (STL) introduced parallel algorithms, allowing developers to harness multi-core processors easily. One such powerful function is std::reduce, which performs reduction operations on a range of elements. This blog will guide you through using std::reduce for custom operations like finding the maximum element and multiplying values in an array.

What is std::reduce? std::reduce is a function that combines elements in a range using a binary operation. It supports parallel execution, making it suitable for performance-critical applications.

Syntax:

cpp

template<class ExecutionPolicy, class ForwardIt, class T, class BinaryOperation>

T reduce(ExecutionPolicy&& policy, ForwardIt first, ForwardIt last, T init, BinaryOperation binary_op);


  • ExecutionPolicy: Determines the execution strategy (e.g., std::execution::par for parallel).

  • first, last: The range of elements to reduce.

  • init: The initial value for the reduction.

  • binary_op: The binary operation to apply.

Example 1: Finding the Maximum Element

To find the maximum element, we can use a lambda function with std::reduce.

cpp

#include <iostream>

#include <vector>

#include <algorithm>

#include <execution>


int main() {

    std::vector<int> array = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

    

    // Using std::reduce to find the maximum element

    int maxElement = std::reduce(std::execution::par, array.begin(), array.end(), array[0], [](int a, int b) {

        return std::max(a, b);

    });

    

    std::cout << "Maximum element using std::reduce: " << maxElement << std::endl;

    

    return 0;

}


Explanation:

  • The lambda function [](int a, int b) { return std::max(a, b); } compares two elements and returns the larger one.

  • std::reduce iterates over the array, applying this lambda function to find the maximum element.

Example 2: Multiplying Elements

To multiply the elements of an array, we can use a different lambda function.

cpp

#include <iostream>

#include <vector>

#include <execution>


int main() {

    std::vector<int> array = {1, 2, 3, 4, 5};

    

    // Using std::reduce to multiply elements

    int product = std::reduce(std::execution::par, array.begin(), array.end(), 1, [](int a, int b) {

        return a * b;

    });

    

    std::cout << "Product of array elements using std::reduce: " << product << std::endl;

    

    return 0;

}


Explanation:

  • The lambda function [](int a, int b) { return a * b; } multiplies two elements.

  • std::reduce applies this operation to accumulate the product of all elements.

Using Standard Library Functions

Instead of custom lambda functions, you can use standard library function objects like std::max and std::multiplies.

Example with std::multiplies:

cpp

#include <iostream>

#include <vector>

#include <execution>

#include <functional> // For std::multiplies


int main() {

    std::vector<int> array = {1, 2, 3, 4, 5};


    // Using std::reduce to multiply elements with std::multiplies

    int product = std::reduce(std::execution::par, array.begin(), array.end(), 1, std::multiplies<>());


    std::cout << "Product of array elements using std::reduce with std::multiplies: " << product << std::endl;


    return 0;

}


Benefits of std::reduce:

  • Simplified Code: Provides a concise way to perform reduction operations.

  • Parallel Execution: Leverages multi-core processors for improved performance.

  • Flexibility: Allows customization of the binary operation.

Example: Comparing Sum Calculation Methods in C++

In this example, we will compare the performance of two methods for calculating the sum of elements in an array. We'll use a simple loop and the std::reduce function with parallel execution policy to see which method is more efficient. Additionally, we'll measure the time taken by each method.

#include <iostream>
#include <vector>
#include <numeric>
#include <algorithm>
#include <execution>  // for parallel execution policy
#include <chrono>  // for time calculation

using namespace std;
//How do you find the sum of elements in an array?
int sum_of_array(int arr[], int n) {
    int sum = 0;
    for (int i = 0; i < n; i++) {
        sum += arr[i];
    }
    cout << "Sum of elements in the array is: " << sum << endl;
    return sum;
}

// using reduce function
int sum_reduce(vector<int> vec, int init) {
        cout << "sume using reduce function" << endl;

    return reduce(execution::par, vec.begin(), vec.end(), 0);
}
int main() {
    int arr[] = {1, 2, 3, 4, 5};
    vector<int> vec {1, 2, 3, 4, 5};
    int sum = 0;
    int n = sizeof(arr) / sizeof(arr[0]);
   
    sum = sum_of_array(arr, 5);

    // using reduce function
    int result = sum_reduce(vec, 0);
    cout << "Sum of elements in the array is: " << result << endl;

    // need to write code to calculate time and memory usage for both the methods
    // Calculate time and memory usage for sum_of_array
    auto start = chrono::high_resolution_clock::now();
    sum = sum_of_array(arr, n);
    auto end = chrono::high_resolution_clock::now();
    auto duration = chrono::duration_cast<chrono::microseconds>(end - start).count();
    cout << "Time taken by sum_of_array: " << duration << " microseconds" << endl;

    // Calculate time and memory usage for sum_reduce
    start = chrono::high_resolution_clock::now();
    result = sum_reduce(vec, 0);
    end = chrono::high_resolution_clock::now();
    duration = chrono::duration_cast<chrono::microseconds>(end - start).count();
    cout << "Time taken by sum_reduce: " << duration << " microseconds" << endl;
}

Output

Sum of elements in the array is: 15
sume using reduce function
Sum of elements in the array is: 15
Sum of elements in the array is: 15
Time taken by sum_of_array: 480 microseconds
sume using reduce function
Time taken by sum_reduce: 351 microseconds

Key Points

  1. Performance Comparison:

    • Measure the time taken by each method to compare their performance.

    • The std::reduce function with parallel execution can potentially be faster for large arrays due to multi-core processing.

  2. Output:

    • The outputs should match, confirming both methods are correctly summing the array elements.

    • The time taken will provide insight into the efficiency of each method.

Conclusion

std::reduce is a versatile function that simplifies reduction operations and enhances performance through parallel execution. By customizing the binary operation, you can easily perform various tasks like finding the maximum, multiplying elements, and more. This blog hopefully sheds light on the power of std::reduce and how it can be applied to your C++ projects.


Meet me on linkedIn

Comments

Popular posts from this blog

Object slicing