From 6237af4a63337661d67f12d2dc37efff9e37043f Mon Sep 17 00:00:00 2001 From: Pradnya Gaitonde Date: Sun, 21 Jul 2024 22:48:02 +0530 Subject: [PATCH] Added Parallel Algorithms in C++ Added Parallel Algorithms in C++ for day 25. --- docs/day-25/Parallel_algorithms.md | 206 +++++++++++++++++++++++++++++ docs/day-25/_category_.json | 7 + 2 files changed, 213 insertions(+) create mode 100644 docs/day-25/Parallel_algorithms.md create mode 100644 docs/day-25/_category_.json diff --git a/docs/day-25/Parallel_algorithms.md b/docs/day-25/Parallel_algorithms.md new file mode 100644 index 00000000..de15dcfe --- /dev/null +++ b/docs/day-25/Parallel_algorithms.md @@ -0,0 +1,206 @@ +--- +sidebar_position: 1 +title: "Parallel Algorithms in C++" +description: "In this tutorial we will learn about Parallel Algorithms in C++." +sidebar_label: "Parallel Algorithms in C++" +slug: Parallel-Algorithms-in-cpp +--- + +# Introduction to Parallel Algorithms in C++ + +Parallel algorithms are essential for utilizing the power of multi-core processors to speed up computations. C++17 introduced parallel algorithms in the standard library, allowing developers to execute many algorithms in parallel. + +## Execution Policies + +C++17 added three execution policies that can be used with standard algorithms: + +* 'std::execution::seq': Sequential execution (default). +* 'std::execution::par': Parallel execution. +* 'std::execution::par_unseq': Parallel and unsequenced execution. + +## Parallel Algorithms in Action + +1. **Parallel 'for_each'** +The for_each algorithm applies a function to each element in a range. Using the parallel execution policy, we can run this operation in parallel. + +Example: + +```C++ +#include +#include +#include +#include + +int main() { + std::vector vec(100000, 1); + + // Parallel for_each + std::for_each(std::execution::par, vec.begin(), vec.end(), [](int& n) { n += 1; }); + + std::cout << "First element: " << vec[0] << std::endl; + return 0; +} +``` + +2. **Parallel sort** +The sort algorithm sorts elements in a range. With the parallel execution policy, sorting can be done concurrently. + +Example: + +```C++ +#include +#include +#include +#include + +int main() { + std::vector vec = {5, 2, 9, 1, 5, 6}; + + // Parallel sort + std::sort(std::execution::par, vec.begin(), vec.end()); + + for (int n : vec) std::cout << n << " "; + std::cout << std::endl; + return 0; +} +``` + +3. **Parallel transform** +The transform algorithm applies a function to each element in a range and stores the result in another range. Using the parallel execution policy, the transformation can be done in parallel. + +Example: + +```C++ +#include +#include +#include +#include + +int main() { + std::vector vec = {1, 2, 3, 4, 5}; + std::vector result(vec.size()); + + // Parallel transform + std::transform(std::execution::par, vec.begin(), vec.end(), result.begin(), [](int n) { return n * n; }); + + for (int n : result) std::cout << n << " "; + std::cout << std::endl; + return 0; +} +``` + +## Thread Pools +A thread pool is a collection of pre-instantiated reusable threads that can execute tasks. It helps manage a pool of worker threads to improve performance and resource utilization. + +Example of a simple thread pool: + +```C++ +#include +#include +#include +#include +#include +#include +#include + +class ThreadPool { +public: + ThreadPool(size_t threads); + ~ThreadPool(); + void enqueue(std::function task); + +private: + std::vector workers; + std::queue> tasks; + std::mutex queue_mutex; + std::condition_variable condition; + bool stop; +}; + +ThreadPool::ThreadPool(size_t threads) : stop(false) { + for (size_t i = 0; i < threads; ++i) + workers.emplace_back([this] { + for (;;) { + std::function task; + { + std::unique_lock lock(this->queue_mutex); + this->condition.wait(lock, [this] { return this->stop || !this->tasks.empty(); }); + if (this->stop && this->tasks.empty()) return; + task = std::move(this->tasks.front()); + this->tasks.pop(); + } + task(); + } + }); +} + +ThreadPool::~ThreadPool() { + { + std::unique_lock lock(queue_mutex); + stop = true; + } + condition.notify_all(); + for (std::thread &worker : workers) worker.join(); +} + +void ThreadPool::enqueue(std::function task) { + { + std::unique_lock lock(queue_mutex); + tasks.emplace(std::move(task)); + } + condition.notify_one(); +} + +int main() { + ThreadPool pool(4); + for (int i = 0; i < 8; ++i) { + pool.enqueue([i] { std::cout << "Task " << i << " is being processed by thread " << std::this_thread::get_id() << std::endl; }); + } + return 0; +} +``` +## Advanced Topics and Best Practices + +**Synchronization Primitives** +Using synchronization primitives like mutexes, locks, and condition variables is essential to avoid data races and ensure thread safety. + +Example: + +```C++ +#include +#include +#include +#include + +std::mutex mtx; + +void print_safe(int n) { + std::lock_guard lock(mtx); + std::cout << "Thread " << std::this_thread::get_id() << ": " << n << std::endl; +} + +int main() { + std::vector threads; + for (int i = 0; i < 10; ++i) { + threads.emplace_back(print_safe, i); + } + + for (auto& t : threads) t.join(); + + return 0; +} +``` + +## Avoiding Data Races +Data races occur when two or more threads access shared data simultaneously without proper synchronization. Use synchronization mechanisms to protect shared resources. + +## Load Balancing +Distribute work evenly across threads to avoid some threads being idle while others are overloaded. Proper load balancing can significantly improve performance. + +## Best Practices +* Minimize Lock Contention: Reduce the amount of time threads spend waiting for locks. +* Avoid False Sharing: Ensure that threads do not inadvertently share cache lines, leading to performance degradation. +* Use Appropriate Granularity: Balance the overhead of parallelization with the computational cost of tasks. + +## Conclusion +Parallel algorithms in C++ offer a powerful way to leverage multi-core processors and improve the performance of your programs. By understanding and using parallel execution policies, thread pools, and synchronization primitives, you can write efficient and scalable concurrent applications. \ No newline at end of file diff --git a/docs/day-25/_category_.json b/docs/day-25/_category_.json new file mode 100644 index 00000000..8fdddfab --- /dev/null +++ b/docs/day-25/_category_.json @@ -0,0 +1,7 @@ +{ + "label": "Day 25", + "position": 25, + "link": { + "type": "generated-index" + } +} \ No newline at end of file