{"id":17396819,"url":"https://github.com/faern/forkjoin","last_synced_at":"2025-07-05T09:36:05.782Z","repository":{"id":30337070,"uuid":"33889435","full_name":"faern/forkjoin","owner":"faern","description":"A work stealing fork-join parallelism library for Rust","archived":false,"fork":false,"pushed_at":"2018-07-02T20:58:31.000Z","size":109,"stargazers_count":57,"open_issues_count":0,"forks_count":2,"subscribers_count":5,"default_branch":"master","last_synced_at":"2025-06-02T18:11:01.799Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/faern.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE-APACHE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2015-04-13T19:35:15.000Z","updated_at":"2025-02-07T10:36:59.000Z","dependencies_parsed_at":"2022-07-31T08:48:03.787Z","dependency_job_id":null,"html_url":"https://github.com/faern/forkjoin","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/faern/forkjoin","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/faern%2Fforkjoin","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/faern%2Fforkjoin/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/faern%2Fforkjoin/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/faern%2Fforkjoin/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/faern","download_url":"https://codeload.github.com/faern/forkjoin/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/faern%2Fforkjoin/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":263719665,"owners_count":23501087,"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":[],"created_at":"2024-10-16T13:13:11.461Z","updated_at":"2025-07-05T09:36:05.763Z","avatar_url":"https://github.com/faern.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ForkJoin\nA work stealing fork-join parallelism library.\n\n[![Build Status](https://api.travis-ci.org/faern/forkjoin.svg?branch=master)](https://travis-ci.org/faern/forkjoin)\n[![deprecated](http://badges.github.io/stability-badges/dist/deprecated.svg)](http://github.com/badges/stability-badges)\n\nInspired by the blog post [Data Parallelism in Rust](http://smallcultfollowing.com/babysteps/blog/2013/06/11/data-parallelism-in-rust/)\nand implemented as part of the master's thesis [Parallelization in Rust with fork-join and friends](http://publications.lib.chalmers.se/records/fulltext/219016/219016.pdf). Repository hosted at [github.com/faern/forkjoin](https://github.com/faern/forkjoin)\n\nLibrary documentation hosted [here](https://faern.github.io/rust-docs/forkjoin/forkjoin/)\n\nThis library has been developed to accommodate the needs of three types of\nalgorithms that all fit very well for fork-join parallelism.\n\n# Reduce style\n\nReduce style is where the algorithm receive an argument, recursively compute a value\nfrom this argument and return one answer. Examples of this style include recursively\nfinding the n:th Fibonacci number and summing of tree structures.\nCharacteristics of this style is that the algorithm does not need to mutate its\nargument and the resulting value is only available after every subtask has been\nfully computed.\n\nIn reduce style algorithms the return values of each subtask is passed to a special\njoin function that is executed when all subtasks have completed.\nTo this join function an extra argument can be sent directly from the task if the algorithm\nhas `ReduceStyle::Arg`. This can be seen in the examples here.\n\n## Example of reduce style (`ReduceStyle::NoArg`)\n\n```rust\nuse forkjoin::{TaskResult,ForkPool,AlgoStyle,ReduceStyle,Algorithm};\n\nfn fib_30_with_4_threads() {\n    let forkpool = ForkPool::with_threads(4);\n    let fibpool = forkpool.init_algorithm(Algorithm {\n        fun: fib_task,\n        style: AlgoStyle::Reduce(ReduceStyle::NoArg(fib_join)),\n    });\n\n    let job = fibpool.schedule(30);\n    let result: usize = job.recv().unwrap();\n    assert_eq!(1346269, result);\n}\n\nfn fib_task(n: usize) -\u003e TaskResult\u003cusize, usize\u003e {\n    if n \u003c 2 {\n        TaskResult::Done(1)\n    } else {\n        TaskResult::Fork(vec![n-1,n-2], None)\n    }\n}\n\nfn fib_join(values: \u0026[usize]) -\u003e usize {\n    values.iter().fold(0, |acc, \u0026v| acc + v)\n}\n```\n\n## Example of reduce style (`ReduceStyle::Arg`)\n\n```rust\nuse forkjoin::{TaskResult,ForkPool,AlgoStyle,ReduceStyle,Algorithm};\n\nstruct Tree {\n    value: usize,\n    children: Vec\u003cTree\u003e,\n}\n\nfn sum_tree(t: \u0026Tree) -\u003e usize {\n    let forkpool = ForkPool::new();\n    let sumpool = forkpool.init_algorithm(Algorithm {\n        fun: sum_tree_task,\n        style: AlgoStyle::Reduce(ReduceStyle::Arg(sum_tree_join)),\n    });\n    let job = sumpool.schedule(t);\n    job.recv().unwrap()\n}\n\nfn sum_tree_task(t: \u0026Tree) -\u003e TaskResult\u003c\u0026Tree, usize\u003e {\n    if t.children.is_empty() {\n        TaskResult::Done(t.value)\n    } else {\n        let mut fork_args: Vec\u003c\u0026Tree\u003e = vec![];\n        for c in t.children.iter() {\n            fork_args.push(c);\n        }\n        TaskResult::Fork(fork_args, Some(t.value)) // Pass current nodes value to join\n    }\n}\n\nfn sum_tree_seq(t: \u0026Tree) -\u003e usize {\n    t.value + t.children.iter().fold(0, |acc, t2| acc + sum_tree_seq(t2))\n}\n\nfn sum_tree_join(value: \u0026usize, values: \u0026[usize]) -\u003e usize {\n    *value + values.iter().fold(0, |acc, \u0026v| acc + v)\n}\n```\n\n# Search style\n\nSearch style return results continuously and can sometimes start without any\nargument, or start with some initial state. The algorithm produce one or multiple\noutput values during the execution, possibly aborting anywhere in the middle.\nAlgorithms where leafs in the problem tree represent a complete solution to the\nproblem (unless the leaf represent a dead end that is not a solution and does\nnot spawn any subtasks), for example nqueens and sudoku solvers, have this style.\nCharacteristics of the search style is that they can produce multiple results\nand can abort before all tasks in the tree have been computed.\n\n## Example of search style\n\n```rust\nuse forkjoin::{ForkPool,TaskResult,AlgoStyle,Algorithm};\n\ntype Queen = usize;\ntype Board = Vec\u003cQueen\u003e;\ntype Solutions = Vec\u003cBoard\u003e;\n\nfn search_nqueens() {\n    let n: usize = 8;\n    let empty = vec![];\n\n    let forkpool = ForkPool::with_threads(4);\n    let queenpool = forkpool.init_algorithm(Algorithm {\n        fun: nqueens_task,\n        style: AlgoStyle::Search,\n    });\n\n    let job = queenpool.schedule((empty, n));\n\n    let mut solutions: Vec\u003cBoard\u003e = vec![];\n    loop {\n        match job.recv() {\n            Err(..) =\u003e break, // Job has completed\n            Ok(board) =\u003e solutions.push(board),\n        };\n    }\n    let num_solutions = solutions.len();\n    println!(\"Found {} solutions to nqueens({}x{})\", num_solutions, n, n);\n}\n\nfn nqueens_task((q, n): (Board, usize)) -\u003e TaskResult\u003c(Board,usize), Board\u003e {\n    if q.len() == n {\n        TaskResult::Done(q)\n    } else {\n        let mut fork_args: Vec\u003c(Board, usize)\u003e = vec![];\n        for i in 0..n {\n            let mut q2 = q.clone();\n            q2.push(i);\n\n            if ok(\u0026q2[..]) {\n                fork_args.push((q2, n));\n            }\n        }\n        TaskResult::Fork(fork_args, None)\n    }\n}\n\nfn ok(q: \u0026[usize]) -\u003e bool {\n    for (x1, \u0026y1) in q.iter().enumerate() {\n        for (x2, \u0026y2) in q.iter().enumerate() {\n            if x2 \u003e x1 {\n                let xd = x2-x1;\n                if y1 == y2 || y1 == y2 + xd || (y2 \u003e= xd \u0026\u0026 y1 == y2 - xd) {\n                    return false;\n                }\n            }\n        }\n    }\n    true\n}\n```\n\n# In-place mutation style\n\nNOTE: This style works in the current lib version, but it requires very ugly\nunsafe code!\n\nIn-place mutation style receive a mutable argument, recursively modifies this value\nand the result is the argument itself. Sorting algorithms that sort their input\narrays are cases of this style. Characteristics of this style is that they mutate\ntheir input argument instead of producing any output.\n\nExamples of this will come when they can be nicely implemented.\n\n# Tasks\n\nThe small units that are executed and can choose to fork or to return a value is the\n`TaskFun`. A TaskFun can NEVER block, because that would block the kernel thread\nit's being executed on. Instead it should decide if it's done calculating or need\nto fork. This decision is taken in the return value to indicate to the user\nthat a TaskFun need to return before anything can happen.\n\nA TaskFun return a `TaskResult`. It can be `TaskResult::Done(value)` if it's done\ncalculating. It can be `TaskResult::Fork(args)` if it needs to fork.\n\n# TODO\n\n- [ ] Make mutation style algorithms work without giving join function\n- [ ] Implement a sorting algorithm. Quicksort?\n- [ ] Remove need to return None on fork with NoArg\n- [ ] Make it possible to use algorithms with different Arg \u0026 Ret on same pool.\n- [ ] Make ForkJoin work in stable Rust.\n- [ ] Remove mutex around channel in search style.\n\n# License\n\nLicensed under either of\n * Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)\n * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)\nat your option.\n\n## Contribution\n\nUnless you explicitly state otherwise, any contribution intentionally submitted\nfor inclusion in the work by you shall be dual licensed as above, without any\nadditional terms or conditions.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffaern%2Fforkjoin","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffaern%2Fforkjoin","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffaern%2Fforkjoin/lists"}