Category Archives: Lambda
migration C#, Java, C++ (day 5), lambda and TBB
Phew, this was a terrible day. I wanted to do far more, but I had realized that there is no proper native threading in the C++ standard libraries. Back in the 1990s threading was a minor issue. We only had one CPU and FPU. We were concentrating more on the processes and devices at that time.
I had already mentioned that I wanted to avoid Boost and Qt. The best option I found was TBB, which seems to support multicore-processors very well. I will include TBB in my migration project from now on. Today, there is only one TBB example. It took me a lot of time to get the Intel software installed on my PC.
Finally, after all these years … we can solve problems with PCs that we would not have without them 😉
TBB covers only a small fraction today. This post is mainly about lambda functions.
In C++ you can find a lot of weird stuff that has been solved in a much better way in C#. As a hardcore C# programmer C++ might feel medieval sometimes. Rome was built bit by bit. Hence its streets are chaotic. C# maybe is like Chicago. When the city was built, people had time to think about the infrastructure before they started digging arbitrary holes in the ground.
Let’s see what my thoughts will be on day 15.
Please also check my post Lambda expressions (advanced). I mentioned that the C# compiler takes care of the local variable life-cycles, when lambda expressions use local variables, which would become invalid by the time these expressions try to use them. Remember, these local variables are not created inside the lambda expressions themselves.
Today’s prerequisite for C++
#include <iostream> #include <vector> #include <ctime> #include <thread> #include "tbb/tbb.h" #include "tbb/parallel_for.h" using namespace std;
Lambda
public void DoSomething() { Console.WriteLine(Thread.CurrentThread.ManagedThreadId + " "); Thread.Sleep(200); } // public void Lambdas() { // functor // There are no functors in C#. // Action or Func delegates are the closest approaches. Action lAction = DoSomething; lAction(); // lambda expression Func<int, int> func2 = (int x) => { return ++x; }; int r2 = func2(4); Console.WriteLine(r2); // 5 // lambda expression capturing a local variable // the compiler takes care of the lifetime of variable i int i = 5; Func<int, int> func3 = (int x) => { return x + i; }; int r3 = func3(4); Console.WriteLine(r3); // 9 // lambda expression capturing any local variable by reference List<int> lList = new List<int>() { 1, 2, 3, 5, 7, 11, 13, 17, 19, 23 }; int lSum1 = 0; lList.ForEach((int q) => { lSum1 += q; }); Console.WriteLine(lSum1); // 101 // lambda expression with a specific return type Func<int, bool> func6 = (int x) => { x++; return true; }; bool r6 = func6(4); Console.WriteLine(r6); // True } //
class Class1 implements Runnable { @Override public void run() { try { System.out.println(Thread.currentThread().getId() + " "); Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } } } // class public static void main(String[] args) { Class1 c1 = new Class1(); c1.run(); // lambda expression Function<Integer, Integer> func2 = i -> { return ++i; }; Integer r2 = func2.apply(4); System.out.println(r2); // 5 // lambda expression capturing a local variable // the compiler takes care of the lifetime of variable i Integer i = 5; Function<Integer, Integer> func3 = x -> { return x + i; }; int r3 = func3.apply(4); System.out.println(r3); // 9 // lambda expression capturing any local variable by reference java.util.ArrayList<Integer> lList = new java.util.ArrayList<>(java.util.Arrays.asList(new Integer[]{1, 2, 3, 5, 7, 11, 13, 17, 19, 23})); Integer lSum1 = 0; lList.forEach((Integer q) -> { //lSum1 += q; compiler complains, because it has no write access to lSum1 }); // thus we have to use: lSum1 = lList.stream().map((q) -> q).reduce(lSum1, Integer::sum); System.out.println(lSum1); // 101 // lambda expression with a specific return type Function<Integer, Boolean> func6 = x -> { x++; return true; }; boolean r6 = func6.apply(4); System.out.println(r6); // true } //
class func1 { public: int operator ()(int x) { return ++x; } }; void Lambdas() { // functor int r1 = func1()(3); cout << r1 << endl; // 4 // lambda expression auto func2 = [](int x) { return ++x; }; int r2 = func2(4); cout << r2 << endl; // 5 // lambda expression capturing a local variable int i = 5; auto func3 = [i](int x) { return x + i; }; int r3 = func3(4); cout << r3 << endl; // 9 // lambda expression capturing any local variable auto func4 = [=](int x) { return x + i + r2 * r1; }; // 1 + 5 + (5 * 4) int r4 = func4(1); cout << r4 << endl; // 26 // lambda expression capturing a local variable by reference vector<int> v1{ 1, 2, 3, 5, 7, 11, 13, 17, 19, 23 }; int lSum1 = 0; for (int q : v1) { [&lSum1](int x) {lSum1 += x; } (q); } cout << lSum1 << endl; // 101 // lambda expression capturing any local variable by reference vector<int> v2{ 1, 2, 3, 5, 7, 11, 13, 17, 19, 23 }; int lSum2 = 0; int lProduct2 = 1; for (int q : v2) { [&](int x) {lSum2 += x; lProduct2 *= x; }(q); } cout << lSum2 << " " << lProduct2 << endl; // 101 223092870 // lambda expression capturing any local variable by value // The external variables can be assigned, but they are not referenced, // because the lambda expression is only working with local copies. // This can be achieved by using the keyword "mutable". // By default lambdas are not mutable. int lSum3 = 0; int lProduct3 = 1; auto func5 = [=](int x) mutable {lSum3 += x; lProduct3 *= x; return x; }; int r5 = func5(1); cout << lSum3 << " " << lProduct3 << " " << r5 << endl; // 0 1 1 // lambda expression with a specific return type auto func6 = [](int x) -> bool { x++; return true; }; bool b6 = func6(6); cout << (b6 ? "true" : "false") << endl; // true } //
TBB
public void TBB() { const int lSize = 100; Stopwatch lStopwatch = new Stopwatch(); lStopwatch.Start(); for (int i = 0; i < lSize; i++) DoSomething(); lStopwatch.Stop(); Console.WriteLine(); Console.WriteLine("time for serial loop was " + lStopwatch.ElapsedMilliseconds / 1000.0 + " seconds"); lStopwatch.Reset(); lStopwatch.Start(); Parallel.For(0, lSize, i => DoSomething()); lStopwatch.Stop(); Console.WriteLine(); Console.WriteLine("time for parallel loop was " + lStopwatch.ElapsedMilliseconds/1000.0 + " seconds"); } //
example output:
9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9
9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9
9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9
time for serial loop was 20.001 seconds
9 10 11 13 14 12 15 17 16 9 10 11 13 14 12 15 17 16 9 10 11 13 14 12 15 17 16 9
10 11 13 14 12 15 17 16 9 10 11 13 14 12 15 17 16 9 10 11 13 14 12 15 17 16 9 10
11 13 14 12 15 17 16 9 10 11 13 14 12 15 16 9 10 11 13 14 12 15 16 9 10 11 13 1
4 12 15 9 10 11 13 14 12 15 9 10 11 13 14 12 15
time for parallel loop was 2.408 seconds
public final void TBB() { Class1 lDoSomething = new Class1(); final int lSize = 100; long lStopwatch = System.currentTimeMillis(); for (int i = 0; i < lSize; i++) { lDoSomething.run(); } long lDiff = System.currentTimeMillis() - lStopwatch; System.out.println(); System.out.println("time for serial loop was " + lDiff / 1000 + " seconds"); lStopwatch = System.currentTimeMillis(); ArrayList<Class1> lList = new ArrayList<>(); for (int i = 0; i < lSize; i++) lList.add(lDoSomething); lList.parallelStream().forEach(x -> x.run()); lDiff = System.currentTimeMillis() - lStopwatch; System.out.println(); System.out.println("time for parallel loop was " + lDiff / 1000.0 + " seconds"); } //
example output:
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
time for serial loop was 20 seconds
12 13 14 1 16 11 15 17 14 13 12 1 16 11 15 17 13 12 14 1 16 17 11 15 13 14 12 16 1 11 15 17 13 14 12 16 1 11 15 17 12 13 14 1 16 11 17 15 14 12 13 16 1 15 17 11 12 13 14 16 1 17 11 15 14 13 12 1 16 11 15 17 13 14 12 1 16 15 17 11 14 12 13 16 1 11 17 15 14 12 13 16 1 17 11 15 12 13 1 15
time for parallel loop was 2.841 seconds
void DoSomething() { cout << this_thread::get_id() << " "; this_thread::sleep_for(chrono::milliseconds(200)); } // void TBBs() { const int lSize = 100; time_t lTimer; time(&lTimer); for (int i = 0; i < lSize; i++) DoSomething(); double lSecondsA = difftime(time(nullptr), lTimer); cout << endl << "time for serial loop was " << lSecondsA << " seconds" << endl; time(&lTimer); tbb::parallel_for(0, lSize, 1, [&](int i) { DoSomething(); }); double lSecondsB = difftime(time(nullptr), lTimer); cout << endl << "time for parallel loop was " << lSecondsB << " seconds" << endl; } //
example output:
4616 4616 4616 4616 4616 4616 4616 4616 4616 4616 4616 4616 4616 46
16 4616 4616 4616 4616 4616 4616 4616 4616 4616 4616 4616 4616 4616
4616 4616 4616 4616 4616 4616 4616 4616 4616 4616 4616 4616 4616
4616 4616 4616 4616 4616 4616 4616 4616 4616 4616 4616 4616 4616 46
16 4616 4616 4616 4616 4616 4616 4616 4616 4616 4616 4616 4616 4616
4616 4616 4616 4616 4616 4616 4616 4616 4616 4616 4616 4616 4616
4616 4616 4616 4616 4616 4616 4616 4616 4616 4616 4616 4616 4616 46
16 4616 4616 4616 4616 4616 4616
time for serial loop was 20 seconds
4616 4068 4916 3788 2880 4168 4752 6796 4616 4916 4068 2880 3788 41
68 6796 4752 4616 4068 4916 2880 3788 4168 6796 4752 4616 4916 4068
2880 3788 4168 6796 4752 4616 4916 4068 3788 2880 4168 6796 4752
4616 4068 4916 2880 3788 4168 6796 4752 4616 4068 4916 3788 2880 41
68 6796 4752 4616 4068 4916 2880 3788 6796 4168 4752 4616 4068 4916
2880 3788 6796 4168 4752 4616 4068 4916 2880 3788 6796 4168 4752
4616 4916 4068 3788 2880 6796 4168 4752 4616 4068 4916 3788 2880 67
96 4168 4752 4616 4916 4068 3788
time for parallel loop was 2 seconds