DSPatch v.11.4.1
|
DSPatch, pronounced "dispatch", is a powerful C++ dataflow framework. DSPatch is not limited to any particular domain or data type, from reactive programming to stream processing, DSPatch's generic, object-oriented API allows you to create virtually any graph processing system imaginable.
DSPatch is designed around the concept of a "circuit" that contains "components" interconnected via "wires" that transfer "signals" to and from I/O "buses".
The two most important classes to consider are DSPatch::Component and DSPatch::Circuit. In order to route data to and from components they must be added to a circuit, where they can be wired together.
The DSPatch engine takes care of data transfers between interconnected components. When data is ready for a component to process, a callback: "Process_()" is executed in that component. For a component to form part of a DSPatch circuit, designers simply have to derive their component from the DSPatch::Component base class, configure the component's IO buses, and implement the virtual Process_() callback method.
In order to create a new component, we must derive our component class from the DSPatch::Component base class, configure component IO, and implement the inherited virtual "Process_()" method.
Lets take a look at how we would go about creating a very simple boolean logic "AND" component. This component will accept 2 boolean input values and output the result of: input 1 && input 2.
We begin by deriving our new "AndBool" component from Component:
The next step is to configure our component's input and output buses. This is achieved by calling the base protected methods: SetInputCount_() and SetOutputCount_() respectively from our component's constructor. In our component's case, we require 2 inputs and 1 output, therefore our constructor code will look like this:
Lastly, our component must implement the virtual Process_() method. This is where our component does its work. The Process_() method provides us with 2 arguments: the input bus and the output bus. It is our duty as the component designer to pull the inputs we require out of the input bus, process them accordingly, then populate the output bus with the results.
Our component's process method will look something like this:
Our component is now ready to form part of a DSPatch circuit. Next we'll look at how we can add our component to a circuit and route it to and from other components.
In order for us to get any real use out of our components, we need them to interact with each other. This is where the DSPatch::Circuit class comes in. A circuit is a workspace for adding and routing components. In this section we will have a look at how to create a simple DSPatch application that generates random boolean pairs, performs a logic AND on each pair, then prints the result to the screen.
First we must include the DSPatch header and any other headers that contain components we wish to use in our application:
Next, we must instantiate our circuit object and all component objects needed for our circuit. Lets say we had 2 other components included with "AndBool" (from the first tutorial): "GenBool" (generates a random boolean value then outputs the result) and "PrintBool" (receives a boolean value and outputs it to the console):
Now that we have a circuit and some components, lets add all of our components to the circuit:
We are now ready to begin wiring the circuit:
The code above results in the following wiring configuration:
__________ _________ | | | | | genBool1 |-0 ===> 0-| | ___________ |__________| | | | | __________ | andBool |-0 ===> 0-| printBool | | | | | |___________| | genBool2 |-0 ===> 1-| | |__________| |_________|
Lastly, in order for our circuit to do any work it must be ticked. This is performed by repeatedly calling the circuit's Tick() method. This method can be called manually in a loop, or alternatively, by calling StartAutoTick(), a seperate thread will spawn, automatically calling Tick() continuously.
Furthermore, to boost performance in stream processing circuits like this one, multi-buffering can be enabled via the SetBufferCount() method:
NOTE: If none of the parallel branches in your circuit are time-consuming (⪆10μs), multi-buffering (or even zero buffering) will almost always outperform multi-threading (via SetThreadCount()). The contention overhead caused by multiple threads processing a single tick must be made negligible by time-consuming parallel components for any performance improvement to be seen.
That's it! Enjoy using DSPatch!
(NOTE: The source code for the above tutorials can be found under the "tutorial" folder in the DSPatch root directory).