*HPX* provides its users with several different tools
to simply express parallel concepts. One of these tools is a local control
object (LCO) called dataflow. An LCO is a type of component that can spawn
a new thread when triggered. They are also distinguished from other components
by a standard interface which allow users to understand and use them easily.
Dataflows, being a LCO, is triggered when the values it depends on become
available. For instance, if you have a calculation X that depends on the
result of three other calculations, you could set up a dataflow that would
begin the calculation X as soon as the other three calculations have returned
their values. Dataflows are set up to depend on other dataflows. It is
this property that makes dataflow a powerful parallelization tool. If you
understand the dependencies of your calculation, you can devise a simple
algorithm which sets up a dependency tree to be executed. In this example,
we calculate compound interest. To calculate compound interest, one must
calculate the interest made in each compound period, and then add that
interest back to the principal before calculating the interest made in
the next period. A practical person would of course use the formula for
compound interest:

F = P(1 + i) ^ n where: F= Future value P= Principal i= Interest rate n= number of compound periods

Nevertheless, we have chosen for the sake of example to manually calculate the future value by iterating:

I = P * i and P = P + I

The source code for this example can be found here: interest_calculator.cpp.

To compile this program, go to your *HPX* build directory
(see Getting Started
for information on configuring and building *HPX*) and
enter:

make examples.quickstart.interest_calculator

To run the program type:

./bin/interest_calculator --principal 100 --rate 5 --cp 6 --time 36

This should print:

Final amount: 134.01 Amount made: 34.0096

Let us begin with main, here we can see that we again are using Boost.Program
Options to set our command line variables (see Fibonacci
Example for more details). These options set the principal, rate,
compound period, and time. It is important to note that the units of time
for `cp`

and `time`

must be the same.

int main(int argc, char ** argv) { options_description cmdline("Usage: " HPX_APPLICATION_STRING " [options]"); cmdline.add_options() ("principal", value<double>()->default_value(1000), "The principal [$]") ("rate", value<double>()->default_value(7), "The interest rate [%]") ("cp", value<int>()->default_value(12), "The compound period [months]") ("time", value<int>()->default_value(12*30), "The time money is invested [months]") ; return hpx::init(cmdline, argc, argv); }

Next we look at hpx_main.

int hpx_main(variables_map & vm) { { using hpx::shared_future; using hpx::make_ready_future; using hpx::lcos::local::dataflow; using hpx::util::unwrapped; hpx::naming::id_type here = hpx::find_here(); double init_principal=vm["principal"].as<double>(); //Initial principal double init_rate=vm["rate"].as<double>(); //Interest rate int cp=vm["cp"].as<int>(); //Length of a compound period int t=vm["time"].as<int>(); //Length of time money is invested init_rate/=100; //Rate is a % and must be converted t/=cp; //Determine how many times to iterate interest calculation: //How many full compund periods can fit in the time invested // In non-dataflow terms the implemented algorithm would look like: // // int t = 5; // number of time periods to use // double principal = init_principal; // double rate = init_rate; // // for (int i = 0; i < t; ++i) // { // double interest = calc(principal, rate); // principal = add(principal, interest); // } // // Please note the similarity with the code below! shared_future<double> principal = make_ready_future(init_principal); shared_future<double> rate = make_ready_future(init_rate); for (int i = 0; i < t; ++i) { shared_future<double> interest = dataflow(unwrapped(calc), principal, rate); principal = dataflow(unwrapped(add), principal, interest); } // wait for the dataflow execution graph to be finished calculating our // overall interest double result = principal.get(); std::cout << "Final amount: " << result << std::endl; std::cout << "Amount made: " << result-init_principal << std::endl; } return hpx::finalize(); }

Here we find our command line variables read in, the rate is converted
from a percent to a decimal, the number of calculation iterations is determined,
and then our dataflows are set up. Notice that we first place our principal
and rate into a dataflow by passing the variables `p`

and `i_rate`

to an action
called `identity_action`

:

[interest_id_action]

In this way `hpx::lcos::dataflow_base`

`<double> principal`

and `rate`

will be initialized
to `p`

and `i_rate`

when `hpx::lcos::dataflow`

`<identity_action>`

returns a value. These dataflows enter
for loop and are passed to `interest`

.
Next `principal`

and `interest`

are passed to the reassignment
of `principal`

. This loop
continues for each compound period that must be calculated. To see how
`interest`

and `principal`

are calculated in the loop
let us look at `calc_action`

and `add_action`

:

// Calculate interest for one period double calc(double principal, double rate) { return principal * rate; } /////////////////////////////////////////////////////////////////////////////// // Add the amount made to the principal double add(double principal, double interest) { return principal + interest; }

After the dataflow dependencies have been defined in hpx_main, we see the following statement:

double result = principal.get_future().get();

This statement calls `hpx::future::get`

`()`

on the last dataflow created by our for
loop. The program will wait here until the entire dataflow tree has been
calculated and the value assigned to result. The program then prints out
the final value of the investment and the amount of interest made by subtracting
the final value of the investment from the initial value of the investment.