ParadisEO - SMP Lesson 1: Algorithm wrapping with Master / Workers model

Note: All the components are not presented in this lesson. To know the completeness of components refer to API documentation of ParadisEO-EO and ParadisEO-SMP.

Introduction

This lesson will teach you how to start several EO algorithms with SMP in order to parallelize some steps. Thanks to SMP, these algorithms are run on differents processes leading to a possible drastic gain of computation time.

Requirements

Now you are supposed to have built an EO algorithm that you want to run on multicores architecture.

Generic use

Before giving an example of practical use, we will show you generic way to use the SMP wrapper.

    #include <smp>
    // Prepare components of your algorithm according to the API of your algorithm
    Component comp1;
    Component comp2;
    ...

    // Create your population
    eoPop<EOT> pop(...);

    // Create and use the wrapper as if it was your algorithm
    MWModel<eoAlgo,EOT> mw(comp1,comp2,...)

    //Start the algorithm on a specific population
    mw(pop);

Notice that the wrapper propose you the same methods as the original algorithm. Only the name of your algorithm type change !

Pratical use : the case of eoEasyEA

Assuming you built an eoEasyEA algorithm (refer to EO documentation and tutorials for further information), let is see how to run it using SMP.

    #include <smp>
    // Here we define all stuff for eoEasyEA
    ... See smp/tutorial/Lesson1/lesson1_eoEasyEA.cpp for more details

    // Create your population
    // Define population
    eoPop<Problem> pop(param.popSize, chromInit);

    try
    {
        // Create the algorithm
        MWModel<eoEasyEA,Problem> mw(genCont, plainEval, select, transform, replace);
        // Start a parallel evaluation on the population
        mw.evaluate(pop);
        std::cout << "Initial population :" << std::endl;
        pop.sort();
        std::cout << pop << std::endl;
        // Start the algorithm on the population
        mw(pop);
        std::cout << "Final population :" << std::endl;
        pop.sort();
        std::cout << pop << std::endl;
    }
    catch(exception& e)
    {
        cout << "Exception: " << e.what() << '\n';
    }

As you can see, it is really simple !

Number of workers

The default behavior of the wrapper is to determine the number of workers according to your hardware. Therefore, the wrapper allows you to change the number of workers manually in the case of, for instance, a core is unavailable.

    // Create the algorithm
    unsigned workersNb = 4;
    MWModel<eoEasyEA,Problem> mw(workersNb, genCont, plainEval, select, transform, replace);

Please, notice that you have to use a unsigned and not an int even if it is positive. This is due to internal mecanisms and more precisly to variadic templates and parameters pack used with default parameter.

Policies

The scheduler can use different policies to perform tasks, which are linear or progressive. The linear one divide your population into a numbers of subgroups according to the number of workers. Then, it sends subgroups and task to workers and wait till the end of the computation. On the contrary, the progressive policy tries to balance the load by sending larger and larger packages according to an internal planning.

The advantage of the second one is that if a core becomes busy, performances are not affected as much as with the linear policy. Its drawback is that the overhead (ie. communications cost) is more important.

In theory, the progressive policy is faster than the linear one, but in practice it depends. As the linear policy is more stable on the most part of hardware configuration, it is the default policy for the scheduler.

Now, let is see how to change the policy with the wrapper :

    //Algorithm with a progressive policy
    MWModel<eoEasyEA,Problem,ProgressivePolicy> mw(genCont, plainEval, select, transform, replace);
    // Algorithm with a linear policy
    MWModel<eoEasyEA,Problem,LinearPolicy> mw(genCont, plainEval, select, transform, replace);