From 45b99334e676ceada2f0e281a7bfe0282e1a3e1f Mon Sep 17 00:00:00 2001 From: Hristiyan Shterev Date: Sun, 19 Apr 2026 13:38:20 +0300 Subject: [PATCH 1/4] Improving exsiting omp notebooks --- openmp/assets/fork_join.png | Bin 0 -> 159036 bytes openmp/hello_world.ipynb | 20 ++++- openmp/linked_list.ipynb | 84 ++++++++++++++++--- openmp/mandel.ipynb | 127 +++++++++++++++++++++-------- openmp/openmp-demo.ipynb | 157 ++++++++++++++++++++++++++++-------- openmp/pi_integral.ipynb | 101 +++++++++++++---------- 6 files changed, 369 insertions(+), 120 deletions(-) create mode 100644 openmp/assets/fork_join.png diff --git a/openmp/assets/fork_join.png b/openmp/assets/fork_join.png new file mode 100644 index 0000000000000000000000000000000000000000..b67ec9abebf02905e83742935b89e1893273fa53 GIT binary patch literal 159036 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

ImYQtbOmNvYf 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

$NJ5>mGprM@mE|xX;e8&E4iYJY`oAxSd*q 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%0A$ae1aE^LZ8*;wX07O3(* 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

pa{)#q|gxg8hmI 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 XqzrsALzlxp00000NkvXXu0mjfe=?|M literal 0 HcmV?d00001 diff --git a/openmp/hello_world.ipynb b/openmp/hello_world.ipynb index 7a62ea8..2b2a4af 100644 --- a/openmp/hello_world.ipynb +++ b/openmp/hello_world.ipynb @@ -1,5 +1,19 @@ { "cells": [ + { + "cell_type": "markdown", + "id": "aa52f895-3a3f-4c39-adaa-27a52c704fdb", + "metadata": {}, + "source": [ + "# OpenMP Hello World\n", + "\n", + "This is a simple OpenMP \"Hello World\" example that demonstrates the basics of parallel programming. We get the maximum number of threads available on the machine and set OpenMP to use all of them - in this case 8. Then we open a parallel region with `#pragma omp parallel`, which spawns all 8 threads simultaneously. Each thread independently reads its own ID via `omp_get_thread_num()` and prints a Hello World message along with the total thread count.\n", + "\n", + "One important thing to notice in the output is that the threads do not print in order, but all print in a seemingly random sequence. This is completely normal and expected. The OS and CPU scheduler decide which thread gets CPU time at any given moment, and since all threads are racing to reach the `printf` at the same time, whichever thread gets scheduled first prints first. This is called a race condition on output and is a fundamental characteristic of parallel execution. The order will likely be different every time you run the program.\n", + "\n", + "It is important to understand that while the print order is unpredictable, the computation itself is correct - each thread does its own independent work without interfering with the others. This distinction between chaotic ordering and correct parallel computation is one of the core concepts in parallel programming and OpenMP." + ] + }, { "cell_type": "code", "execution_count": 1, @@ -64,7 +78,7 @@ ], "metadata": { "kernelspec": { - "display_name": "C++23 (xcpp+OpenMP)", + "display_name": "C++23 + OpenMP", "language": "cpp", "name": "xcpp23-omp" }, @@ -73,7 +87,9 @@ "file_extension": ".cpp", "mimetype": "text/x-c++src", "name": "C++", - "version": "23" + "nbconvert_exporter": "", + "pygments_lexer": "", + "version": "cxx23" } }, "nbformat": 4, diff --git a/openmp/linked_list.ipynb b/openmp/linked_list.ipynb index 005380b..fb4a337 100644 --- a/openmp/linked_list.ipynb +++ b/openmp/linked_list.ipynb @@ -1,5 +1,19 @@ { "cells": [ + { + "cell_type": "markdown", + "id": "4de82d38-88d2-4d56-8ed1-3a33cdd4dac1", + "metadata": {}, + "source": [ + "# Linked List\n", + "\n", + "The program builds a linked list of 6 nodes, each storing a Fibonacci input (38 through 43), then uses parallel programming to compute the Fibonacci value for every node in parallel. A single thread walks the list and spawns one task per node, each task calling the slow recursive `fib()` on its own copy of the pointer `firstprivate(p)`. Once all tasks finish, the results are printed and memory is freed.\n", + "\n", + "### What is a linked list?\n", + "\n", + "A linked list is a chain of nodes where each node holds some data and a pointer to the next node. In this code, each node stores a Fibonacci input (data), its result (fibdata), and a next pointer to the following node. The last node's next is NULL, signaling the end of the chain. Instead of storing elements in contiguous memory like an array, each node lives at a random location in the heap (via malloc) and they are connected only through those next pointers - so to reach node 4 you must walk through nodes 1, 2, and 3 in order." + ] + }, { "cell_type": "code", "execution_count": 1, @@ -29,7 +43,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 3, "id": "22f97c49-78d1-496e-ac7c-978aed95331a", "metadata": {}, "outputs": [], @@ -41,9 +55,17 @@ "};" ] }, + { + "cell_type": "markdown", + "id": "afc7ee35-8fd1-45de-be44-f987d98b7808", + "metadata": {}, + "source": [ + "Here we can see the 3 main functions used for this program. " + ] + }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 4, "id": "b16b1e8a-8831-4b8d-9d57-09deeaaa88ee", "metadata": {}, "outputs": [], @@ -53,9 +75,19 @@ "int fib(int n);" ] }, + { + "cell_type": "markdown", + "id": "10231105-2ab9-44e9-9878-24d128a473c6", + "metadata": {}, + "source": [ + "### Fibonachi function\n", + "\n", + "`int fib(int n)`: recursively computes the N-th Fibonacci number by breaking it down into fib(n-1) + fib(n-2) until it hits the base cases 0 or 1, then returns the final integer result." + ] + }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 5, "id": "0ef8af6c-1d6f-4c68-84bc-3dd1d8092b06", "metadata": {}, "outputs": [], @@ -72,9 +104,19 @@ "}" ] }, + { + "cell_type": "markdown", + "id": "a13acd85-15f5-4ac7-9312-5288cd8ad598", + "metadata": {}, + "source": [ + "### Process work\n", + "\n", + "`void processwork(struct node *p)`: takes a single node, computes the Fibonacci number of its data field by calling fib(), and stores the result in its fibdata field. Returns nothing." + ] + }, { "cell_type": "code", - "execution_count": 7, + "execution_count": null, "id": "1fa0307d-fdc9-4503-95cb-1c6448791354", "metadata": {}, "outputs": [], @@ -88,9 +130,19 @@ "}" ] }, + { + "cell_type": "markdown", + "id": "a35c79a7-a72e-4fa8-81ba-6bdc0f18e0a9", + "metadata": {}, + "source": [ + "### initialize list\n", + "\n", + "`struct node *init_list(struct node *p)`: builds the linked list in memory using malloc, creates N+1 nodes with Fibonacci inputs starting from FS, links them together, and returns a pointer to the head (first node)." + ] + }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 7, "id": "03acb599-9329-49ff-8aff-c0902adb6c3c", "metadata": {}, "outputs": [], @@ -117,9 +169,19 @@ "}" ] }, + { + "cell_type": "markdown", + "id": "111c0742-1c27-40ad-a261-7ed85e379d10", + "metadata": {}, + "source": [ + "## The main code\n", + "\n", + "`main` prints some info messages, sets OpenMP to use the maximum available threads, builds the linked list via `init_list`, then starts a timer. Inside the parallel region, one thread walks the list and creates an independent OpenMP task for each node, while all 8 threads grab and execute those tasks concurrently — each computing its Fibonacci number via `processwork`. Once all tasks finish, the timer stops, the results are printed node by node, memory is freed, and the total compute time is displayed." + ] + }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 8, "id": "f2dfb41b-e55f-43c0-b7f6-546a1697acb1", "metadata": {}, "outputs": [], @@ -177,7 +239,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 9, "id": "353e5dfd-fcae-43e6-97e3-ec98070811a1", "metadata": {}, "outputs": [ @@ -195,7 +257,7 @@ "41 : 165580141\n", "42 : 267914296\n", "43 : 433494437\n", - "Compute Time: 2.617225 seconds\n" + "Compute Time: 4.668121 seconds\n" ] } ], @@ -206,7 +268,7 @@ ], "metadata": { "kernelspec": { - "display_name": "C++23 (xcpp+OpenMP)", + "display_name": "C++23 + OpenMP", "language": "cpp", "name": "xcpp23-omp" }, @@ -215,7 +277,9 @@ "file_extension": ".cpp", "mimetype": "text/x-c++src", "name": "C++", - "version": "23" + "nbconvert_exporter": "", + "pygments_lexer": "", + "version": "cxx23" } }, "nbformat": 4, diff --git a/openmp/mandel.ipynb b/openmp/mandel.ipynb index 08a6b64..29646d0 100644 --- a/openmp/mandel.ipynb +++ b/openmp/mandel.ipynb @@ -1,5 +1,29 @@ { "cells": [ + { + "cell_type": "markdown", + "id": "7c545848-70df-4ec4-bea8-93969fc96aa9", + "metadata": {}, + "source": [ + "# Mandelbrot set\n", + "\n", + "This notebook estimates the area of the Mandelbrot set using parallel processing with OpenMP.\n", + "\n", + "### How the Mandelbrot Set Works\n", + "\n", + "1. **Grid Mapping**\n", + " - The grid indices are mapped to complex numbers within a specific range.\n", + " - For each point `c`, we check if iterating the function $z_{n+1} = z_n² + c$ remains bounded.\n", + "\n", + "2. **Iteration Process**\n", + " - Starting with `z = 0`, compute subsequent values using the formula.\n", + " - If at any iteration, the magnitude of `z` exceeds a threshold (here, 2), the point is marked as escaping and not part of the set.\n", + "\n", + "3. **Counting Escaping Points**\n", + " - `numoutside` keeps track of how many points escape within the given iterations.\n", + " - The area estimate is based on the proportion of these points within the grid.\n" + ] + }, { "cell_type": "code", "execution_count": 1, @@ -14,57 +38,93 @@ ] }, { - "cell_type": "code", - "execution_count": 3, - "id": "8b66f96a-14ef-4f23-8024-bcfc42b31e4e", + "cell_type": "markdown", + "id": "f86e5fd6-f657-4848-8f80-780b5ca67678", "metadata": {}, - "outputs": [], "source": [ - "#define NPOINTS 1000\n", - "#define MAXITER 1000" + "1. **Constants Definition**:\n", + " - `NPOINTS = 5000`: The grid size, creating a 5000x5000 grid.\n", + " - `MAXITER = 1000`: Maximum iterations per point to determine if it escapes." ] }, { "cell_type": "code", - "execution_count": 4, - "id": "d89dd57c-fe19-4233-a33a-df9b24fae98a", + "execution_count": 2, + "id": "8b66f96a-14ef-4f23-8024-bcfc42b31e4e", "metadata": {}, "outputs": [], "source": [ + "#define NPOINTS 5000\n", + "#define MAXITER 1000\n", + "\n", "int numoutside = 0;" ] }, + { + "cell_type": "markdown", + "id": "dfd58cbd-798b-472c-869e-5a9de42a502e", + "metadata": {}, + "source": [ + "2. Function testpoint:\n", + " - Takes complex number parameters `creal` and `cimag`.\n", + " - Initializes `zreal` and `zimag` to 0.\n", + " - Iterates up to `MAXITER`, checking if `(zreal^2 + zimag^2) > 4.0`. If so, the point escapes, incrementing `numoutside`." + ] + }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 3, "id": "5c35c479-2f79-46b7-bc66-24be6b1694e0", "metadata": {}, "outputs": [], "source": [ "void testpoint(double creal, double cimag) {\n", - " // iterate z=z*z+c, until |z| > 2 when point is known to be outside set\n", - " // If loop count reaches MAXITER, point is considered to be inside the set\n", - "\n", - " double zreal, zimag, temp;\n", + " double zreal = 0.0; // Start at 0\n", + " double zimag = 0.0; \n", + " double r2, i2;\n", " int iter;\n", - " zreal = creal;\n", - " zimag = cimag;\n", "\n", " for (iter = 0; iter < MAXITER; iter++) {\n", - " temp = (zreal * zreal) - (zimag * zimag) + creal;\n", - " zimag = zreal * zimag * 2 + cimag;\n", - " zreal = temp;\n", - " if ((zreal * zreal + zimag * zimag) > 4.0) {\n", + " r2 = zreal * zreal;\n", + " i2 = zimag * zimag;\n", + " \n", + " if ((r2 + i2) > 4.0) {\n", + " #pragma omp atomic // Safety check if not using reduction correctly\n", " numoutside++;\n", - " break;\n", + " return;\n", " }\n", + " \n", + " zimag = 2.0 * zreal * zimag + cimag;\n", + " zreal = r2 - i2 + creal;\n", " }\n", "}" ] }, + { + "cell_type": "markdown", + "id": "53fb8c2a-5b6e-41bd-a6eb-d31ab6c94ad0", + "metadata": {}, + "source": [ + "3. **Main Function**\n", + " - Maps grid indices to complex numbers within a specific range using scaling factors:\n", + " ```\n", + " creal = (i * (-1.0 / ((double)(NPOINTS - 1)))) + (-1.0);\n", + " cimag = (j * (-1.0 / ((double)(NPOINTS - 1)))) + (-0.75);\n", + " ```\n", + " - Uses `#pragma omp parallel for` to distribute the computation across threads.\n", + " - `default(shared)` means variables not specified are shared among all threads.\n", + " - `private(i, j, creal, cimag)` ensures each thread has its own copy of these variables.\n", + " - `reduction(+:numoutside)` accumulates the total count of escaping points.\n", + "\n", + "\n", + "3. **Area Calculation**:\n", + " - Computes area as `(2.0 * 2.5 * 1.125) * ((NPOINTS^2 - numoutside) / NPOINTS^2)`\n", + " - Error estimate is derived by dividing area by `NPOINTS`." + ] + }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 4, "id": "ea116fef-7d05-4e29-97a1-55c85c7241d8", "metadata": {}, "outputs": [], @@ -76,14 +136,15 @@ " // Loop over grid of points in the complex plane which contains the Mandelbrot set,\n", " // testing each point to see whether it is inside or outside the set.\n", "\n", - "#pragma omp parallel for private(eps)\n", - " for (i = 0; i < NPOINTS; i++) {\n", - " for (j = 0; j < NPOINTS; j++) {\n", - " creal = -2.0 + 2.5 * (double) (i) / (double) (NPOINTS) + eps;\n", - " cimag = 1.125 * (double) (j) / (double) (NPOINTS) + eps;\n", - " testpoint(creal, cimag);\n", - " }\n", + "// i, j, creal, and cimag MUST be private so threads don't overwrite each other\n", + "#pragma omp parallel for default(shared) private(i, j, creal, cimag) reduction(+:numoutside)\n", + "for (i = 0; i < NPOINTS; i++) {\n", + " for (j = 0; j < NPOINTS; j++) {\n", + " creal = -2.0 + 2.5 * (double)(i) / (double)(NPOINTS) + eps;\n", + " cimag = 1.125 * (double)(j) / (double)(NPOINTS) + eps;\n", + " testpoint(creal, cimag);\n", " }\n", + "}\n", "\n", " // Calculate area of set and error estimate and output the results\n", " area = 2.0 * 2.5 * 1.125 * (double) (NPOINTS * NPOINTS - numoutside) / (double) (NPOINTS * NPOINTS);\n", @@ -96,7 +157,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 5, "id": "39cf129c-8106-4e67-a2f1-1a7fff17cd38", "metadata": {}, "outputs": [ @@ -104,7 +165,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "Area of Mandlebrot set = 3.80247750 +/- 0.00380248\n", + "Area of Mandlebrot set = 1.51064145 +/- 0.00030213\n", "Correct answer should be around 1.510659\n" ] } @@ -116,7 +177,7 @@ ], "metadata": { "kernelspec": { - "display_name": "C++23 (xcpp+OpenMP)", + "display_name": "C++23 + OpenMP", "language": "cpp", "name": "xcpp23-omp" }, @@ -125,7 +186,9 @@ "file_extension": ".cpp", "mimetype": "text/x-c++src", "name": "C++", - "version": "23" + "nbconvert_exporter": "", + "pygments_lexer": "", + "version": "cxx23" } }, "nbformat": 4, diff --git a/openmp/openmp-demo.ipynb b/openmp/openmp-demo.ipynb index 101ade0..67d996c 100644 --- a/openmp/openmp-demo.ipynb +++ b/openmp/openmp-demo.ipynb @@ -1,5 +1,40 @@ { "cells": [ + { + "cell_type": "markdown", + "id": "2bf44eba-b903-40f1-9ef6-67fb8e2d9cc8", + "metadata": {}, + "source": [ + "# Introduction to OpenMP\n", + "\n", + "OpenMP (Open Multi-Processing) allows you to use multiple threads of the CPU when programming. It is an industry-standard API for shared-memory parallel programming, designed to help C++ developers take full advantage of multi-core processors without needing to manage low-level thread creation.\n", + "\n", + "## How it works\n", + "\n", + "1. The Fork-Join Model\n", + "OpenMP operates on a simple execution pattern:\n", + "\n", + "- The Master Thread: The program begins as a single serial thread.\n", + "- The Fork: When a parallel directive is reached, the master thread creates a team of worker threads.\n", + "- Parallel Execution: The work is distributed across these threads.\n", + "- The Join: Once the work is finished, the threads synchronize and terminate, leaving only the master thread to continue.\n", + "\n", + "2. Practical Implementation\n", + "- The most common use case is parallelising a for loop. By adding a single \"pragma\" line, you can distribute millions of calculations across your CPU cores.\n", + "\n", + "Here is an image of how the Fork-Join model works\n", + "\n", + "![fork-join](assets/fork_join.png)" + ] + }, + { + "cell_type": "markdown", + "id": "0fb794c1-2999-43ae-9c9d-58060e4eb66f", + "metadata": {}, + "source": [ + "In this first example function we can see how to print Hello World normally. There is only standard c++ syntax." + ] + }, { "cell_type": "code", "execution_count": 1, @@ -13,7 +48,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 2, "id": "5001e441-1fa5-4bdc-9fa5-2ca103ae484f", "metadata": {}, "outputs": [ @@ -32,9 +67,21 @@ "example1();" ] }, + { + "cell_type": "markdown", + "id": "f37a7cfd-f158-4ef9-a4cf-977296ccf5d4", + "metadata": {}, + "source": [ + "Now, let's use OpenMP to run the same code in parallel. By adding a simple compiler directive, we tell the system to create a \"team\" of threads.\n", + "\n", + "```#pragma omp parallel``` This directive tells the compiler to execute the following block of code in parallel using multiple threads.\n", + "\n", + "The result is that instead of printing once, you will see \"Hello World!\" printed multiple times—once for every available logical core on your CPU." + ] + }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 3, "id": "53fb7656-b72e-42bc-ade7-2ae2077142da", "metadata": {}, "outputs": [ @@ -42,14 +89,14 @@ "name": "stdout", "output_type": "stream", "text": [ - "Hello World!Hello World!\n", - "Hello World!Hello World!\n", "Hello World!\n", - "\n", "Hello World!\n", - "\n", "Hello World!\n", - "Hello World!\n" + "Hello World!\n", + "Hello World!\n", + "Hello World!\n", + "Hello World!Hello World!\n", + "\n" ] } ], @@ -63,9 +110,23 @@ "example2();" ] }, + { + "cell_type": "markdown", + "id": "f1568cd4-d407-49c7-98e4-26f971fdb63d", + "metadata": {}, + "source": [ + "There is one thing we need to address before we continue\n", + "\n", + "When we use #pragma omp parallel, OpenMP spawns multiple threads that all execute the code inside the curly braces simultaneously. Every thread is trying to talk to our monitor at the exact same time.\n", + "\n", + "Thread A might finish sending \"Hello World!\" but, before it can send the newline, Thread B jumps in and prints its own \"Hello World!\". This results in two greetings on one line, followed by two newlines later.\n", + "\n", + "We fix this by using this by using ```#pragma omp critical```. It makes each thread wait for each other. This significantly slows down the program and loses the whole point of using multiple threads, but we are going to use it here in some examples to show you better how OpenMP works. Below we have a function that uses this fix." + ] + }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 4, "id": "efcdfdb6-a60b-46af-8194-75ef9cc0e27f", "metadata": {}, "outputs": [ @@ -73,14 +134,14 @@ "name": "stdout", "output_type": "stream", "text": [ - "Hello World! (Hello World! (Hello World! (Hello World! (34)\n", - "Hello World! (7)\n", - "0)\n", - "2Hello World! (6))\n", - "\n", - ")\n", + "Hello World! (0)\n", "Hello World! (5)\n", - "Hello World! (1)\n" + "Hello World! (6)\n", + "Hello World! (4)\n", + "Hello World! (1)\n", + "Hello World! (3)\n", + "Hello World! (7)\n", + "Hello World! (2)\n" ] } ], @@ -88,15 +149,26 @@ "void example3() {\n", " #pragma omp parallel\n", " {\n", - " std::cout << \"Hello World! (\" << omp_get_thread_num() << \")\" << std::endl;\n", + " #pragma omp critical\n", + " {\n", + " std::cout << \"Hello World! (\" << omp_get_thread_num() << \")\" << std::endl;\n", + " }\n", " }\n", "}\n", "example3();" ] }, + { + "cell_type": "markdown", + "id": "a6123ee3-92b9-42fe-b1a0-57f9764569aa", + "metadata": {}, + "source": [ + "Other things we can do in OMP, includes using a set number of threads that are going to be used: ```#pragma omp parallel num_threads(2)``` " + ] + }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 5, "id": "d86a9efa-ba28-4cb6-bbfc-abc00ee63506", "metadata": {}, "outputs": [ @@ -104,17 +176,17 @@ "name": "stdout", "output_type": "stream", "text": [ - "Hello World! (Hello World! (34))\n", "Hello World! (0)\n", - "Hello World! (2)\n", - "Hello World! (Hello World! (1)\n", - "\n", - "7)Hello World! (\n", - "6)\n", + "Hello World! (6)\n", "Hello World! (5)\n", + "Hello World! (3)\n", + "Hello World! (4)\n", + "Hello World! (1)\n", + "Hello World! (7)\n", + "Hello World! (2)\n", "This is another message! (0)\n", - "Goodbye World! (0)\n", - "Goodbye World! (1)\n" + "Goodbye World! (Goodbye World! (01)\n", + ")\n" ] } ], @@ -122,7 +194,10 @@ "void example4() {\n", " #pragma omp parallel\n", " {\n", - " std::cout << \"Hello World! (\" << omp_get_thread_num() << \")\" << std::endl;\n", + " #pragma omp critical\n", + " {\n", + " std::cout << \"Hello World! (\" << omp_get_thread_num() << \")\" << std::endl;\n", + " }\n", " }\n", "\n", " std::cout << \"This is another message! (\" << omp_get_thread_num() << \")\" << std::endl;\n", @@ -135,9 +210,21 @@ "example4();" ] }, + { + "cell_type": "markdown", + "id": "25421d19-67e5-4bc9-9420-d96439cffba5", + "metadata": {}, + "source": [ + "Now that you know basic syntax we can do a speed benchmark. We are going to time filling two arrays with 1 million elements each, adding them and calculating the average. In the average calculation there is a problem that can occur. In parallel programming, if multiple threads try to add to the same average variable at the same time, they will overwrite each other. When we use ```reduction(+:average)```, OpenMP does the following:\n", + "\n", + "- Each thread gets its own private mini-average variable initialized to 0.\n", + "- Each thread calculates the sum for its assigned chunk of the array (e.g., Thread 1 sums indices 0 to 1000, Thread 2 sums 1001 to 2000).\n", + "- Once all threads finish, OpenMP safely adds all those private mini-sums together into the final global average variable." + ] + }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 6, "id": "5557e01a-7c7d-4b54-8545-962ad11027df", "metadata": {}, "outputs": [ @@ -145,12 +232,12 @@ "name": "stdout", "output_type": "stream", "text": [ - "Initialize a[] time: 0.588681\n", - "Initialize b[] time: 0.513927\n", - "Add arrays time: 1.58928\n", - "Average result time: 0.637053\n", + "Initialize a[] time: 0.657814\n", + "Initialize b[] time: 0.856986\n", + "Add arrays time: 0.581521\n", + "Average result time: 0.449791\n", "Average: 5e+08\n", - "Total time: 3.33191\n" + "Total time: 2.54675\n" ] } ], @@ -203,7 +290,7 @@ ], "metadata": { "kernelspec": { - "display_name": "C++23 (xcpp+OpenMP)", + "display_name": "C++23 + OpenMP", "language": "cpp", "name": "xcpp23-omp" }, @@ -212,7 +299,9 @@ "file_extension": ".cpp", "mimetype": "text/x-c++src", "name": "C++", - "version": "23" + "nbconvert_exporter": "", + "pygments_lexer": "", + "version": "cxx23" } }, "nbformat": 4, diff --git a/openmp/pi_integral.ipynb b/openmp/pi_integral.ipynb index 7289ff8..1690439 100644 --- a/openmp/pi_integral.ipynb +++ b/openmp/pi_integral.ipynb @@ -1,5 +1,15 @@ { "cells": [ + { + "cell_type": "markdown", + "id": "b71e8dea-b45b-4560-b1fd-7569914195d7", + "metadata": {}, + "source": [ + "# Pi Calculation Benchmark\n", + "\n", + "Here we see how using multiple threads at the same time can save time. The code uses the Rectangle Rule to approximate the area under the curve of the function $f(x) = \\frac{4}{1+x^2}$ from $0$ to $1$. In calculus, the definite integral of this function is:$$\\int_{0}^{1} \\frac{4}{1+x^2} \\, dx = \\pi$$The code divides the area into num_steps small rectangles, calculates the height at the midpoint of each, and sums them up. Also instead of just calculating it once, we use a for loop to calculate it four different times, each time adding one more thread." + ] + }, { "cell_type": "code", "execution_count": 1, @@ -13,7 +23,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 2, "id": "9078ac79-ca50-4fef-b785-37f35fec3cab", "metadata": {}, "outputs": [], @@ -24,76 +34,81 @@ }, { "cell_type": "code", - "execution_count": 4, - "id": "f3c10995-6f29-4d71-9e61-1993ca9d1cc9", + "execution_count": 3, + "id": "3bee0992-3a6d-4da1-9020-3b6c153ecd0a", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Num threads available: 8\n" + ] + } + ], "source": [ - "int main() {\n", - " int i, j, num_threads_allocated;\n", - " double x, pi, sum = 0.0;\n", - " double start_time, run_time;\n", + "int i, j, num_threads_allocated;\n", + "double pi, sum = 0.0;\n", + "double start_time, run_time;\n", "\n", - " step = 1.0 / (double)num_steps;\n", - " printf(\"Num threads available: %d\\n\", omp_get_max_threads());\n", - " for (i = 1; i <= 4; i++) {\n", - " sum = 0.0;\n", - " omp_set_num_threads(i);\n", - " start_time = omp_get_wtime();\n", - "#pragma omp parallel\n", - " {\n", - " num_threads_allocated = omp_get_num_threads();\n", - "#pragma omp single\n", - " printf(\"Num threads allocated for this run: %d\\n\", num_threads_allocated);\n", - "\n", - "#pragma omp for reduction(+ : sum)\n", - " for (j = 1; j <= num_steps; j++) {\n", - " x = (j - 0.5) * step;\n", - " sum = sum + 4.0 / (1.0 + x * x);\n", - " }\n", - " }\n", - "\n", - " pi = step * sum;\n", - " run_time = omp_get_wtime() - start_time;\n", - " printf(\"pi is %f in %f seconds using %d threads\\n\\n\", pi, run_time, num_threads_allocated);\n", - " }\n", - "}" + "step = 1.0 / (double)num_steps;\n", + "printf(\"Num threads available: %d\\n\", omp_get_max_threads());" ] }, { "cell_type": "code", - "execution_count": 5, - "id": "0f84442a-d947-4860-bd3c-aeeea963b419", + "execution_count": 4, + "id": "9c3baec7-f369-4e28-bcdb-fd1fc7f985fc", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Num threads available: 8\n", "Num threads allocated for this run: 1\n", - "pi is 3.141593 in 0.179501 seconds using 1 threads\n", + "pi is 3.141593 in 1.106737 seconds using 1 threads\n", "\n", "Num threads allocated for this run: 2\n", - "pi is 3.141592 in 0.184605 seconds using 2 threads\n", + "pi is 3.141593 in 0.551873 seconds using 2 threads\n", "\n", "Num threads allocated for this run: 3\n", - "pi is 3.141593 in 0.097145 seconds using 3 threads\n", + "pi is 3.141593 in 0.368655 seconds using 3 threads\n", "\n", "Num threads allocated for this run: 4\n", - "pi is 3.141593 in 0.071473 seconds using 4 threads\n", + "pi is 3.141593 in 0.289997 seconds using 4 threads\n", "\n" ] } ], "source": [ - "main();" + "for (i = 1; i <= 4; i++) {\n", + " sum = 0.0;\n", + " omp_set_num_threads(i);\n", + " start_time = omp_get_wtime();\n", + " #pragma omp parallel\n", + " {\n", + " double x;\n", + " num_threads_allocated = omp_get_num_threads();\n", + " \n", + " #pragma omp single\n", + " printf(\"Num threads allocated for this run: %d\\n\", num_threads_allocated);\n", + " \n", + " #pragma omp for reduction(+ : sum)\n", + " for (j = 1; j <= num_steps; j++) {\n", + " x = (j - 0.5) * step;\n", + " sum = sum + 4.0 / (1.0 + x * x);\n", + " }\n", + " }\n", + " pi = step * sum;\n", + " run_time = omp_get_wtime() - start_time;\n", + " printf(\"pi is %f in %f seconds using %d threads\\n\\n\", pi, run_time, num_threads_allocated);\n", + "}" ] } ], "metadata": { "kernelspec": { - "display_name": "C++23 (xcpp+OpenMP)", + "display_name": "C++23 + OpenMP", "language": "cpp", "name": "xcpp23-omp" }, @@ -102,7 +117,9 @@ "file_extension": ".cpp", "mimetype": "text/x-c++src", "name": "C++", - "version": "23" + "nbconvert_exporter": "", + "pygments_lexer": "", + "version": "cxx23" } }, "nbformat": 4, From ba79e363e8a4f38616a0a6f411c88dbfb1605cd3 Mon Sep 17 00:00:00 2001 From: Hristiyan Shterev Date: Fri, 24 Apr 2026 15:24:13 +0300 Subject: [PATCH 2/4] Added new OMP notebook and fixed names --- ...openmp-demo.ipynb => 01_openmp-demo.ipynb} | 0 ...hello_world.ipynb => 02_hello_world.ipynb} | 0 ...pi_integral.ipynb => 03_pi_integral.ipynb} | 0 openmp/{mandel.ipynb => 04_mandel.ipynb} | 0 ...linked_list.ipynb => 05_linked_list.ipynb} | 0 openmp/06_false_sharing.ipynb | 321 +++++++++++ openmp/07_race_conditions.ipynb | 518 ++++++++++++++++++ 7 files changed, 839 insertions(+) rename openmp/{openmp-demo.ipynb => 01_openmp-demo.ipynb} (100%) rename openmp/{hello_world.ipynb => 02_hello_world.ipynb} (100%) rename openmp/{pi_integral.ipynb => 03_pi_integral.ipynb} (100%) rename openmp/{mandel.ipynb => 04_mandel.ipynb} (100%) rename openmp/{linked_list.ipynb => 05_linked_list.ipynb} (100%) create mode 100644 openmp/06_false_sharing.ipynb create mode 100644 openmp/07_race_conditions.ipynb diff --git a/openmp/openmp-demo.ipynb b/openmp/01_openmp-demo.ipynb similarity index 100% rename from openmp/openmp-demo.ipynb rename to openmp/01_openmp-demo.ipynb diff --git a/openmp/hello_world.ipynb b/openmp/02_hello_world.ipynb similarity index 100% rename from openmp/hello_world.ipynb rename to openmp/02_hello_world.ipynb diff --git a/openmp/pi_integral.ipynb b/openmp/03_pi_integral.ipynb similarity index 100% rename from openmp/pi_integral.ipynb rename to openmp/03_pi_integral.ipynb diff --git a/openmp/mandel.ipynb b/openmp/04_mandel.ipynb similarity index 100% rename from openmp/mandel.ipynb rename to openmp/04_mandel.ipynb diff --git a/openmp/linked_list.ipynb b/openmp/05_linked_list.ipynb similarity index 100% rename from openmp/linked_list.ipynb rename to openmp/05_linked_list.ipynb diff --git a/openmp/06_false_sharing.ipynb b/openmp/06_false_sharing.ipynb new file mode 100644 index 0000000..ff8aa42 --- /dev/null +++ b/openmp/06_false_sharing.ipynb @@ -0,0 +1,321 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# False Sharing in OpenMP\n", + "\n", + "You fixed the race condition\n", + "Your atomic code is correct\n", + "But it's still slow... why? → **False Sharing**\n", + "\n", + "---\n", + "\n", + "## What is a Cache Line?\n", + "\n", + "CPUs don't load individual bytes from RAM - they load chunks called **cache lines** (typically **64 bytes**).\n", + "\n", + "When Thread 0 writes to `hist[0]`, the CPU loads the **entire 64-byte line** into Thread 0's cache. \n", + "When Thread 1 writes to `hist[1]`, it **invalidates** that line on Thread 0's cache - even though they touched different variables!\n", + "\n", + "This is **false sharing** - the sharing is false because the threads aren't actually sharing data, but the CPU thinks they are." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Setup" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Threads: 4\n", + "Iterations: 100000000\n", + "Cache line: 64 bytes\n", + "sizeof(int) = 4 bytes\n", + "4 ints fit in 16 bytes (one cache line = 64 bytes)\n" + ] + } + ], + "source": [ + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "\n", + "#define THREADS 4\n", + "#define ITERATIONS 100000000 // 100 million\n", + "#define CACHE_LINE 64 // bytes per cache line\n", + "\n", + "printf(\"Threads: %d\\n\", THREADS);\n", + "printf(\"Iterations: %d\\n\", ITERATIONS);\n", + "printf(\"Cache line: %d bytes\\n\", CACHE_LINE);\n", + "printf(\"sizeof(int) = %zu bytes\\n\", sizeof(int));\n", + "printf(\"4 ints fit in %zu bytes (one cache line = %d bytes)\\n\",\n", + " 4 * sizeof(int), CACHE_LINE);" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "---\n", + "## Experiment 1 - False Sharing (Bad)\n", + "\n", + "Each thread writes to its own `counter[thread_id]` - **different variables**, so no race condition. \n", + "But all 4 counters fit in **one cache line** → every write invalidates the line for all other threads." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "False sharing: 0.2637 s (total = 100000000)\n" + ] + } + ], + "source": [ + "void false_sharing_demo() {\n", + " // All 4 counters sit next to each other in memory = one cache line\n", + " int counters[THREADS];\n", + " memset(counters, 0, sizeof(counters));\n", + "\n", + " double t0 = omp_get_wtime();\n", + "\n", + " #pragma omp parallel num_threads(THREADS)\n", + " {\n", + " int tid = omp_get_thread_num();\n", + " for (int i = 0; i < ITERATIONS / THREADS; i++) {\n", + " counters[tid]++; // No race condition, but FALSE SHARING!\n", + " }\n", + " }\n", + "\n", + " double elapsed = omp_get_wtime() - t0;\n", + "\n", + " long long total = 0;\n", + " for (int t = 0; t < THREADS; t++) total += counters[t];\n", + " printf(\"False sharing: %.4f s (total = %lld)\\n\", elapsed, total);\n", + "}\n", + "\n", + "false_sharing_demo();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "---\n", + "## Experiment 2 - No False Sharing (Padded)\n", + "\n", + "We pad each counter so it occupies a **full 64-byte cache line** by itself. \n", + "Now each thread's counter is on its own cache line → writes don't interfere.\n", + "\n", + "```\n", + "Cache line 1 (64 bytes) Cache line 2 (64 bytes)\n", + "┌────────┬──────────────────┐ ┌────────┬──────────────────┐\n", + "│counter0│ padding(60B) │ │counter1│ padding(60B) │\n", + "└────────┴──────────────────┘ └────────┴──────────────────┘\n", + " Thread 0 only Thread 1 only\n", + "```" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Padded (no FS): 0.0347 s (total = 100000000)\n" + ] + } + ], + "source": [ + "void no_false_sharing_demo() {\n", + " // Pad each counter to 64 bytes so each lives on its own cache line\n", + " struct {\n", + " int value;\n", + " char padding[60]; // 4 + 60 = 64 bytes = one full cache line\n", + " } counters[THREADS];\n", + "\n", + " for (int t = 0; t < THREADS; t++) counters[t].value = 0;\n", + "\n", + " double t0 = omp_get_wtime();\n", + "\n", + " #pragma omp parallel num_threads(THREADS)\n", + " {\n", + " int tid = omp_get_thread_num();\n", + " for (int i = 0; i < ITERATIONS / THREADS; i++) {\n", + " counters[tid].value++; // Each thread has its own cache line\n", + " }\n", + " }\n", + "\n", + " double elapsed = omp_get_wtime() - t0;\n", + "\n", + " long long total = 0;\n", + " for (int t = 0; t < THREADS; t++) total += counters[t].value;\n", + " printf(\"Padded (no FS): %.4f s (total = %lld)\\n\", elapsed, total);\n", + "}\n", + "\n", + "no_false_sharing_demo();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "---\n", + "## Experiment 3 - Private Variables (Best)\n", + "\n", + "Even better than padding: use **thread-private local variables** on the stack. \n", + "Stack variables are guaranteed to be in the thread's own memory - no sharing at all." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Private vars: 0.0433 s (total = 100000000)\n" + ] + } + ], + "source": [ + "void private_vars_demo() {\n", + " long long total = 0;\n", + "\n", + " double t0 = omp_get_wtime();\n", + "\n", + " #pragma omp parallel num_threads(THREADS) reduction(+:total)\n", + " {\n", + " long long local = 0; // lives on THIS thread's stack - completely private\n", + " for (int i = 0; i < ITERATIONS / THREADS; i++) {\n", + " local++; // no sharing of any kind\n", + " }\n", + " total += local; // reduction merges at the end\n", + " }\n", + "\n", + " double elapsed = omp_get_wtime() - t0;\n", + " printf(\"Private vars: %.4f s (total = %lld)\\n\", elapsed, total);\n", + "}\n", + "\n", + "private_vars_demo();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "---\n", + "## Experiment 4 - All Three Side by Side with Speedup" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " Method Time Speedup vs FS\n", + "--------------------------------------------\n", + " False sharing: 0.2729 s 1.0x\n", + " Padded: 0.0315 s 8.7x\n", + " Private vars: 0.0425 s 6.4x\n" + ] + } + ], + "source": [ + "void run_all() {\n", + " double t0, t_fs, t_pad, t_priv;\n", + " long long total;\n", + "\n", + " //False Sharing\n", + " int counters_bad[THREADS];\n", + " memset(counters_bad, 0, sizeof(counters_bad));\n", + " t0 = omp_get_wtime();\n", + " #pragma omp parallel num_threads(THREADS)\n", + " {\n", + " int tid = omp_get_thread_num();\n", + " for (int i = 0; i < ITERATIONS / THREADS; i++)\n", + " counters_bad[tid]++;\n", + " }\n", + " t_fs = omp_get_wtime() - t0;\n", + "\n", + " //Padded\n", + " struct { int value; char pad[60]; } counters_good[THREADS];\n", + " for (int t = 0; t < THREADS; t++) counters_good[t].value = 0;\n", + " t0 = omp_get_wtime();\n", + " #pragma omp parallel num_threads(THREADS)\n", + " {\n", + " int tid = omp_get_thread_num();\n", + " for (int i = 0; i < ITERATIONS / THREADS; i++)\n", + " counters_good[tid].value++;\n", + " }\n", + " t_pad = omp_get_wtime() - t0;\n", + "\n", + " //Private\n", + " total = 0;\n", + " t0 = omp_get_wtime();\n", + " #pragma omp parallel num_threads(THREADS) reduction(+:total)\n", + " {\n", + " long long local = 0;\n", + " for (int i = 0; i < ITERATIONS / THREADS; i++)\n", + " local++;\n", + " total += local;\n", + " }\n", + " t_priv = omp_get_wtime() - t0;\n", + "\n", + " //Results\n", + " printf(\" Method Time Speedup vs FS\\n\");\n", + " printf(\"--------------------------------------------\\n\");\n", + " printf(\" False sharing: %.4f s 1.0x\\n\", t_fs);\n", + " printf(\" Padded: %.4f s %.1fx\\n\", t_pad, t_fs / t_pad);\n", + " printf(\" Private vars: %.4f s %.1fx\\n\", t_priv, t_fs / t_priv);\n", + "}\n", + "\n", + "run_all();" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "C++23 + OpenMP", + "language": "cpp", + "name": "xcpp23-omp" + }, + "language_info": { + "codemirror_mode": "text/x-c++src", + "file_extension": ".cpp", + "mimetype": "text/x-c++src", + "name": "C++", + "nbconvert_exporter": "", + "pygments_lexer": "", + "version": "cxx23" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/openmp/07_race_conditions.ipynb b/openmp/07_race_conditions.ipynb new file mode 100644 index 0000000..2e18e77 --- /dev/null +++ b/openmp/07_race_conditions.ipynb @@ -0,0 +1,518 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "de7f748b", + "metadata": {}, + "source": [ + "# Race Conditions in OpenMP\n", + "\n", + "A **race condition** occurs when two or more threads access shared memory simultaneously, and at least one of them is writing. The result depends on the unpredictable order of execution - making bugs hard to reproduce and debug." + ] + }, + { + "cell_type": "markdown", + "id": "fc192d05", + "metadata": {}, + "source": [ + "## 1. The Classic Race Condition - Parallel Counter\n", + "\n", + "We want to increment a shared counter 1 000 000 times using multiple threads.\n", + "Intuitively `counter++` looks like one action, but it compiles to **three** instructions:\n", + "\n", + "```\n", + "LOAD reg ← counter ; read current value\n", + "ADD reg, 1 ; increment in register\n", + "STORE counter ← reg ; write back\n", + "```\n", + "\n", + "If two threads execute these steps concurrently, one write will silently overwrite the other - an **increment is lost**." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "3e2e951e", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Run 1: counter = 458255 (expected 1000000, lost 541745)\n", + "Run 2: counter = 340448 (expected 1000000, lost 659552)\n", + "Run 3: counter = 367539 (expected 1000000, lost 632461)\n", + "Run 4: counter = 367803 (expected 1000000, lost 632197)\n", + "Run 5: counter = 405751 (expected 1000000, lost 594249)\n" + ] + } + ], + "source": [ + "#include \n", + "#include \n", + "#define N 1000000\n", + "\n", + "void example1()\n", + "{\n", + " int counter = 0;\n", + " \n", + " // Run the experiment 5 times to show non-determinism\n", + " for (int run = 0; run < 5; run++) {\n", + " counter = 0;\n", + " #pragma omp parallel for num_threads(4)\n", + " {\n", + " for (int i = 0; i < N; i++) {\n", + " counter++; // RACE CONDITION: no protection\n", + " }\n", + " }\n", + " printf(\"Run %d: counter = %d (expected %d, lost %d)\\n\",\n", + " run+1, counter, N, N - counter);\n", + " }\n", + "}\n", + "\n", + "example1();" + ] + }, + { + "cell_type": "markdown", + "id": "6ab1f67b", + "metadata": {}, + "source": [ + "### What you should observe\n", + "\n", + "- The counter is **almost never** 1 000 000.\n", + "- Each run gives a **different** wrong answer - the race is truly non-deterministic.\n", + "\n", + "> **Key insight**: You cannot rely on \"it worked in my tests\" for racy code. On a lightly loaded machine you might get lucky; under production load the bug emerges." + ] + }, + { + "cell_type": "markdown", + "id": "8d67c01b", + "metadata": {}, + "source": [ + "---\n", + "## 2. Fix #1 - `#pragma omp critical`\n", + "\n", + "`critical` creates a **named mutex**: only one thread at a time may execute the enclosed block. It is the most general but also the most expensive fix." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "bb9dac12", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "critical: counter = 1000000, time = 0.1604 s\n" + ] + } + ], + "source": [ + "#include\n", + "\n", + "void example2() {\n", + " int counter = 0;\n", + " double t0 = omp_get_wtime();\n", + "\n", + " #pragma omp parallel for num_threads(4)\n", + " for (int i = 0; i < N; i++) {\n", + " #pragma omp critical\n", + " {\n", + " counter++;\n", + " }\n", + " }\n", + "\n", + " double elapsed = omp_get_wtime() - t0;\n", + " printf(\"critical: counter = %d, time = %.4f s\\n\", counter, elapsed);\n", + "}\n", + "\n", + "example2();" + ] + }, + { + "cell_type": "markdown", + "id": "ec744b0d", + "metadata": {}, + "source": [ + "The counter is now correct. But notice: with `critical`, threads spend most of their time **queuing at the mutex** rather than doing work. This makes parallel code *slower* than sequential for fine-grained operations.\n", + "\n", + "**When to use `critical`**: When the protected block is non-trivial (multiple statements, complex logic) and cannot be expressed as a single atomic operation." + ] + }, + { + "cell_type": "markdown", + "id": "8762443d", + "metadata": {}, + "source": [ + "---\n", + "## 3. Fix #2 - `#pragma omp atomic`\n", + "\n", + "`atomic` maps directly to a hardware atomic instruction (e.g., `LOCK XADD` on x86). It is **much faster** than `critical` for simple read-modify-write operations." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "8358fa20", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "atomic: counter = 1000000, time = 0.0168 s\n" + ] + } + ], + "source": [ + "void example3() {\n", + " int counter = 0;\n", + " double t0 = omp_get_wtime();\n", + "\n", + " #pragma omp parallel for num_threads(4)\n", + " for (int i = 0; i < N; i++) {\n", + " #pragma omp atomic\n", + " counter++; // maps to a single atomic hardware instruction\n", + " }\n", + "\n", + " double elapsed = omp_get_wtime() - t0;\n", + " printf(\"atomic: counter = %d, time = %.4f s\\n\", counter, elapsed);\n", + "}\n", + "\n", + "example3();" + ] + }, + { + "cell_type": "markdown", + "id": "6c34b191", + "metadata": {}, + "source": [ + "### `atomic` clauses (OpenMP 3.1+)\n", + "\n", + "| Clause | Effect |\n", + "|--------|--------|\n", + "| `atomic update` (default) | `x++`, `x--`, `x += expr`, etc. |\n", + "| `atomic read` | `v = x` atomically |\n", + "| `atomic write` | `x = expr` atomically |\n", + "| `atomic capture` | `v = x; x op= expr` - read + update together |\n", + "\n", + "**Limitation**: `atomic` only works for a *single* variable with a *single* supported operator. Use `critical` for anything more complex." + ] + }, + { + "cell_type": "markdown", + "id": "ce4969eb", + "metadata": {}, + "source": [ + "---\n", + "## 4. Fix #3 - `reduction` clause (the fastest approach)\n", + "\n", + "The `reduction` clause is the idiomatic OpenMP solution for accumulation. Each thread gets a **private copy** of the variable, works independently (no synchronisation!), and OpenMP merges the results at the end of the parallel region.\n", + "\n", + "This is a **divide-and-conquer** strategy: eliminate the shared state entirely during the parallel phase." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "cf2a0336", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "reduction: counter = 1000000, time = 0.0005 s\n" + ] + } + ], + "source": [ + "void example4() {\n", + " int counter = 0;\n", + " double t0 = omp_get_wtime();\n", + "\n", + " #pragma omp parallel for num_threads(4) reduction(+:counter)\n", + " for (int i = 0; i < N; i++) {\n", + " counter++; // each thread increments its own private copy\n", + " }\n", + "\n", + " double elapsed = omp_get_wtime() - t0;\n", + " printf(\"reduction: counter = %d, time = %.4f s\\n\", counter, elapsed);\n", + "}\n", + "\n", + "example4();" + ] + }, + { + "cell_type": "markdown", + "id": "e1b99804", + "metadata": {}, + "source": [ + "### Supported reduction operators\n", + "\n", + "| Operator | Initial value | Use case |\n", + "|----------|--------------|----------|\n", + "| `+` | 0 | Sum |\n", + "| `*` | 1 | Product |\n", + "| `-` | 0 | Subtraction accumulation |\n", + "| `min` | type max | Minimum value |\n", + "| `max` | type min | Maximum value |\n", + "| `&` | ~0 | Bitwise AND |\n", + "| `\\|` | 0 | Bitwise OR |\n", + "| `^` | 0 | Bitwise XOR |\n", + "| `&&` | 1 | Logical AND |\n", + "| `\\|\\|` | 0 | Logical OR |" + ] + }, + { + "cell_type": "markdown", + "id": "572cbe98", + "metadata": {}, + "source": [ + "---\n", + "## 5. Benchmark - Compare All Approaches" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "f67d05a5", + "metadata": { + "scrolled": true + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "sequential: 0.001751 s\n", + "critical: 0.165203 s\n", + "atomic: 0.019443 s\n", + "reduction: 0.000474 s\n" + ] + } + ], + "source": [ + "#define THREADS 4\n", + "#define RUNS 5\n", + "\n", + "double bench_sequential() {\n", + " long long c = 0;\n", + " double t0 = omp_get_wtime();\n", + " for (int i = 0; i < N; i++) c++;\n", + " return omp_get_wtime() - t0;\n", + "}\n", + "\n", + "double bench_critical() {\n", + " long long c = 0;\n", + " double t0 = omp_get_wtime();\n", + " #pragma omp parallel for num_threads(THREADS)\n", + " for (int i = 0; i < N; i++) {\n", + " #pragma omp critical\n", + " c++;\n", + " }\n", + " return omp_get_wtime() - t0;\n", + "}\n", + "\n", + "double bench_atomic() {\n", + " long long c = 0;\n", + " double t0 = omp_get_wtime();\n", + " #pragma omp parallel for num_threads(THREADS)\n", + " for (int i = 0; i < N; i++) {\n", + " #pragma omp atomic\n", + " c++;\n", + " }\n", + " return omp_get_wtime() - t0;\n", + "}\n", + "\n", + "double bench_reduction() {\n", + " long long c = 0;\n", + " double t0 = omp_get_wtime();\n", + " #pragma omp parallel for num_threads(THREADS) reduction(+:c)\n", + " for (int i = 0; i < N; i++) c++;\n", + " return omp_get_wtime() - t0;\n", + "}\n", + "\n", + "double avg(double (*fn)()) {\n", + " double s = 0;\n", + " for (int i = 0; i < RUNS; i++) s += fn();\n", + " return s / RUNS;\n", + "}\n", + "\n", + "printf(\"sequential: %.6f s\\n\", avg(bench_sequential));\n", + "printf(\"critical: %.6f s\\n\", avg(bench_critical));\n", + "printf(\"atomic: %.6f s\\n\", avg(bench_atomic));\n", + "printf(\"reduction: %.6f s\\n\", avg(bench_reduction));" + ] + }, + { + "cell_type": "markdown", + "id": "a26c94cb", + "metadata": {}, + "source": [ + "### Reading the results\n", + "\n", + "| Approach | Why it's slow/fast |\n", + "|----------|--------------------|\n", + "| **sequential** | Baseline: single-threaded, no sync overhead |\n", + "| **critical** | Threads serialise at the mutex - effectively sequential *plus* lock overhead |\n", + "| **atomic** | Hardware atomic instruction: much cheaper than a mutex, but still causes cache-line bouncing between cores |\n", + "| **reduction** | No synchronisation during the loop; one merge at the end - fastest of the parallel options |" + ] + }, + { + "cell_type": "markdown", + "id": "71d7fe12", + "metadata": {}, + "source": [ + "---\n", + "## 6. A More Realistic Example - Histogram\n", + "\n", + "Counters are a toy problem. A **histogram** is a real-world accumulation where multiple bins are updated, making `reduction` over a single variable insufficient. This shows `critical` vs `atomic` in a meaningful context." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "abcaac25", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "atomic (per element): 0.1241 s\n", + "private + merge: 0.0066 s (speedup vs atomic: 18.9x)\n", + "\n", + "Histogram (should sum to 10000000):\n", + " bin[ 0] = 623773\n", + " bin[ 1] = 624866\n", + " bin[ 2] = 624957\n", + " bin[ 3] = 624934\n", + " bin[ 4] = 625238\n", + " bin[ 5] = 626471\n", + " bin[ 6] = 625747\n", + " bin[ 7] = 624270\n", + " bin[ 8] = 624621\n", + " bin[ 9] = 624898\n", + " bin[10] = 624977\n", + " bin[11] = 625217\n", + " bin[12] = 624620\n", + " bin[13] = 625424\n", + " bin[14] = 625240\n", + " bin[15] = 624747\n", + "Total: 10000000\n" + ] + } + ], + "source": [ + "#include \n", + "#include \n", + "\n", + "#define N_SAMPLES 10000000\n", + "#define N_BINS 16\n", + "\n", + "// Approach A: atomic update per bin\n", + "double histogram_atomic(int *data, long long *hist) {\n", + " memset(hist, 0, N_BINS * sizeof(long long));\n", + " double t0 = omp_get_wtime();\n", + " #pragma omp parallel for num_threads(THREADS)\n", + " for (int i = 0; i < N_SAMPLES; i++) {\n", + " #pragma omp atomic\n", + " hist[data[i]]++;\n", + " }\n", + " return omp_get_wtime() - t0;\n", + "}\n", + "\n", + "// Approach B: private histogram per thread, merge at end\n", + "double histogram_private(int *data, long long *hist) {\n", + " memset(hist, 0, N_BINS * sizeof(long long));\n", + " double t0 = omp_get_wtime();\n", + " #pragma omp parallel num_threads(THREADS)\n", + " {\n", + " long long local[N_BINS] = {0}; // thread-private copy\n", + " #pragma omp for\n", + " for (int i = 0; i < N_SAMPLES; i++)\n", + " local[data[i]]++;\n", + " // Merge: each bin is independent, so atomic is fine here\n", + " for (int b = 0; b < N_BINS; b++) {\n", + " #pragma omp atomic\n", + " hist[b] += local[b];\n", + " }\n", + " }\n", + " return omp_get_wtime() - t0;\n", + "}\n", + "\n", + "// Generate random data\n", + "int *data = (int*)malloc(N_SAMPLES * sizeof(int));\n", + "srand(42);\n", + "for (int i = 0; i < N_SAMPLES; i++) data[i] = rand() % N_BINS;\n", + "\n", + "long long hist[N_BINS];\n", + "\n", + "double t_atomic = histogram_atomic(data, hist);\n", + "double t_private = histogram_private(data, hist);\n", + "\n", + "printf(\"atomic (per element): %.4f s\\n\", t_atomic);\n", + "printf(\"private + merge: %.4f s (speedup vs atomic: %.1fx)\\n\",\n", + " t_private, t_atomic / t_private);\n", + "\n", + "// Print histogram to verify correctness\n", + "long long total = 0;\n", + "printf(\"\\nHistogram (should sum to %d):\\n\", N_SAMPLES);\n", + "for (int b = 0; b < N_BINS; b++) { printf(\" bin[%2d] = %lld\\n\", b, hist[b]); total += hist[b]; }\n", + "printf(\"Total: %lld\\n\", total);\n", + "\n", + "free(data);" + ] + }, + { + "cell_type": "markdown", + "id": "6371ac17", + "metadata": {}, + "source": [ + "The **private histogram** pattern is the general-purpose technique when you can't express the accumulation as a single built-in `reduction`. It trades memory (one copy of the histogram per thread) for speed (zero disagreement during the loop).\n", + "\n", + "> **Rule of thumb**: If threads all write to the *same* bin at the same time, cache-line contention kills performance even with `atomic`. Private copies eliminate this entirely." + ] + }, + { + "cell_type": "markdown", + "id": "be248cdf", + "metadata": {}, + "source": [ + "---\n", + "## 7. Summary - When to Use What\n", + "\n", + "| Mechanism | Overhead | Flexibility | Best for |\n", + "|-----------|----------|-------------|----------|\n", + "| `reduction` | **Lowest** | Only built-in ops on loop variable | Sums, products, min/max in loops |\n", + "| `atomic` | Low | Single variable, supported operators | Simple shared counters/flags |\n", + "| `critical` | **Highest** | Arbitrary code | Complex shared-state updates |\n", + "| Private + merge | Low (memory) | Anything | Histograms, complex accumulators |" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "C++23 + OpenMP", + "language": "cpp", + "name": "xcpp23-omp" + }, + "language_info": { + "codemirror_mode": "text/x-c++src", + "file_extension": ".cpp", + "mimetype": "text/x-c++src", + "name": "C++", + "nbconvert_exporter": "", + "pygments_lexer": "", + "version": "cxx23" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} From 2bca35628a5e4cea1c24fb8b9830113e62b6b496 Mon Sep 17 00:00:00 2001 From: Hristiyan Shterev Date: Sat, 25 Apr 2026 22:14:37 +0300 Subject: [PATCH 3/4] Added new notebooks + updated old ones --- openmp/01_openmp-demo.ipynb | 8 + openmp/04_mandel.ipynb | 6 +- ...ditions.ipynb => 06_race_conditions.ipynb} | 15 +- ...e_sharing.ipynb => 07_false_sharing.ipynb} | 2 +- openmp/08_game_of_life.ipynb | 623 ++++++++++++++++++ 5 files changed, 642 insertions(+), 12 deletions(-) rename openmp/{07_race_conditions.ipynb => 06_race_conditions.ipynb} (98%) rename openmp/{06_false_sharing.ipynb => 07_false_sharing.ipynb} (99%) create mode 100644 openmp/08_game_of_life.ipynb diff --git a/openmp/01_openmp-demo.ipynb b/openmp/01_openmp-demo.ipynb index 67d996c..4921645 100644 --- a/openmp/01_openmp-demo.ipynb +++ b/openmp/01_openmp-demo.ipynb @@ -72,6 +72,8 @@ "id": "f37a7cfd-f158-4ef9-a4cf-977296ccf5d4", "metadata": {}, "source": [ + "---\n", + "\n", "Now, let's use OpenMP to run the same code in parallel. By adding a simple compiler directive, we tell the system to create a \"team\" of threads.\n", "\n", "```#pragma omp parallel``` This directive tells the compiler to execute the following block of code in parallel using multiple threads.\n", @@ -115,6 +117,8 @@ "id": "f1568cd4-d407-49c7-98e4-26f971fdb63d", "metadata": {}, "source": [ + "---\n", + "\n", "There is one thing we need to address before we continue\n", "\n", "When we use #pragma omp parallel, OpenMP spawns multiple threads that all execute the code inside the curly braces simultaneously. Every thread is trying to talk to our monitor at the exact same time.\n", @@ -163,6 +167,8 @@ "id": "a6123ee3-92b9-42fe-b1a0-57f9764569aa", "metadata": {}, "source": [ + "---\n", + "\n", "Other things we can do in OMP, includes using a set number of threads that are going to be used: ```#pragma omp parallel num_threads(2)``` " ] }, @@ -215,6 +221,8 @@ "id": "25421d19-67e5-4bc9-9420-d96439cffba5", "metadata": {}, "source": [ + "---\n", + "\n", "Now that you know basic syntax we can do a speed benchmark. We are going to time filling two arrays with 1 million elements each, adding them and calculating the average. In the average calculation there is a problem that can occur. In parallel programming, if multiple threads try to add to the same average variable at the same time, they will overwrite each other. When we use ```reduction(+:average)```, OpenMP does the following:\n", "\n", "- Each thread gets its own private mini-average variable initialized to 0.\n", diff --git a/openmp/04_mandel.ipynb b/openmp/04_mandel.ipynb index 29646d0..eb4aba8 100644 --- a/openmp/04_mandel.ipynb +++ b/openmp/04_mandel.ipynb @@ -42,6 +42,7 @@ "id": "f86e5fd6-f657-4848-8f80-780b5ca67678", "metadata": {}, "source": [ + "---\n", "1. **Constants Definition**:\n", " - `NPOINTS = 5000`: The grid size, creating a 5000x5000 grid.\n", " - `MAXITER = 1000`: Maximum iterations per point to determine if it escapes." @@ -65,6 +66,7 @@ "id": "dfd58cbd-798b-472c-869e-5a9de42a502e", "metadata": {}, "source": [ + "---\n", "2. Function testpoint:\n", " - Takes complex number parameters `creal` and `cimag`.\n", " - Initializes `zreal` and `zimag` to 0.\n", @@ -105,6 +107,8 @@ "id": "53fb8c2a-5b6e-41bd-a6eb-d31ab6c94ad0", "metadata": {}, "source": [ + "---\n", + "\n", "3. **Main Function**\n", " - Maps grid indices to complex numbers within a specific range using scaling factors:\n", " ```\n", @@ -116,7 +120,7 @@ " - `private(i, j, creal, cimag)` ensures each thread has its own copy of these variables.\n", " - `reduction(+:numoutside)` accumulates the total count of escaping points.\n", "\n", - "\n", + "---\n", "3. **Area Calculation**:\n", " - Computes area as `(2.0 * 2.5 * 1.125) * ((NPOINTS^2 - numoutside) / NPOINTS^2)`\n", " - Error estimate is derived by dividing area by `NPOINTS`." diff --git a/openmp/07_race_conditions.ipynb b/openmp/06_race_conditions.ipynb similarity index 98% rename from openmp/07_race_conditions.ipynb rename to openmp/06_race_conditions.ipynb index 2e18e77..9c898a4 100644 --- a/openmp/07_race_conditions.ipynb +++ b/openmp/06_race_conditions.ipynb @@ -1,20 +1,15 @@ { "cells": [ - { - "cell_type": "markdown", - "id": "de7f748b", - "metadata": {}, - "source": [ - "# Race Conditions in OpenMP\n", - "\n", - "A **race condition** occurs when two or more threads access shared memory simultaneously, and at least one of them is writing. The result depends on the unpredictable order of execution - making bugs hard to reproduce and debug." - ] - }, { "cell_type": "markdown", "id": "fc192d05", "metadata": {}, "source": [ + "# Race Conditions\n", + "\n", + "A **race condition** occurs when two or more threads access shared memory simultaneously, and at least one of them is writing. The result depends on the unpredictable order of execution - making bugs hard to reproduce and debug.\n", + "\n", + "---\n", "## 1. The Classic Race Condition - Parallel Counter\n", "\n", "We want to increment a shared counter 1 000 000 times using multiple threads.\n", diff --git a/openmp/06_false_sharing.ipynb b/openmp/07_false_sharing.ipynb similarity index 99% rename from openmp/06_false_sharing.ipynb rename to openmp/07_false_sharing.ipynb index ff8aa42..4419172 100644 --- a/openmp/06_false_sharing.ipynb +++ b/openmp/07_false_sharing.ipynb @@ -4,7 +4,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# False Sharing in OpenMP\n", + "# False Sharing\n", "\n", "You fixed the race condition\n", "Your atomic code is correct\n", diff --git a/openmp/08_game_of_life.ipynb b/openmp/08_game_of_life.ipynb new file mode 100644 index 0000000..09f39dc --- /dev/null +++ b/openmp/08_game_of_life.ipynb @@ -0,0 +1,623 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Conway's Game of Life - Parallel with OpenMP\n", + "\n", + "## The Rules (just 4)\n", + "\n", + "Each cell is either **alive** (1) or **dead** (0). Every generation, each cell looks at its **8 neighbours**:\n", + "\n", + "```\n", + "┌───┬───┬───┐\n", + "│ N │ N │ N │\n", + "├───┼───┼───┤\n", + "│ N │ X │ N │ X = current cell, N = neighbours\n", + "├───┼───┼───┤\n", + "│ N │ N │ N │\n", + "└───┴───┴───┘\n", + "```\n", + "\n", + "| Cell state | Neighbours | Next state | Rule name |\n", + "|---|---|---|---|\n", + "| Alive | < 2 | Dies | Underpopulation |\n", + "| Alive | 2 or 3 | Survives | Survival |\n", + "| Alive | > 3 | Dies | Overpopulation |\n", + "| Dead | exactly 3 | Born | Reproduction |\n", + "\n", + "Simple rules → incredibly complex emergent behaviour.\n", + "\n", + "---\n", + "\n", + "## The Parallelism Problem\n", + "\n", + "Every cell needs to **read** its neighbours and **write** its new state simultaneously.\n", + "If we read and write the same grid, a cell updated early would corrupt the neighbours of cells updated later.\n", + "\n", + "The solution: **double buffering** - two grids, always read from one, write to the other, then swap.\n", + "\n", + "---\n", + "## 1. Setup & Grid Utilities" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Grid size: 8 x 16 = 128 cells\n", + "Threads: 4\n", + "Each thread handles ~2 rows\n" + ] + } + ], + "source": [ + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "\n", + "#define ROWS 8\n", + "#define COLS 16\n", + "#define THREADS 4\n", + "\n", + "// Two grids - the core of double buffering\n", + "// We ALWAYS read from grid[0], write to grid[1], then swap pointers\n", + "int grid_A[ROWS][COLS];\n", + "int grid_B[ROWS][COLS];\n", + "int (*current)[COLS] = grid_A; // pointer to the grid we READ from\n", + "int (*next)[COLS] = grid_B; // pointer to the grid we WRITE to\n", + "\n", + "// Print the grid nicely\n", + "void print_grid(int g[ROWS][COLS], int gen) {\n", + " printf(\"Generation %d:\\n\", gen);\n", + " for (int r = 0; r < ROWS; r++) {\n", + " printf(\"|\");\n", + " for (int c = 0; c < COLS; c++)\n", + " printf(\"%c\", g[r][c] ? '#' : '.');\n", + " printf(\"|\\n\");\n", + " }\n", + "}\n", + "\n", + "// Count living cells\n", + "int count_alive(int g[ROWS][COLS]) {\n", + " int count = 0;\n", + " for (int r = 0; r < ROWS; r++)\n", + " for (int c = 0; c < COLS; c++)\n", + " count += g[r][c];\n", + " return count;\n", + "}\n", + "\n", + "printf(\"Grid size: %d x %d = %d cells\\n\", ROWS, COLS, ROWS * COLS);\n", + "printf(\"Threads: %d\\n\", THREADS);\n", + "printf(\"Each thread handles ~%d rows\\n\", ROWS / THREADS);" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "---\n", + "## 2. Why Double Buffering?\n", + "\n", + "Before we parallelize, let's understand the core problem.\n", + "\n", + "```\n", + "WRONG - single grid (read and write same grid):\n", + "\n", + " Step 1: update cell (2,3) → writes new value into grid[2][3]\n", + " Step 2: update cell (2,4) → reads grid[2][3] as a neighbour\n", + " BUT grid[2][3] is already the NEW value!\n", + " Cell (2,4) is computing based on corrupted data.\n", + "\n", + "CORRECT - double buffer:\n", + " \n", + " current[][] = old state (READ ONLY)\n", + " next[][] = new state (WRITE ONLY)\n", + "\n", + " All cells read from current[][] simultaneously - no corruption possible\n", + " All cells write to next[][] simultaneously - no conflicts\n", + " After all cells done: swap pointers (next becomes current)\n", + "```\n", + "\n", + "This is exactly what makes it safe to parallelize - threads can compute any cell in any order because they all read from the same frozen snapshot.\n", + "\n", + "---\n", + "## 3. The Step Function (Core Logic)\n", + "\n", + "`collapse(2)` flattens both loops so OpenMP splits all **128 cells** (8×16) across 4 threads — 32 cells per thread." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "step() and count_neighbours() defined.\n", + "collapse(2) flattens the 2D loop into 1D so OpenMP can split 8*16=128 cells across 4 threads\n" + ] + } + ], + "source": [ + "// Count live neighbours of cell (r, c)\n", + "// Uses wrap-around edges (toroidal grid — top connects to bottom, left to right)\n", + "int count_neighbours(int g[ROWS][COLS], int r, int c) {\n", + " int count = 0;\n", + " for (int dr = -1; dr <= 1; dr++) {\n", + " for (int dc = -1; dc <= 1; dc++) {\n", + " if (dr == 0 && dc == 0) continue; // skip self\n", + " int nr = (r + dr + ROWS) % ROWS; // wrap around vertically\n", + " int nc = (c + dc + COLS) % COLS; // wrap around horizontally\n", + " count += g[nr][nc];\n", + " }\n", + " }\n", + " return count;\n", + "}\n", + "\n", + "// One generation step - parallel with OpenMP\n", + "// READ from: src (frozen snapshot)\n", + "// WRITE to: dst (fresh empty grid)\n", + "void step(int src[ROWS][COLS], int dst[ROWS][COLS]) {\n", + " #pragma omp parallel for num_threads(THREADS) collapse(2)\n", + " for (int r = 0; r < ROWS; r++) {\n", + " for (int c = 0; c < COLS; c++) {\n", + " int neighbours = count_neighbours(src, r, c);\n", + " int alive = src[r][c];\n", + "\n", + " // Apply the 4 rules\n", + " if (alive && neighbours < 2) dst[r][c] = 0; // underpopulation\n", + " else if (alive && neighbours <= 3) dst[r][c] = 1; // survival\n", + " else if (alive && neighbours > 3) dst[r][c] = 0; // overpopulation\n", + " else if (!alive && neighbours == 3) dst[r][c] = 1; // reproduction\n", + " else dst[r][c] = 0; // stays dead\n", + " }\n", + " }\n", + "}\n", + "\n", + "printf(\"step() and count_neighbours() defined.\\n\");\n", + "printf(\"collapse(2) flattens the 2D loop into 1D so OpenMP can split %d*%d=%d cells across %d threads\\n\",\n", + " ROWS, COLS, ROWS*COLS, THREADS);" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "---\n", + "## 4. `collapse(2)` Explained\n", + "\n", + "Without `collapse(2)`, OpenMP only splits the **outer loop** (rows):\n", + "```\n", + "Thread 0 → rows 0-1 (each doing all 16 columns)\n", + "Thread 1 → rows 2-3\n", + "Thread 2 → rows 4-5\n", + "Thread 3 → rows 6-7\n", + "```\n", + "With `collapse(2)`, OpenMP merges both loops and splits **all 128 cells**:\n", + "```\n", + "Thread 0 → cells 0-31\n", + "Thread 1 → cells 32-63\n", + "Thread 2 → cells 64-95\n", + "Thread 3 → cells 96-127\n", + "```\n", + "On a small 8×16 grid this matters more — without collapse, each thread only gets 2 rows which is very coarse granularity." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "---\n", + "## 5. Pattern 1: Blinker (Period-2 Oscillator)\n", + "\n", + "The simplest oscillator - 3 cells in a line that flip between horizontal and vertical every generation. Placed in the middle of the 8×16 grid at row 4, col 8." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "=== Blinker (should alternate horizontal/vertical) ===\n", + "\n", + "Generation 0:\n", + "|................|\n", + "|................|\n", + "|................|\n", + "|................|\n", + "|.......###......|\n", + "|................|\n", + "|................|\n", + "|................|\n", + "Generation 1:\n", + "|................|\n", + "|................|\n", + "|................|\n", + "|........#.......|\n", + "|........#.......|\n", + "|........#.......|\n", + "|................|\n", + "|................|\n", + "Generation 2:\n", + "|................|\n", + "|................|\n", + "|................|\n", + "|................|\n", + "|.......###......|\n", + "|................|\n", + "|................|\n", + "|................|\n", + "Generation 3:\n", + "|................|\n", + "|................|\n", + "|................|\n", + "|........#.......|\n", + "|........#.......|\n", + "|........#.......|\n", + "|................|\n", + "|................|\n", + "Generation 4:\n", + "|................|\n", + "|................|\n", + "|................|\n", + "|................|\n", + "|.......###......|\n", + "|................|\n", + "|................|\n", + "|................|\n" + ] + } + ], + "source": [ + "void run_blinker() {\n", + " // Reset both grids\n", + " memset(grid_A, 0, sizeof(grid_A));\n", + " memset(grid_B, 0, sizeof(grid_B));\n", + " current = grid_A;\n", + " next = grid_B;\n", + "\n", + " // Place a horizontal blinker in the middle\n", + " int r = ROWS / 2;\n", + " int c = COLS / 2;\n", + " current[r][c-1] = 1;\n", + " current[r][c] = 1;\n", + " current[r][c+1] = 1;\n", + "\n", + " printf(\"=== Blinker (should alternate horizontal/vertical) ===\\n\\n\");\n", + " print_grid(current, 0);\n", + "\n", + " for (int gen = 1; gen <= 4; gen++) {\n", + " step(current, next);\n", + "\n", + " // Swap buffers — next becomes current, current becomes next\n", + " int (*tmp)[COLS] = current;\n", + " current = next;\n", + " next = tmp;\n", + "\n", + " print_grid(current, gen);\n", + " }\n", + "}\n", + "\n", + "run_blinker();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "---\n", + "## 6. Pattern 2: Glider\n", + "\n", + "The glider moves diagonally across the grid forever - it takes 4 generations to return to its original shape but shifted by 1 cell diagonally." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "=== Glider (watch it move diagonally) ===\n", + "\n", + "Generation 0:\n", + "|................|\n", + "|..#.............|\n", + "|...#............|\n", + "|.###............|\n", + "|................|\n", + "|................|\n", + "|................|\n", + "|................|\n", + "Generation 1:\n", + "|................|\n", + "|................|\n", + "|.#.#............|\n", + "|..##............|\n", + "|..#.............|\n", + "|................|\n", + "|................|\n", + "|................|\n", + "Generation 2:\n", + "|................|\n", + "|................|\n", + "|...#............|\n", + "|.#.#............|\n", + "|..##............|\n", + "|................|\n", + "|................|\n", + "|................|\n", + "Generation 3:\n", + "|................|\n", + "|................|\n", + "|..#.............|\n", + "|...##...........|\n", + "|..##............|\n", + "|................|\n", + "|................|\n", + "|................|\n", + "Generation 4:\n", + "|................|\n", + "|................|\n", + "|...#............|\n", + "|....#...........|\n", + "|..###...........|\n", + "|................|\n", + "|................|\n", + "|................|\n", + "Generation 5:\n", + "|................|\n", + "|................|\n", + "|................|\n", + "|..#.#...........|\n", + "|...##...........|\n", + "|...#............|\n", + "|................|\n", + "|................|\n", + "Generation 6:\n", + "|................|\n", + "|................|\n", + "|................|\n", + "|....#...........|\n", + "|..#.#...........|\n", + "|...##...........|\n", + "|................|\n", + "|................|\n", + "Generation 7:\n", + "|................|\n", + "|................|\n", + "|................|\n", + "|...#............|\n", + "|....##..........|\n", + "|...##...........|\n", + "|................|\n", + "|................|\n", + "Generation 8:\n", + "|................|\n", + "|................|\n", + "|................|\n", + "|....#...........|\n", + "|.....#..........|\n", + "|...###..........|\n", + "|................|\n", + "|................|\n" + ] + } + ], + "source": [ + "void run_glider() {\n", + " memset(grid_A, 0, sizeof(grid_A));\n", + " memset(grid_B, 0, sizeof(grid_B));\n", + " current = grid_A;\n", + " next = grid_B;\n", + "\n", + " // Classic glider pattern:\n", + " // .X.\n", + " // ..X\n", + " // XXX\n", + " current[1][2] = 1;\n", + " current[2][3] = 1;\n", + " current[3][1] = 1;\n", + " current[3][2] = 1;\n", + " current[3][3] = 1;\n", + "\n", + " printf(\"=== Glider (watch it move diagonally) ===\\n\\n\");\n", + " print_grid(current, 0);\n", + "\n", + " for (int gen = 1; gen <= 8; gen++) {\n", + " step(current, next);\n", + " int (*tmp)[COLS] = current;\n", + " current = next;\n", + " next = tmp;\n", + "\n", + " //if (gen % 2 == 0) // print every 2 generations to keep output short\n", + " print_grid(current, gen);\n", + " }\n", + "}\n", + "\n", + "run_glider();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "---\n", + "## 7. Performance: Sequential vs Parallel" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Grid: 512x512 = 262144 cells, 100 generations\n", + "=============================================\n", + " Sequential: 1.3265 s\n", + " Parallel: 0.3410 s (3.9x speedup)\n", + "=============================================\n" + ] + } + ], + "source": [ + "#define BENCH_ROWS 512\n", + "#define BENCH_COLS 512\n", + "#define GENERATIONS 100\n", + "\n", + "// Allocate large grids on the heap for benchmarking\n", + "int *bench_A = (int*)calloc(BENCH_ROWS * BENCH_COLS, sizeof(int));\n", + "int *bench_B = (int*)calloc(BENCH_ROWS * BENCH_COLS, sizeof(int));\n", + "\n", + "#define BENCH_IDX(r, c) ((r) * BENCH_COLS + (c))\n", + "\n", + "// Seed with random pattern\n", + "srand(42);\n", + "for (int i = 0; i < BENCH_ROWS * BENCH_COLS; i++)\n", + " bench_A[i] = rand() % 2;\n", + "\n", + "// Sequential step\n", + "void step_seq(int *src, int *dst) {\n", + " for (int r = 0; r < BENCH_ROWS; r++) {\n", + " for (int c = 0; c < BENCH_COLS; c++) {\n", + " int n = 0;\n", + " for (int dr = -1; dr <= 1; dr++)\n", + " for (int dc = -1; dc <= 1; dc++) {\n", + " if (dr == 0 && dc == 0) continue;\n", + " int nr = (r + dr + BENCH_ROWS) % BENCH_ROWS;\n", + " int nc = (c + dc + BENCH_COLS) % BENCH_COLS;\n", + " n += src[BENCH_IDX(nr, nc)];\n", + " }\n", + " int alive = src[BENCH_IDX(r, c)];\n", + " dst[BENCH_IDX(r, c)] = (alive && n == 2) || n == 3 ? 1 : 0;\n", + " }\n", + " }\n", + "}\n", + "\n", + "// Parallel step\n", + "void step_par(int *src, int *dst) {\n", + " #pragma omp parallel for num_threads(THREADS) collapse(2)\n", + " for (int r = 0; r < BENCH_ROWS; r++) {\n", + " for (int c = 0; c < BENCH_COLS; c++) {\n", + " int n = 0;\n", + " for (int dr = -1; dr <= 1; dr++)\n", + " for (int dc = -1; dc <= 1; dc++) {\n", + " if (dr == 0 && dc == 0) continue;\n", + " int nr = (r + dr + BENCH_ROWS) % BENCH_ROWS;\n", + " int nc = (c + dc + BENCH_COLS) % BENCH_COLS;\n", + " n += src[BENCH_IDX(nr, nc)];\n", + " }\n", + " int alive = src[BENCH_IDX(r, c)];\n", + " dst[BENCH_IDX(r, c)] = (alive && n == 2) || n == 3 ? 1 : 0;\n", + " }\n", + " }\n", + "}\n", + "\n", + "// Benchmark sequential\n", + "int *src = bench_A, *dst = bench_B;\n", + "double t0 = omp_get_wtime();\n", + "for (int g = 0; g < GENERATIONS; g++) {\n", + " step_seq(src, dst);\n", + " int *tmp = src; src = dst; dst = tmp;\n", + "}\n", + "double t_seq = omp_get_wtime() - t0;\n", + "\n", + "// Reset and benchmark parallel\n", + "srand(42);\n", + "for (int i = 0; i < BENCH_ROWS * BENCH_COLS; i++) bench_A[i] = rand() % 2;\n", + "src = bench_A; dst = bench_B;\n", + "t0 = omp_get_wtime();\n", + "for (int g = 0; g < GENERATIONS; g++) {\n", + " step_par(src, dst);\n", + " int *tmp = src; src = dst; dst = tmp;\n", + "}\n", + "double t_par = omp_get_wtime() - t0;\n", + "\n", + "printf(\"Grid: %dx%d = %d cells, %d generations\\n\",\n", + " BENCH_ROWS, BENCH_COLS, BENCH_ROWS * BENCH_COLS, GENERATIONS);\n", + "printf(\"=============================================\\n\");\n", + "printf(\" Sequential: %.4f s\\n\", t_seq);\n", + "printf(\" Parallel: %.4f s (%.1fx speedup)\\n\", t_par, t_seq / t_par);\n", + "printf(\"=============================================\\n\");\n", + "\n", + "free(bench_A);\n", + "free(bench_B);" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "---\n", + "## 8. Why Double Buffering Has Zero Race Conditions\n", + "\n", + "Let's prove to ourselves it's safe by thinking through what each thread touches:\n", + "\n", + "```\n", + "Generation N (frozen - nobody writes here):\n", + "┌─────────────────────────────────────┐\n", + "│ current[][] READ ONLY │\n", + "│ Thread 0 reads rows 0-1 │\n", + "│ Thread 1 reads rows 2-3 │\n", + "│ Thread 2 reads rows 4-5 │\n", + "│ Thread 3 reads rows 6-7 │\n", + "│ (multiple readers = always safe) │\n", + "└─────────────────────────────────────┘\n", + "\n", + "Generation N+1 (being built - nobody reads here yet):\n", + "┌─────────────────────────────────────┐\n", + "│ next[][] WRITE ONLY │\n", + "│ Thread 0 writes rows 0-1 │\n", + "│ Thread 1 writes rows 2-3 │\n", + "│ Thread 2 writes rows 4-5 │\n", + "│ Thread 3 writes rows 6-7 │\n", + "│ (each thread owns different rows) │\n", + "└─────────────────────────────────────┘\n", + "\n", + "After all threads finish:\n", + " swap(current, next) ← just swapping two pointers, O(1)\n", + "```\n", + "\n", + "No atomics needed. No critical sections. No locks. \n", + "The double buffer design **eliminates** the race condition structurally." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "C++23 + OpenMP", + "language": "cpp", + "name": "xcpp23-omp" + }, + "language_info": { + "codemirror_mode": "text/x-c++src", + "file_extension": ".cpp", + "mimetype": "text/x-c++src", + "name": "C++", + "nbconvert_exporter": "", + "pygments_lexer": "", + "version": "cxx23" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} From 62f8ab87590ca5bd78d677d0841b8fc845dfea27 Mon Sep 17 00:00:00 2001 From: Hristiyan Shterev Date: Sun, 26 Apr 2026 16:26:52 +0300 Subject: [PATCH 4/4] rename folder for images --- openmp/01_openmp-demo.ipynb | 2 +- openmp/{assets => images}/fork_join.png | Bin 2 files changed, 1 insertion(+), 1 deletion(-) rename openmp/{assets => images}/fork_join.png (100%) diff --git a/openmp/01_openmp-demo.ipynb b/openmp/01_openmp-demo.ipynb index 4921645..031dc69 100644 --- a/openmp/01_openmp-demo.ipynb +++ b/openmp/01_openmp-demo.ipynb @@ -24,7 +24,7 @@ "\n", "Here is an image of how the Fork-Join model works\n", "\n", - "![fork-join](assets/fork_join.png)" + "![fork-join](images/fork_join.png)" ] }, { diff --git a/openmp/assets/fork_join.png b/openmp/images/fork_join.png similarity index 100% rename from openmp/assets/fork_join.png rename to openmp/images/fork_join.png