{"id":21766361,"url":"https://github.com/drilonaliu/parallel-sierpinski-triangle","last_synced_at":"2026-05-07T08:31:56.065Z","repository":{"id":251388403,"uuid":"837245340","full_name":"drilonaliu/Parallel-Sierpinski-Triangle","owner":"drilonaliu","description":"GPU-accelerated Sierpinski Triangle generation with CUDA and OpenGL interoperability.","archived":false,"fork":false,"pushed_at":"2024-08-08T13:19:34.000Z","size":902,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-01-26T02:18:21.505Z","etag":null,"topics":["cuda","fractals","gpu","parallel-programming","sierpinski-triangle"],"latest_commit_sha":null,"homepage":"","language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/drilonaliu.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-08-02T14:18:42.000Z","updated_at":"2024-10-10T22:10:32.000Z","dependencies_parsed_at":"2024-08-08T14:28:06.132Z","dependency_job_id":null,"html_url":"https://github.com/drilonaliu/Parallel-Sierpinski-Triangle","commit_stats":null,"previous_names":["drilonaliu/parallel-sierpinski-triangle"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/drilonaliu%2FParallel-Sierpinski-Triangle","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/drilonaliu%2FParallel-Sierpinski-Triangle/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/drilonaliu%2FParallel-Sierpinski-Triangle/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/drilonaliu%2FParallel-Sierpinski-Triangle/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/drilonaliu","download_url":"https://codeload.github.com/drilonaliu/Parallel-Sierpinski-Triangle/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244741920,"owners_count":20502376,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cuda","fractals","gpu","parallel-programming","sierpinski-triangle"],"created_at":"2024-11-26T13:16:57.220Z","updated_at":"2026-05-07T08:31:51.018Z","avatar_url":"https://github.com/drilonaliu.png","language":"C","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Parallel-Sierpinski-Triangle\n GPU-accelerated Sierpinski Triangle generation with CUDA and OpenGL interoperability.\n\n\n\n\n# Sierpinski Triangle\n\n\nThe Sierpinski Triangle is a famous fractal named after the mathematician Waclaw Sierpinski. The fractal can be constructed by starting with an equilateral triangle and then recursively dividing each triangle as follows:\n\n1. On each side of the triangle, find the midpoint and connect these points, forming four new triangles.\n\n2. Repeat the process in each triangle except the middle triangle.\n\n\n\u003cdiv\u003e\n  \u003cp\u003e\n    \u003cimg src=\"images/triangle_1.png\" width=\"200\" height=\"200\" alt=\"Tree 1\"\u003e\n    \u003cimg src=\"images/triangle_2.png\" width=\"200\" height=\"200\" alt=\"Tree 2\"\u003e\n    \u003cimg src=\"images/triangle_3.png\" width=\"200\" height=\"200\" alt=\"Tree 3\"\u003e\n    \u003cimg src=\"images/triangle_4.png\" width=\"200\" height=\"200\" alt=\"Tree 4\"\u003e\n  \u003c/p\u003e\n\u003c/div\u003e\n\n## Construction of the inner triangle \n\nLet the triangle $\\triangle ABC$ be given and the points $D, E, F$\nthat bisect the edges of the triangle. The coordinates of the points $D, E, F$\nare:\n\n\u003cp\u003e\n   \u003cimg src=\"images/triangle_5.png\"\u003e\n\u003c/p\u003e\n\n\n$$D = \\left( \\frac{a_1 + b_1}{2}, \\frac{a_2 + b_2}{2} \\right)$$\n\n$$E = \\left( \\frac{b_1 + c_1}{2}, \\frac{b_2 + c_2}{2} \\right)$$\n\n$$F = \\left( \\frac{a_1 + c_1}{2}, \\frac{a_2 + c_2}{2} \\right)$$\n\n\nTriangles $\\triangle ADF$, $\\triangle DBE$, $\\triangle FEC$, will enter the next iteration for division.\n\n## Number of vertices.\n\nIn the visualization of the fractal, the initial triangle will be rendered in green, and the inner triangles will be rendered in black. It is necessary to find the number of points of the inner (black) triangles.\nLet $T(n)$ denote the number of triangles added from iteration $n-1$ to $n$. Thus, $T(1) = 1$,\n$T(2) = 3$, $T(3) = 9$. Each triangle, after division, gives 3 new triangles in which the black triangles will be constructed.\n\n$$T(n) = 3T(n-1) = 3^2 T(n-2) = \\cdots = 3^{n-1} T(1) = 3^{n-1}$$\n\n\\\nThe number of all black triangles in iteration $n$ is the sum:\n\n$$T(1) + T(2) + \\cdots + T(n) = 3^0 + 3^1 + \\cdots + 3^n = \\sum_{i=0}^{n-1} 3^i = \\frac{3^n - 1}{2}$$\n\nEach triangle has three points, and if we also calculate the three points of the initiatl triangle we have:\n\n$$f(n) = 3 + \\frac{3 \\cdot (3^n - 1)}{2} = 3 + \\frac{3^{n+1} - 3}{2} = \\frac{3}{2} (3^n + 1)$$\n\nThe sequence $f(n)$ gives the number of total points of the inner triangles(black triangles) and of the initial triangle in iteration $n$. First we have rendered the initial triangle in green, \n(line 6), and then the black triangles (line 10).\n\n```\n    void renderTriangleFromBuffer() {\n     glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, nullptr);\n     glEnableVertexAttribArray(0);\n     //Render first green triangle\n     glColor3f(0.29f, 0.44f, 0.55f);\n     glDrawArrays(GL_TRIANGLES, 0, 3);\n     //Render blackTriangles\n     int numberVerticesBlackTriangles = (pow(3, iterations + 1) - 3) / 2;\n     glColor3f(0.0f, 0.0f, 0.0f);\n     glDrawArrays(GL_TRIANGLES, 3, numberVerticesBlackTriangles);\n     glutSwapBuffers();\n    } \n```\n\n## Paralelizimi\n\nParallelization occurs hierarchically, where a certain number of threads are active in each iteration. Each active thread takes the  $i$-th triangle of the figure and finds the three points for constructing the inner triangle. \nThe three triangles that will be divided in the next iteration are defined by each thread and inserted into the array of all triangles. The size of this array will be the number of points of the triangles founded previously, divided by 3, because each triangle has three points.\n\n$$\\frac{f(n)}{3} = \\frac{1}{2} (3^n + 1)$$\n\n\\\n\nIn each iteration, there will be $3^n$ active threads. As the iterations increase, the number of active threads will increase exponentially:\n\n- Iteration 0: Only the thread with id 0 will be active. This thread initializes the initial triangle.\n\n- Iteration 1: Only the thread with id 1 is active. This thread constructs the first inner triangle. The three triangles are defined and inserted into the array of triangles at positions 2, 3, and 4, which will be divided in the next iteration.\n\n- Iteration 2: Threads 2, 3, and 4 are active. Each thread takes the corresponding triangle based on their index. Each thread constructs the inner triangle, defines 3 triangles, which are inserted into the array of all triangles.\n\n- Iteration $n$: Threads $$\\frac{3^n + 1}{2}$$ to $$\\frac{3^{n+1} - 1}{2}$$ will be active. These threads take the triangles from the previous iteration and construct the inner triangles in them.]\n\n\u003cp\u003e\n   \u003cimg src=\"images/triangle_6.png\"\u003e\n\u003c/p\u003e\n\n\n\u003cp\u003e\n   \u003cimg src=\"images/triangle_7.png\"\u003e\n\u003c/p\u003e\n\nParallelization and data addition are modeled in a ternary graph. Each node in the ternary graph represents a thread that constructs the inner triangles, while the branches represent the division of three triangles in future iterations. \nEach thread $i$ takes triangle $i$ from the array of triangles, divides it, and adds three new triangles in the child positions $3i-1$, $3i$, and $3i+1$. Each level of the graph represents the threads that are working in parallel.\n\n\\\nThe leftmost index at the level n of the graph is:\n\n$$\\text{rightMost}(n) = \\sum_{i=0}^n 3^i = \\frac{3^{n+1} - 1}{2}$$\n\nThe rightmost index at the level n of the graph is:\n\n$$\\text{leftMost}(n) = \\text{rightMost}(n-1) + 1 = \\frac{3^n - 1}{2} + 1 = \\frac{3^n + 1}{2}$$\n\nNumber of of threads working in parallel at level $n$ is:\n\n$$\\text{leftMost}(n+1) - \\text{leftMost}(n) = \\frac{3^{n+1} + 1}{2} - \\frac{3^n + 1}{2} = 3^n$$\n\n## Kerneli\n\nThe following kernel generates the fractal through parallel computation. Initially, the thread with index 0 handles the initializing triangle. For each iteration, threads between the start_at and end_at intervals are active and calculate the midpoints of the triangle's segments. \nThree new triangles are defined and stored in the array of triangles to be divided. The midpoints of the edges are added to the points array for visualizing the triangle. Synchronization ensures that all threads complete their tasks before proceeding to the next iteration.\n\n```\n    __global__ void kernel(float* points, Triangle* triangles, int start_iteration, int max_iteration, int threadShiftIndex) {\n        int idx = threadIdx.x + blockDim.x * blockIdx.x;\n        idx += threadShiftIndex;\n        Point A,B,C,A1,B1,C1;\n        Triangle triangle,t_1,t_2t_3;\n        auto g = cg::this_grid();\n        if (idx == 0) {\n        triangles[1] = triangle;\n        }\n        \n        for (int iteration = start_iteration; iteration \u003c= max_iteration; iteration++) {\n            int start_at = (round((pow(3, iteration) + 1))) / 2;\n            int end_at = (round((pow(3, iteration+1) - 1))) / 2;\n            if (idx \u003e= start_at \u0026\u0026 idx \u003c= end_at) {\n                triangle = triangles[idx];\n                A = triangle.A;\n                B = triangle.B;\n                C = triangle.C;\n                //DivideTriangle \n                A1.x = (A.x + B.x) / 2.0f;\n                A1.y = (A.y + B.y) / 2.0f;\n                B1.x = (B.x + C.x) / 2.0f;\n                B1.y = (B.y + C.y) / 2.0f;\n                C1.x = (C.x + A.x) / 2.0f;\n                C1.y = (C.y + A.y) / 2.0f;\n                //Make three new Triangles\n                t_1.A = A;\n                t_1.B = A1;\n                t_1.C = C1;\n                \n                t_2.A = A1;\n                t_2.B = B;\n                t_2.C = B1;\n                \n                t_3.A = C1;\n                t_3.B = B1;\n                t_3.C = C;\n                //Insert three new triangles to triangles array\n                triangles[3 * idx - 1] = t_1;\n                triangles[3 * idx] = t_2;\n                triangles[3 * idx + 1] = t_3;\n                \n                //Add three points \n                int offset = 2 * 3 * (idx);\n                points[offset] = A1.x;\n                points[offset + 1] = A1.y;\n                points[offset + 2] = B1.x;\n                points[offset + 3] = B1.y;\n                points[offset + 4] = C1.x;\n                points[offset + 5] = C1.y;\n            }\n            g.sync();\n        }\n    }\n\n```\n\n## Comparisions\n\nThe table below compares the execution time in microseconds of the fractal between the sequential version and the parallel version. The CUDA implementation was done using cooperative groups, with 25600 threads per kernel call. \n\n\n| Iteration | C++    | CUDA    |\n|-----------|--------|---------|\n| 0         | 1      | 32      |\n| 1         | 2      | 23      |\n| 2         | 3      | 23      |\n| 3         | 3      | 31      |\n| 4         | 5      | 20      |\n| 5         | 19     | 23      |\n| 6         | 47     | 19      |\n| 7         | 133    | 23      |\n| 8         | 257    | 22      |\n| 9         | 861    | 37      |\n| 10        | 2633   | 34      |\n| 11        | 8374   | 82      |\n| 12        | 30457  | 243     |\n| 13        | 74373  | 653     |\n| 14        | 271835 | 1886    |\n| 15        | 1060085| 6367    |\n| 16        | 5812052| 2782181 |\n| 17        | 14557977| 6679207 |\n\n*Table: Performance Comparison.*\n\n\n\u003cp\u003e\n   \u003cimg src=\"images/triangle_8.png\"\u003e\n\u003c/p\u003e\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdrilonaliu%2Fparallel-sierpinski-triangle","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdrilonaliu%2Fparallel-sierpinski-triangle","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdrilonaliu%2Fparallel-sierpinski-triangle/lists"}