{"id":25705426,"url":"https://github.com/mmore500/conduit","last_synced_at":"2025-04-30T10:13:28.058Z","repository":{"id":40721108,"uuid":"278400174","full_name":"mmore500/conduit","owner":"mmore500","description":"C++ library that wraps intra-thread, inter-thread, and inter-process communication in a uniform, modular, object-oriented interface, with a focus on asynchronous high-performance computing applications.","archived":false,"fork":false,"pushed_at":"2024-10-17T16:29:36.000Z","size":1745994,"stargazers_count":13,"open_issues_count":56,"forks_count":4,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-04-30T10:13:10.478Z","etag":null,"topics":["high-performance-computing","multiprocess","multithread"],"latest_commit_sha":null,"homepage":"https://conduit.fyi","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mmore500.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.rst","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":"CITATION.cff","codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":"AUTHORS.rst","dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2020-07-09T15:20:33.000Z","updated_at":"2024-10-17T16:29:40.000Z","dependencies_parsed_at":"2023-11-19T00:22:31.787Z","dependency_job_id":"24015776-8da4-4746-8dca-c4ef208b9229","html_url":"https://github.com/mmore500/conduit","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mmore500%2Fconduit","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mmore500%2Fconduit/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mmore500%2Fconduit/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mmore500%2Fconduit/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mmore500","download_url":"https://codeload.github.com/mmore500/conduit/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":251683356,"owners_count":21626953,"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":["high-performance-computing","multiprocess","multithread"],"created_at":"2025-02-25T06:47:40.732Z","updated_at":"2025-04-30T10:13:28.032Z","avatar_url":"https://github.com/mmore500.png","language":"C++","readme":"![conduit logo](docs/assets/logo.png)\n\n[![version](https://img.shields.io/endpoint?url=https%3A%2F%2Fmmore500.com%2Fconduit%2Fversion-badge.json)](https://github.com/mmore500/conduit/releases)\n[![Codacy Badge](https://api.codacy.com/project/badge/Grade/eeb84ac3b8a3419f9714819f9191d7bf)](https://app.codacy.com/manual/mmore500/conduit?utm_source=github.com\u0026utm_medium=referral\u0026utm_content=mmore500/conduit\u0026utm_campaign=Badge_Grade_Dashboard)\n[![continuous integration](https://github.com/mmore500/conduit/workflows/CI/badge.svg)](https://github.com/mmore500/conduit/actions?query=workflow%3ACI)\n[![documentation status](https://readthedocs.org/projects/uit/badge/?version=latest)](https://uit.readthedocs.io/en/latest/?badge=latest)\n[![documentation coverage](https://img.shields.io/endpoint?url=https%3A%2F%2Fmmore500.com%2Fconduit%2Fdocumentation-coverage-badge.json)](https://uit.readthedocs.io/en/latest/)\n[![code coverage status](https://codecov.io/gh/mmore500/conduit/branch/master/graph/badge.svg)](https://codecov.io/gh/mmore500/conduit)\n[![DockerHub link](https://img.shields.io/badge/DockerHub-Hosted-blue)](https://hub.docker.com/r/mmore500/conduit)\n[![Lines of Code](https://tokei.rs/b1/github/mmore500/conduit?category=code)](https://github.com/XAMPPRocky/tokei)\n[![Comments](https://tokei.rs/b1/github/mmore500/conduit?category=comments)](https://github.com/XAMPPRocky/tokei)\n[![dotos](https://img.shields.io/endpoint?url=https%3A%2F%2Fmmore500.com%2Fconduit%2Fdoto-badge.json)](https://github.com/mmore500/conduit/search?q=todo+OR+fixme\u0026type=)\n[![GitHub stars](https://img.shields.io/github/stars/mmore500/conduit.svg?style=flat-square\u0026logo=github\u0026label=Stars\u0026logoColor=white)](https://github.com/mmore500/conduit)\n[![DOI](https://zenodo.org/badge/278400174.svg)](https://zenodo.org/badge/latestdoi/278400174)\n\n  * Free software: MIT license\n  * Documentation: [https://conduit.fyi](https://conduit.fyi)\n  * header-only, namespace-encapsulated software\n\nC++ library that wraps intra-thread, inter-thread, and inter-process communication in a uniform, modular, object-oriented interface, with a focus on asynchronous high-performance computing applications.\n\n## Design\n\nThe driving objective behind this library is to provide a performant, uniform, convenient interface for communication between simulation elements, whether those simulation elements reside on the same thread, different threads, or entirely different processes.\n\n![Inlet and Outlet holding a shared Duct with intra-thread implementation active](docs/assets/default.png)\n\nThe conduit model consists of:\n  * `Inlet`'s, which accepts inputs `T` through a non-blocking call,\n  * `Duct`'s, which handle transmission logistics,\n  * `Outlet`'s, which provides the latest `T` or next `T` through a non-blocking call.\n\n`Inlet` and `Outlet` objects both hold a [`std::shared_ptr`](https://en.cppreference.com/w/cpp/memory/shared_ptr) to a `Duct` object.\nThe `Duct` object is implemented as a [`std::variant`](https://en.cppreference.com/w/cpp/utility/variant) of three implementation types:\n  * `IntraDuct` type for intra-thread communication (default),\n  * `ThreadDuct` type one for inter-thread communication, and\n  * `ProcDuct` type for inter-process communication.\n\nThe `Duct`'s active implementation can be switched at run-time by calling `EmplaceDuct\u003cType\u003e` from either the `Inlet` or the `Outlet`.\nAll calls to a `Duct` at run-time are forwarded to its active implementation.\nFor example, emplacing a `ThreadDuct` might yield the following.\n\n![Inlet and Outlet holding a shared Duct with inter-thread implementation active](docs/assets/emplace.png)\n\nCalling `SplitDuct\u003cType\u003e` from either the `Inlet` or the `Outlet` will drop the callee's `std::shared_ptr` to the existing `Duct` in favor of a `std::shared_ptr` to a newly-constructed `Duct` with the specified implementation type active.\n(This operation is useful for inter-process communication, where coupled `Inlet` and `Outlet`'s do not reside in a common memory space).\nFor example, calling `SplitDuct\u003cProcDuct\u003e` on an `Inlet` might yield the following.\n\n![Inlet and Outlet holding separate Ducts](docs/assets/split.png)\n\n`Inlet` and `Outlet` are entirely interchangeable no matter the current `Duct` implementation is active.\nOnce a `Duct`'s are configured, `Inlet` and `Outlet` objects can be without any concern for underlying implementation.\nThis abstraction ensures a uniform API whether underlying communication is intra-thread, inter-thread, or inter-process.\nFurthermore, a `Duct` implementation can be re-configured or even re-directed at run time without any interaction with an `Inlet` or `Outlet` its tied to.\n\n## Low-Level Interface: `uit`\n\nConduit provides three helper construction interfaces:\n  * `Conduit`, which constructs an `Inlet` and `Outlet` with a shared `Duct`,\n  * `Sink`, which constructs an `Inlet` with sole holdership of a `Duct`,\n  * `Source`, which constructs an `Outlet` with sole holdership of a `Duct`.\n\n![comparison of Conduit, Sink, and Source](docs/assets/conduit-sink-source.png)\n\nAfter constructing a `Conduit`, `Sink`, or `Source`, users can use [structured binding](https://en.cppreference.com/w/cpp/language/structured_binding) or an accessor method to retrieve `Inlet`'s or `Outlet`'s.\n\nHere's an example of how this works in code.\n\n`conduit/low.cpp`:\n```cpp\n#include \u003ciostream\u003e\n#include \u003cratio\u003e\n#include \u003cutility\u003e\n\n#include \"uit/fixtures/Conduit.hpp\"\n#include \"uitsl/parallel/ThreadTeam.hpp\"\n#include \"uit/setup/ImplSpec.hpp\"\n\n// use int as message type\nusing Spec = uit::ImplSpec\u003cint\u003e;\n\nint main() {\n\n  // construct conduit with thread-safe implementation active\n  uit::Conduit\u003cSpec\u003e conduit{\n    std::in_place_type_t\u003cSpec::ThreadDuct\u003e{}\n  };\n\n  auto\u0026 [inlet, outlet] = conduit;\n\n  uitsl::ThreadTeam team;\n\n  // start a producer thread\n  team.Add( [\u0026inlet](){\n    for (int i = 0; i \u003c std::mega::num; ++i) inlet.TryPut(i);\n  } );\n\n  // start a consumer thread\n  team.Add( [\u0026outlet](){\n    int prev{ outlet.JumpGet() };\n    size_t update_counter{};\n    for (size_t i = 0; i \u003c std::mega::num; ++i) {\n      update_counter += std::exchange(prev, outlet.JumpGet()) == prev;\n    }\n    std::cout \u003c\u003c update_counter \u003c\u003c \" updates detected\" \u003c\u003c '\\n';\n  } );\n\n  // wait for threads to complete\n  team.Join();\n\n  return 0;\n\n}\n```\n\nNavigate to the `conduit` directory.\nThen, to compile and run,\n```sh\nmpicxx --std=c++17 -O3 -DNDEBUG -Iinclude/ low.cpp -lpthread\n./a.out\n```\n\n:bangbang:\nYou'll need an MPI compiler and runtime library for the code examples here.\nIf you don't have those on hand, grab a copy of our pre-built Docker container and hop inside there.\n```sh\nsudo docker run -it mmore500/conduit:latest\n```\n\nIf you're on a cluster without root access, you can try using Singularity.\n```sh\nsingularity shell docker://mmore500/conduit\n```\n\n## High-Level Interface: `uitnet`\n\nThe conduit library provides a `Mesh` interface to streamline construction of complex, potentially irregular, conduit networks.\nThese networks are conceived as a directed graph, with edges representing conduits and nodes representing an actor that holds a set of `Inlet`'s and/or `Outlet`'s.\n\n`Mesh`es are constructed through two independently-specified components,\n 1. topology: how should nodes be connected?\n 2. delegation: how should nodes be assigned to threads and processes?\n\nHere's an example topology, with each node connected to a successor in a one-dimensional ring.\n\n![unidirectional ring graph](docs/assets/ring-topology.png)\n\nWe might choose to delegate contiguous subsets of nodes to threads and processes.\nFor example, to distribute 24 nodes over four double-threaded processes, we might perform the following assignment:\n  * node 0 :arrow_right: thread 0, process 0\n  * node 1 :arrow_right: thread 0, process 0\n  * node 2 :arrow_right: thread 0, process 0\n  * node 3 :arrow_right: thread 1, process 0\n  * node 4 :arrow_right: thread 1, process 0\n  * node 5 :arrow_right: thread 1, process 0\n  * node 6 :arrow_right: thread 0, process 1\n  * node 7 :arrow_right: thread 0, process 1\n  * node 8 :arrow_right: thread 0, process 1\n  * node 9 :arrow_right: thread 1, process 1\n  * etc.\n\n![graph nodes assigned to threads and processes](docs/assets/ring-mesh.png)\n\nArbitrary topologies can be specified, with pre-built factories available to construct the most common configurations.\nFor example, a two-dimensional lattice grid,\n\n![grid lattice graph](docs/assets/lattice-topology.png)\n\nWe can use a specialized delegation function to distribute nodes.\n\n![graph nodes assigned to threads and processes](docs/assets/lattice-mesh.png)\n\nWhen a `Mesh` is constructed from a topology and a delegation function, edges between nodes are instantiated in terms of `Inlet`'s and `Outlet`'s.\nDuring `Mesh` construction, thread-safe `Duct` implementations are emplaced on conduits that span between nodes assigned to different threads and inter-process `Duct` implementations are emplaced on conduits that span between nodes assigned to different proceses.\n\nOnce the `Mesh` is constructed, `GetSubmesh()` returns the network components that are assigned to a particular thread or process.\n\n![nodes within a particular thread on a particular process](docs/assets/submesh.png)\n\nThe `GetSubmesh()` call returns an `emp::vector` of `MeshNode`'s.\nEach `MeshNode` consists of an \"input\" vector of `Outlet`'s and an \"output\" vector of `Inlet`'s.\n\n![mesh node with constituent input outlets and output inlets](docs/assets/mesh-node.png)\n\nHere's what the entire process looks like in code.\n\n`conduit/high.cpp`:\n```cpp\n#include \u003ciostream\u003e\n#include \u003ctuple\u003e\n#include \u003csstream\u003e\n\n#include \"uitsl/mpi/MpiGuard.hpp\"\n#include \"uitsl/parallel/ThreadTeam.hpp\"\n\n#include \"uit/setup/ImplSpec.hpp\"\n\n#include \"netuit/arrange/RingTopologyFactory.hpp\"\n#include \"netuit/mesh/Mesh.hpp\"\n\nconst size_t num_nodes = 5; // five nodes in our topology\nconst size_t num_procs = 2; // two MPI processes\nconst size_t num_threads = 2; // two threads per process\n\n// message to pass through the conduits\n// contains information about node, thread, and process of sender\nstruct Message {\n\n  size_t node_id{};\n  uitsl::thread_id_t thread_id{};\n  uitsl::proc_id_t proc_id{};\n\n  bool operator==(const Message\u0026 other) const {\n    return std::tuple{\n      node_id,\n      thread_id,\n      proc_id\n    } == std::tuple{\n      other.node_id,\n      other.thread_id,\n      other.proc_id\n    };\n  }\n\n  std::string ToString() const {\n\n    std::stringstream ss;\n    ss \u003c\u003c \"p\" \u003c\u003c uitsl::get_proc_id();\n    ss \u003c\u003c \" / \";\n    ss \u003c\u003c \"t\" \u003c\u003c thread_id;\n    ss \u003c\u003c \" / \";\n    ss \u003c\u003c \"n\" \u003c\u003c node_id;\n\n    return ss.str();\n\n  }\n\n};\n\n// transmit Message through conduits with default duct implementations\nusing Spec = uit::ImplSpec\u003cMessage\u003e;\n\n// MPI initialization \u0026 finalization boilerplate\nconst uitsl::MpiGuard guard;\n\n// first task each thread will execute\nvoid send_task(\n  const uitsl::thread_id_t thread_id,\n  netuit::Mesh\u003cSpec\u003e::submesh_t\u0026 my_nodes\n) {\n\n  // goal: for each output in each node, send a message with info about the\n  // sending node, thread, and process so we can check how things are connected\n  for (size_t node_id = 0; node_id \u003c my_nodes.size(); ++node_id) {\n\n    const Message message{ node_id, thread_id, uitsl::get_proc_id() };\n\n    auto\u0026 node = my_nodes[node_id];\n\n    // send message\n    for (auto\u0026 output : node.GetOutputs()) output.Put( message );\n\n  }\n\n}\n\n// second task each thread will execute\nvoid receive_task(\n  const uitsl::thread_id_t thread_id,\n  netuit::Mesh\u003cSpec\u003e::submesh_t\u0026 my_nodes\n) {\n\n  // goal: for each input to each node, print out info about who received what\n  // message\n  for (size_t node_id = 0; node_id \u003c my_nodes.size(); ++node_id) {\n\n    // for convenience of printing who I am\n    const Message my_info{ node_id, thread_id, uitsl::get_proc_id() };\n\n    auto\u0026 node = my_nodes[node_id];\n\n    for (auto\u0026 input : node.GetInputs()) {\n      const Message received{ input.GetNext() };\n      std::cout \u003c\u003c received.ToString() \u003c\u003c \"  =\u003e  \" \u003c\u003c my_info.ToString()\n        \u003c\u003c '\\n';\n    }\n  }\n\n}\n\n// make each thread execute send_task then receive_\u0026ask\nvoid thread_job(const uitsl::thread_id_t thread_id, netuit::Mesh\u003cSpec\u003e\u0026 mesh) {\n\n  // get nodes that this thread on this process are responsible for\n  auto my_nodes = mesh.GetSubmesh(thread_id);\n\n  send_task(thread_id, my_nodes);\n  receive_task(thread_id, my_nodes);\n\n}\n\nint main() {\n\n  // instantiate a network of conduits\n  netuit::Mesh\u003cSpec\u003e mesh{\n    // how should nodes be connected?\n    netuit::RingTopologyFactory{}(num_nodes),\n    // how should nodes be assigned to threads?\n    uitsl::AssignRoundRobin\u003cuitsl::thread_id_t\u003e{num_threads},\n    // how should nodes be assigned to processes?\n    uitsl::AssignContiguously\u003cuitsl::proc_id_t\u003e{num_procs, num_nodes}\n  };\n\n  // kick off threads\n  uitsl::ThreadTeam team;\n  for (uitsl::thread_id_t tid = 0; tid \u003c num_threads; ++tid) {\n    team.Add([tid, \u0026mesh](){ thread_job(tid, mesh); });\n  }\n\n  // wait for threads to complete\n  team.Join();\n\n  return 0;\n\n}\n```\n\nNow compile and run.\n\n```sh\nmpicxx --std=c++17 -O3 -DNDEBUG -Iinclude/ high.cpp -lpthread\nscript/uitexec -n 2 script/uitwrap ./a.out | cat\n```\n\n## Modular Duct Implementations\n\nThe Duct implementations used for intra-thread, inter-thread, or inter-process communication can be swapped at compile time.\nDesired implementations should be specified through the `ImplSelect` type.\nHere's an example.\n\n`swipswap.cpp`:\n```cpp\n#include \u003cstring\u003e\n\n#include \"cereal/include/cereal/types/string.hpp\"\n\n#include \"uit/ducts/intra/put=dropping+get=stepping+type=any/a::SerialPendingDuct.hpp\"\n#include \"uit/ducts/proc/put=dropping+get=stepping+type=cereal/inlet=RingIsend+outlet=Iprobe_c::IriOiDuct.hpp\"\n#include \"uit/ducts/thread/put=dropping+get=stepping+type=any/a::RigtorpDuct.hpp\"\n#include \"uit/setup/ImplSelect.hpp\"\n#include \"uit/setup/ImplSpec.hpp\"\n\n#include \"netuit/arrange/RingTopologyFactory.hpp\"\n#include \"netuit/mesh/Mesh.hpp\"\n\nconstexpr size_t num_nodes = 4;\n\nusing Message = std::string;\n\nusing ImplSel = uit::ImplSelect\u003c\n  uit::a::SerialPendingDuct, // specifies IntraDuct\n  uit::a::RigtorpDuct, // specifies ThreadDuct\n  uit::c::IriOiDuct // specifies ProcDuct\n\u003e;\n\nusing ImplSpec = uit::ImplSpec\u003c\n  Message, // specifies message type\n  ImplSel // specifies impls to use for intra, thread, proc communication\n\u003e;\n\nint main() {\n\n  netuit::Mesh\u003cImplSpec\u003e mesh{\n    // how should nodes be connected?\n    netuit::RingTopologyFactory{}(num_nodes),\n    // how should nodes be assigned to threads?\n    uitsl::AssignSegregated\u003cuitsl::thread_id_t\u003e{},\n    // how should nodes be assigned to processes?\n    uitsl::AssignSegregated\u003cuitsl::proc_id_t\u003e{}\n  };\n\n  // etc...\n\n  return 0;\n\n}\n\n```\n\n```sh\nmpicxx --std=c++17 -Iinclude/ -Ithird-party/ swipswap.cpp -lpthread\nscript/uitexec -n 2 script/uitwrap ./a.out | cat\n```\n\nBecause implementation is specified through type instances instead of globally, multiple implementations may be used within the same executable.\n\n## Duct Categories\n\nIn the `include/uit/ducts/` directory, duct implementations are categorized by the communication context they're designed for:\n  * intra-thread communication (\"`intra/`\"),\n  * inter-thread communication (\"`thread/`\"), and\n  * inter-process communication (\"`proc/`\").\n\nA fourth category, \"`mock`\", provides non-functional implementations meant for testing or situations compiling multithread or multiprocess code isn't feasible.\n\n## Message Type\n\nImplementations are templated on message type, allowing nearly any type to be sent as a message.\nHowever, not all implementations are compatible with all types.\nImplementations are organized and subnamespaced using the following type-compatibility designators.\n\n  * \"any\" (subnamespace `a`)\n    * any type with a move or copy constructor\n  * \"cereal\" (subnamespace `c`)\n    * any type compatible with the [cereal C++ serialization library](https://github.com/USCiLab/cereal)\n    * cereal has great support for standard library containers!\n    * custom types can be easily be made serializable with cereal by writing some boilerplate member functions\n  * \"fundamental\" (subnamespace `f`)\n    * nominally, [fundamental types](https://en.cppreference.com/w/cpp/language/types)\n    * basically, [arithmetic types](https://en.cppreference.com/w/c/language/arithmetic_types)\n  * \"span\" (subnamespace `s`)\n    * class with `.data()`, `.size()`, `::value_type` members\n    * size can be dynamic or fixed at runtime when a duct is created\n  * [\"trivially copyable\"](https://en.cppreference.com/w/cpp/types/is_trivially_copyable) (subnamespace `t`)\n    * basically, objects that can be safely copied with [std:memcpy](https://en.cppreference.com/w/cpp/string/byte/memcpy)\n\nProper use is `static_assert`'ed at compile time.\n\n## Word Ducts vs. Accumulating Ducts\n\nIn addition to word ducts, which deliver individual messages, conduit also provides accumulating ducts, which may perform an operation (for example, addition) to consolidate messages traveling through the same duct.\nThese ducts are useful for modeling, for example, the transfer of a continuously-valued quantity.\n\n![word behavior](docs/assets/word.gif)\n*Animation of word behavior, where any message that is received corresponds to an identical sent message.*\n\n![accumulating behavior](docs/assets/accumulating.gif)\n*Animation of accumulating behavior, where received messages may constitute an amalgamation of several sent messages.*\n\n`TryStep` or `GetNext` may not be called on accumulating ducts.\nCalling `Jump` or `JumpGet` on an accumulating duct will reset the accumulating quantity.\nCalling `Get` allows the accumulating quantity to be observed without being reset.\n\n## Word Ducts: Growing vs. Dropping\n\nWord ducts may exhibit one of two behaviors with respect to put operations: dropping or growing.\n\nFor a \"growing\" duct, no put call on that duct will block or reject the incoming message.\n\n![growing behavior](docs/assets/growing.gif)\n*Animation of growing behavior, where puts are always accommodated.*\n\nFor a \"dropping\" duct, a put call may block or reject an incoming message i.e., if its send buffer is full.\n\n![dropping behavior](docs/assets/dropping.gif)\n*Animation of growing behavior, where puts may be rejected.*\n\n## Word Ducts: Skipping vs. Stepping\n\nWord ducts may have one of two capacities with respect to get operations: skipping or stepping.\n\nFor a \"stepping\" duct, received messages may be retrieved one-by-one.\n\n![stepping capability](docs/assets/stepping.gif)\n*Animation of stepping get capability, where received messages can be requested one-by-one.*\n\nFor a \"skipping\" duct, only the most-recently received message can be retrieved.\nCalling `GetNext` or `TryStep` (instead of `JumpGet` or `Jump`) on a skipping duct will throw an assert (in debug mode).\n\n![skipping capability](docs/assets/skipping.gif)\n*Animation of skipping get capability, where only most-recently received messages are available.*\n\n## Consolidation\n\nInter-process messages may incur significant overhead in terms of processing time and message size, especially for small messages.\nSo, consolidating many small messages sent between pairs of processes into fewer larger messages can pay off as a big performance win.\n\nConduit provides duct implementations that can perform different types of consolidation.\nTo reduce thread synchronization overhead and prevent threads from needing to run in lockstep, implementations only consolidate messages between ducts within a particular thread that are headed to another particular thread and process (not between all sets of threads on two processes).\n(If inter-thread consolidation makes sense for your use case, though, writing an extension implementation would be feasible.)\n\n![consolidated ducts](docs/assets/consolidated.gif)\n*Comparison of unconsolidated and consolidated interprocess messaging.*\n\nUnder the hood, consolidation among ducts is accomplished by a shared back-end, managed via `shared_ptr`.\nThis design allows for multiple independent sets of consolidated ducts to be instantiated concurrently.\nThe high-level `netuit` interface takes care of initializing and cleaning up the shared back-end automatically.\n\n## Unconsolidated\n\nWithout consolidation, each duct would send independent inter-process messages between all pairs of connected inlets and outlets.\n\n![unconsolidated ducts](docs/assets/unconsolidated.gif)\n*Animation of unconsolidated inter-process ducts.*\n\n## Buffered\n\nBuffered ducts consolidate messages on an inlet-by-inlet basis.\nWhen `TryFlush()` is called on a duct, its pending inter-process messages are consolidated and sent as a single message.\n\n![buffered ducts](docs/assets/buffered.gif)\n*Animation of buffered inter-process ducts.*\n\n## Pooled\n\nPooled ducts expect one contribution to be provided from each constituent duct in a round-robin fashion.\nThis potentially allows for fixed-length interprocess messages to be used under the hood.\nThe pooled message is automatically sent immediately all constituent ducts have contributed.\n\n![pooled ducts](docs/assets/pooled.gif)\n*Animation of pooled inter-process ducts.*\n\n## Aggregated\n\nAggregated ducts allow an arbitrary number of message contributions --- zero or more --- to be made from each duct.\nOnce each duct has called `TryFlush()`, all pending inter-process messages are consolidated and sent as a single message.\nThis operation preserves the ordering of messages sent from the same inlet.\n\n![aggregated ducts](docs/assets/aggregated.gif)\n*Animation of aggregated inter-process ducts.*\n\n## Extensibility\n\n  * Create your own topologies by writing a topology-generating method or loading a topology from file, perhaps generated via another tool like [NetworkX](https://networkx.github.io/documentation/stable/reference/readwrite/adjlist.html).\n  * Define your own delegation functor to control how nodes are distributed between threads and processes.\n  * Seamlessly write and build with your own intra-thread, inter-thread, or inter-process duct implementations.\n\nImplementations of inter-process communication currently use the [Messgage Passing Interface (MPI)](https://www.mpi-forum.org/docs/) standard.\n\n## Benchmarks\n\nBenchmarks are performed during Travis builds and occasionally on the [iCER HPCC cluster](https://icer.msu.edu/).\nBenchmark results are available at [https://osf.io/7jkgp/](https://osf.io/7jkgp/).\n\n## Organization\n\nCode is organized into three hierarchically-leveized namespaces.\n\nBase-level utility code resides in the `uitsl` (\"conduit support library\") namespace.\nAlthough tools in this namespace may be useful outside the scope of the conduit project, no conduit API is written in this namespace.\n\nThe low-level API resides in the `uit` (\"conduit\") namespace.\nThe high-level API resides in the `netuit` (\"networked conduit\") namespace.\n\n## Acknowledgement\n\nThis project is built using the [Empirical C++ library](https://github.com/devosoft/Empirical/).\n\nThis project is developed in support of the [DISHTINY](https://mmore500.com/dishtiny) digital multicellularity evolution project.\n\nThis package was created with [Cookiecutter](https://github.com/audreyr/cookiecutter) and the [devosoft/cookiecutter-empirical-project](https://github.com/devosoft/cookiecutter-empirical-project) project template.\n\nThis research was supported in part by NSF grants DEB-1655715 and DBI-0939454.\nThis material is based upon work supported by the National Science Foundation Graduate Research Fellowship under Grant No. DGE-1424871.\nAny opinions, findings, and conclusions or recommendations expressed in this material are those of the author(s) and do not necessarily reflect the views of the National Science Foundation.\n\nMichigan State University provided computational resources used in this work through the [Institute for Cyber-Enabled Research](https://icer.msu.edu/).\n\nThis research is conducted in affiliation with the [Digital Evolution Laboratory](https://devolab.org/) at Michigan State University, the [BEACON Center for the Study of Evolution in Action](https://www3.beacon-center.org/), the [Ecology, Evolutionary Biology, and Behavior](https://eebb.natsci.msu.edu/) Program at MSU, and the [Department of Computer Science and Engineering](http://cse.msu.edu/) at MSU.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmmore500%2Fconduit","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmmore500%2Fconduit","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmmore500%2Fconduit/lists"}