parallel_for_ex.cpp 5.26 KB
Newer Older
1
2
3
// The contents of this file are in the public domain. See LICENSE_FOR_EXAMPLE_PROGRAMS.txt
/*

Davis King's avatar
Davis King committed
4
5
6
7
    This is an example illustrating the use of the parallel for loop tools from the dlib
    C++ Library.

    Normally, a for loop executes the body of the loop in a serial manner.  This means
Davis King's avatar
Davis King committed
8
9
    that, for example, if it takes 1 second to execute the body of the loop and the body
    needs to execute 10 times then it will take 10 seconds to execute the entire loop.
Davis King's avatar
Davis King committed
10
11
12
    However, on modern multi-core computers we have the opportunity to speed this up by
    executing multiple steps of a for loop in parallel.  This example program will walk you
    though a few examples showing how to do just that.  
13
14
15
16
17
18
19
20
21
22
23
*/


#include <dlib/threads.h>
#include <dlib/misc_api.h>  // for dlib::sleep
#include <vector>
#include <iostream>

using namespace dlib;
using namespace std;

Davis King's avatar
Davis King committed
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
// ----------------------------------------------------------------------------------------

void print(const std::vector<int>& vect)
{
    for (unsigned long i = 0; i < vect.size(); ++i)
    {
        cout << vect[i] << endl;
    }
    cout << "\n**************************************\n";
}

// ----------------------------------------------------------------------------------------

void example_using_regular_non_parallel_loops();
void example_using_lambda_functions();

// ----------------------------------------------------------------------------------------

int main()
{
44
45
46
47
48
    // We have 2 examples, each contained in a separate function.  Both examples perform
    // exactly the same computation, however, the second does so using parallel for loops.
    // The first example is here to show you what we are doing in terms of classical
    // non-parallel for loops.  The other example will illustrate how to parallelize the
    // for loops in C++11. 
Davis King's avatar
Davis King committed
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76

    example_using_regular_non_parallel_loops();
    example_using_lambda_functions();
}

// ----------------------------------------------------------------------------------------

void example_using_regular_non_parallel_loops()
{
    cout << "\nExample using regular non-parallel for loops\n" << endl;

    std::vector<int> vect;

    // put 10 elements into vect which are all equal to -1
    vect.assign(10, -1);

    // Now set each element equal to its index value.  We put a sleep call in here so that
    // when we run the same thing with a parallel for loop later on you will be able to
    // observe the speedup. 
    for (unsigned long i = 0; i < vect.size(); ++i)
    {
        vect[i] = i;
        dlib::sleep(1000); // sleep for 1 second
    }
    print(vect);



Davis King's avatar
Davis King committed
77
    // Assign only part of the elements in vect.
Davis King's avatar
Davis King committed
78
79
80
81
82
83
84
85
86
87
    vect.assign(10, -1);
    for (unsigned long i = 1; i < 5; ++i)
    {
        vect[i] = i;
        dlib::sleep(1000);
    }
    print(vect);



Davis King's avatar
Davis King committed
88
    // Sum all element sin vect.
Davis King's avatar
Davis King committed
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
    int sum = 0;
    vect.assign(10, 2);
    for (unsigned long i = 0; i < vect.size(); ++i)
    {
        dlib::sleep(1000);
        sum += vect[i];
    }

    cout << "sum: "<< sum << endl;
}

// ----------------------------------------------------------------------------------------

void example_using_lambda_functions()
{
    cout << "\nExample using parallel for loops\n" << endl;

    std::vector<int> vect;

    vect.assign(10, -1);
109
    parallel_for(0, vect.size(), [&](long i){
Davis King's avatar
Davis King committed
110
        // The i variable is the loop counter as in a normal for loop.  So we simply need
Davis King's avatar
Davis King committed
111
        // to place the body of the for loop right here and we get the same behavior.  The
112
113
114
115
        // range for the for loop is determined by the 1nd and 2rd arguments to
        // parallel_for().  This way of calling parallel_for() will use a number of threads
        // that is appropriate for your hardware.  See the parallel_for() documentation for
        // other options.
Davis King's avatar
Davis King committed
116
117
118
119
120
121
        vect[i] = i;
        dlib::sleep(1000);
    });
    print(vect);


Davis King's avatar
Davis King committed
122
    // Assign only part of the elements in vect.
Davis King's avatar
Davis King committed
123
    vect.assign(10, -1);
124
    parallel_for(1, 5, [&](long i){
Davis King's avatar
Davis King committed
125
126
127
128
129
130
131
132
133
134
        vect[i] = i;
        dlib::sleep(1000);
    });
    print(vect);


    // Note that things become a little more complex if the loop bodies are not totally
    // independent.  In the first two cases each iteration of the loop touched different
    // memory locations, so we didn't need to use any kind of thread synchronization.
    // However, in the summing loop we need to add some synchronization to protect the sum
Davis King's avatar
Davis King committed
135
    // variable.  This is easily accomplished by creating a mutex and locking it before
Davis King's avatar
Davis King committed
136
137
138
139
140
    // adding to sum.  More generally, you must ensure that the bodies of your parallel for
    // loops are thread safe using whatever means is appropriate for your code.  Since a
    // parallel for loop is implemented using threads, all the usual techniques for
    // ensuring thread safety can be used. 
    int sum = 0;
141
    dlib::mutex m;
Davis King's avatar
Davis King committed
142
    vect.assign(10, 2);
143
    parallel_for(0, vect.size(), [&](long i){
Davis King's avatar
Davis King committed
144
145
146
147
148
149
150
151
152
153
154
155
        // The sleep statements still execute in parallel.  
        dlib::sleep(1000);

        // Lock the m mutex.  The auto_mutex will automatically unlock at the closing }.
        // This will ensure only one thread can execute the sum += vect[i] statement at
        // a time.
        auto_mutex lock(m);
        sum += vect[i];
    });

    cout << "sum: "<< sum << endl;
}
156

Davis King's avatar
Davis King committed
157
// ----------------------------------------------------------------------------------------
158