One of the fundamental concepts of HPX is the notion of a "future", or "future value".
We can create a future value and then when we need it call a get()
function on it.
With this in mind we will create a mutlithreaded factorial program that will use futures to spawn asynchronous actions to calculate the factorial of a given value.
Navigate to the directory you want to create your factorial, for this tutorial I am going to create a folder in my home directory called factorial
and create the file factorial.cpp
in there.
Note that I use vim, but you can use whatever text editor you want to edit the file.
$ cd $ mkdir factorial $ cd factorial $ vim factorial.cpp
The first thing we will do in this file is to include all of the header files we need.
// factorial.cpp #include <iostream> #include <hpx/hpx.hpp> #include <hpx/runtime/actions/plain_action.hpp> #include <hpx/runtime/components/plain_component_factory.hpp> #include <hpx/hpx_init.hpp> #include <boost/program_options.hpp> #include <boost/lexical_cast.hpp> #include <boost/serialization/serialization.hpp> #include <boost/serialization/export.hpp> using namespace hpx; namespace po = boost::program_options;
As you can see we include quite a number of things from hpx and boost. Now we will define our factorial function. To refresh your memory here is the regular recursive version.
// Not part of the HPX version of factorial // double fact(double n) // { // if (n == 0) // return 1; // return n * fact(n-1); // }
In order to use our future construct we need to package up this function in an HPX action. Actions are work that can be spawned by the runtime system. So we will declare our function prototype and package it into an action.
double fact(double n); typedef actions::plain_result_action1<double, double, fact> factorial_action; HPX_REGISTER_PLAIN_ACTION(factorial_action);
With this bit of code we have created our factorial action and registered it with the runtime system. Now since we have the action we can create our future construct.
typedef lcos::eager_future<factorial_action> factorial_future;
Now that we have our future construct available, we can code our function! This is what the HPX factorial function looks like.
double fact(double n) { if (n == 0) return 1; // We have to specify which locality to spawn this future on, // and we want to spawn it on the logical 'next' locality // (if there is one) gid_type next = get_runtime().get_process().next(); factorial_future future(next, n - 1); return n * future.get(); }
Not too bad so far! We created the future and then immediately tried to get the value of it.
However we could have done some other stuff in between and then when we called future.get()
it would have tried to get the value.
Since this is an eager future, the action is created when we create the future.
If we had done a lot of stuff in between creating the future and then calling future.get()
we likely would have already had the value available!
Now you might want to jump to defining main()
, but we have to do some things first in order to make sure the runtime is set up correctly.
Just like main()
is the entry point for any C/C++ program, there is an hpx_main()
that is the entry point for any HPX program.
We use the Program Option library from Boost in order to easily process our command line options, as well as those for the runtime so we will use that in order to pass in the value to calculate.
int hpx_main(po::variables_map &vm) { double argument = 6; // Default value to calculate is 6 double result = 0; // Where we will store our result // Now we process our command line option, which stores its value in // argument get_option(vm, "value", argument); // We use the helper method provided in hpx_init.hpp called // "find_here()" to find the locality we are on gid_type here = find_here(); factorial_future future(here, argument); result = future.get(); std::cout << "Result: " << result << std::endl; // Make sure to properly shut down the runtime for all localities hpx_finalize(); return 0; }
Not too complicated, the only tricky thing is to make sure we properly shut down the runtime.
Now we write main()
. Since all of the work for our program is in hpx_main()
, we only use main()
for declaring which command line arguments we want to support and then initialize the runtime system.
int main(int argc, char* argv[]) { // Configure application-specific options po::options_description desc_commandline("Usage: factorial [hpx_options] [options]"); desc_commandline.add_options() ("value,v", po::value<double>(), "the number to be used as the argument to fact (default is 6)") ; // Initialize and run HPX, hpx_init calls hpx_main so we don't need to int retcode = hpx_init(desc_commandline, argc, argv); return retcode; }
Congratulations!
You have written your very first HPX program.
Now in order to compile and run it we have to tell CMake to integrate with the HPX build system to build it.
Now when you save the file, go back into the directory we just made and create the file CMakeLists.txt
.
$ cd ~/factorial $ vim CMakeLists.txt
In this file we are going to add the following lines.
project(factorial_example) cmake_minimum_required(VERSION 2.6) set(HPX_MAJOR_VERSION 0) set(HPX_MINOR_VERSION 2) set(HPX_PATCH_LEVEL 0) set(HPX_VERSION "${HPX_MAJOR_VERSION}.${HPX_MINOR_VERSION}.${HPX_PATCH_LEVEL}") set(HPX_SOVERSION ${HPX_MAJOR_VERSION}) include($ENV{HPX_ROOT}/share/cmake/Hpx.cmake) include($ENV{BOOST}) # set default cmake build type to RelWithDebInfo (None Debug Release RelWithDebInfo MinSizeRel) if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE "RelWithDebInfo") endif() add_hpx_executable(factorial SOURCES factorial.cpp)
It is important to note that you need to have the environment variable HPX_ROOT set. In this case, HPX_ROOT should point to wherever the HPX files are being installed to. This will vary based on whether you have an in-source build or an out-of-source build. If you have an in-source build, then you should be able to set HPX_ROOT to be HPX_HEAD (if you followed the Installing HPX tutorial).
$ export HPX_ROOT=$HPX_HEAD
If you have an out-of-source build, and you followed the Out of Source build tutorial, then you need to set HPX_ROOT to point to HPX like this.
$ export HPX_ROOT=$HPX
Now that you have told CMake to build our executable, we are going to build our file in a separate directory.
(We could build it in the same directory, but for any real applications work you would want these to be separate, see the Out of Source build tutorial for reasons).
I will call the directory build_factorial
and put it in the home directory.
$ cd $ mkdir build_factorial $ cd build_factorial
Now that we have our build directory, we will call the appropriate CMake commands and then make commands. Note that you need to have the BOOST environment variable set (look in Installing HPX tutorial).
$ cd ~/build_factorial $ cmake ../factorial/ -DBOOST_ROOT=$BOOST -DCMAKE_INSTALL_PREFIX=`pwd` $ make install
If all goes well then you will have compiled your first HPX program! To run it type
$ cd ~/build_factorial/bin $ ./factorial Result: 720 $ ./factorial -v 8 Result: 40320
That is it for this tutorial! Please take a look at the other examples and our doxygen to learn even more about HPX.
--Phillip LeBlanc