Changes

Jump to: navigation, search

OpenMP Debugging in Visual Studio / Team Debug

113 bytes added, 17:32, 6 December 2017
Case B - Using the Parallel Stacks and the Parallel Watch Window
From the output, we can see that for this run, 4 different threads occupied the 6 function calls. In order of the function calls in the code, worker 0 took foo(), worker 3 took coo(), worker 2 took boo(), worker 1 took doo(), worker 0 took zoo(), and finally worker 3 took the remaining foo() function. Worker 0 in this case actually refers to the Main Thread.
The '''Parallel Stacks window Window''' allows us to see the call stack information for all active threads at any point in our program.
'''Setup: '''
1. Put a breakpoint at all function calls, all function definitions, and cilk_sync.<br/>
'''Walkthrough:'''
First function call:<br/>
At our first function call at
cilk_spawn foo(i);
we can see in the Threads window the Main Thread, with a yellow arrow pointing at it :
[[File:Stacks-step1-threads.PNG|500px|center|Main Thread]]
In the above, the blue-highlighted boxes refer to the call stack of the current thread, which is Main Thread, indicated by the yellow arrow. The program begins with 4 threads; 1 splits off into what is our Main Thread, and the other 3 split off elsewhere.<br/>
We can hover our mouse over any row in the boxes to get more info:
[[File:Stacks-step1-hover.PNG|300px|center|Step 1 - stacksMain Thread]]Hovering above "main" in the Main "1 Thread", we can see which line in the code the current stack frame is at.  As we keep hitting F5, we go through each stack frame at the point of our breakpoints. Here, our foo() function was executed. We can see the sleep_for function that was executed, all within the Main thread:[[File:Stacks-step1b.PNG|500px|center|Step 1b - stacks]]<br/>
As we keep hitting F5, we go through each stack frame at the point of our breakpoints. Here, our foo(i) function was executed. We can see the sleep_for function that was executed, all within the Main thread:
[[File:Stacks-step1b.PNG|500px|center|Main Thread]]
<br/>
At this point, another thread has begun. We can see "Worker 3" has started in the Threads window:
[[File:Stacks-step1b-threads.PNG|500px|center|Step 1b - Worker 3]]<br/>
And if we double click on it, the focus will shift to its call stack in the Parallel Stacks window:
[[File:Stacks-step1b-worker3.PNG|500px|center|Step 1b - Worker 3]]<br/>To clear out the other threads from the program which have nothing to do with our cilk spawned threads, we can flag the threads we want in the Threads window, and then click on the flags icon at the top of the Parallel Stacks windowWindow, which will just show the call stacks of the flagged threads:[[File:Stacks-step1b-threads-flagged.PNG|500px|center|Step 1b - Flagged Threads]][[File:Stacks-step1b-flagged.PNG|500px|center|Step 1b - Flagged Threads]] 
<br/>
From this point forward, we will just view the call stacks for the threads which we are flagging, which are the Main Thread, and the 3 worker threads.
Now Worker 3 has taken the next spawned thread, which we can see the call stack highlighted in blue:
[[File:Stacks-step2.PNG|500px|center|Worker 3]]
Also, the middle box indicates the 2 threads, Worker 1 and Worker 2, which seem to just be waiting for work. Main Thread on the left also seems to be just waiting.<br/>
Third function call:
Now Worker 1 has taken charge of the next spawned thread, highlighted in blue:
[[File:Stacks-step3.PNG|500px|center|Worker 1]]
<br/>
Fourth function call:
cilk_spawn doo(i);
[[File:Stacks-step4.PNG|500px|center|Worker 2]]
At this point, the Main thread, in function foo, has already printed its line.
<br/>
Fifth function call:
cilk_spawn zoo(i);
Finally, the Main thread is available and picks up the next spawned thread, which is a call to zoo function.
[[File:Stacks-step5.PNG|500px|center|Main thread]]
As we had stepped to the breakpoint set at each function definitions, we saw that Worker 1's stack call had gone from boo(i), to zzz(i) to cough(i), Worker 2 had gone from doo(i) to vroom(i) to beep(i), and Worker 3's had gone from coo(i) to bla(i).<br/>
Sixth function call:
The next thread to free up was Worker 2, which snatched up the call to function foo() since it was available.
[[File:Stacks-step6.PNG|500px|center|Worker 2]]
<br/>
Worker 3 is free:
Now, Worker 3 has finished its work and is just waiting, as shown in the rightmost box:
[[File:Stacks-step7.PNG|500px|center|Worker 23]]<br/>
Syncing up:
At this point, all spawned threads have synced up, as indicated in the right box, and the Main Thread continues, on the left side box.
[[File:Stacks-step8.PNG|500px|center|Synced up]]
<br/>
The Parallel Stacks Window is a valuable tool in debugging multi-threaded applications as we can have a view of all threads at once and their call stacks in any given time. It allows us to see the delegation of work to different threads as they are made free and as they are indicated by the compiler to work.
<br/>
Now we will use the '''Parallel Watch Window''' to view the variable i, and its value in the different threads as we go through the stack frames as determined by our breakpoints.
Now we will use the Parallel Watch Window to view the variable i, and its value in the different threads as we go through the stack frames as determined by our breakpoints. '''Setup: '''
1. After starting debug (F5), open a Parallel Watch Window, and add i into an <Add Watch> column.<br/>
2. Since we also want to see how variable i changes inside each of the functions, on top of the previous breakpoints we placed as in the above walkthrough, we will place more breakpoints, one at each of the return statements in each of the functions. That way we will be able to see the parameter value when it first entered the function, and how it leaves the function.
'''Walkthrough:'''
First Function Call:
At this breakpoint, we can see Main Thread has taken up the spawned thread, and thread id is 8120.
[[File:Parallelwatch-step1-threads.PNG|500px|center|Synced upMain Thread]]
In our Parallel Watch window, the thread is active, and we can see that the value of i is 1. When we hover our mouse over the value, we can see the previous value, which is a random int because that was the value before it was initialized.
[[File:Parallelwatch-step1.PNG|200px|center|Synced upMain Thread]]<br/>
Second Function Call:
cilk_spawn coo(i);
[[File:Parallelwatch-step2-callstack.PNG|300px|center|Synced up]]
[[File:Parallelwatch-step2b.PNG|200px|center|Synced up]]
<br/>As we step through each breakpoint, we see the same thing happen with the other worker threads once they pick up a function call, but although the i value gets set once they enter the functions of each of the different calls, when the stack frame goes back to the main method to get ready to call the next function, we don't see the i value as 1 for all threads, because the stack frame from the main method only sees the Main Thread's i set as 1, from the main method. The Parallel Watch only lets us see the pertinent threads for the current stack frame we are on. So we will only see all threads in the same window if we are on a stack frame that belongs from the main method. But as we step into a stack frame that is within one of the functions, we will see the thread that is handling that function, and the i value at that point. This window is not like the Parallel Stacks window where you can see all threads' positions simultaneously. <br/>
As we step to the breakpoint that is the return line for the foo(i) function, which Main Thread was working on, we can see the changed value for i:
[[File:Parallelwatch-step3.PNG|200px|center|Foo(i)]]
Foo() had incremented i by 1.
<br/>Now let's see say we don't care to see all threads based on all of the breakpoints we had put. Let's say we just want to focus on the Main Thread's work, as well as the thread that took the doo(i) function, which according to the Parallel Stacks Window, we can see Worker 1 took up. So in the Threads we will flag those two threads:[[File:Parallelwatch-step4-flags.PNG|300px|center|Foo(i)Flag]]
And then in our Parallel Watch Window, we will check on the Flag icon in the top corner, which will only watch the threads we have flagged:
[[File:Parallelwatch-step4-flags-checked.PNG|300px|center|Foo(i)Flag]]<br/>
Now, we can only focus on the i values for Main Thread and Worker 1. As we step through each breakpoint, the Parallel Watch Window will only show rows that have to do with any of our flagged threads.
[[File:Parallelwatch-step5-nothreads.PNG|500px|center|Foo(i)No Thread]]
We can see that in this stack frame, which is in method cough(i), nothing shows because our two flagged threads don't have that stack frame.
<br/>
Following Main Thread and Worker 1:
Finally, when we hit the breakpoint where zoo(i) gets called, we can see Main Thread picking up the work, and the value of i upon entering the function is set to 1.
[[File:Parallelwatch-step6.PNG|500px|center|Foozoo(i)]]
The Parallel Stacks Window also shows just our flagged threads. The blue highlighted boxes are Main Thread's stack, and the right box is Worker 1' stack.
We keep stepping through the breakpoints. Next, we see Worker 1's next stack frame, which is the point where it calls vroom(i) from within doo(i).
[[File:Parallelwatch-step7.PNG|500px|center|Foovroom(i)]]
At this point, the value of i is 100. At doo(), it had been multiplied by 100.
<br/>
We keep pressing F5 and see as Main Thread calls woof, meow and oink from doo and see its i value changes, and Worker 1 calls beep, then screech, making the appropriate i changes.
[[File:Parallelwatch-step8.PNG|500px|center|screech(i)]]
<br/>
The Parallel Watch Window is great for seeing variables' values in multiple threads, or seeing specific threads and the variables they deal with. This would be good for tracking how a variable changes in a particular thread, especially in very complex calculations where it's easy to lose track of each thread's work.
92
edits

Navigation menu