- Execute the command ltsatool & to bring up the LTSA analyser.
(this is in fact an `alias' command; type the command alias
to see how it is implemented).
Type in your MEETING program of Tutorial Question 1 into the Edit pane
and select Build -> Compile and check that the
resulting LTS is correct in the Draw pane.
If you get a syntax error, the
Edit pane will indicate in grey
the point in the code where the error was detected.
- *Repeat this for VARIABLE program.
Using Check -> Run,
generate the trace from Tutorial Question 2.
Demonstrate the trace to your tutor. Worth
1 Prac Point.
Activate Window -> Alphabet and re-compile. In
the Alphabet pane, confirm that the Alphabet
of VARIABLE is what you expect. Again run the program,
but select the Draw pane and observe the transitions in the LTS.
Note: write.2 is the old FSP format for indexed
events; it is the same as write[2]. The Animator still uses the
old format.
- A definition of the INC_VARIABLE process for N=2
which does not use the when construct is as follows:
INC_VARIABLE_ = INC_VARIABLE_[0],
INC_VARIABLE_[0] = ( read[0] -> INC_VARIABLE_[0]
| write[0] -> INC_VARIABLE_[0]
| write[1] -> INC_VARIABLE_[1] ),
INC_VARIABLE_[1] = ( read[1] -> INC_VARIABLE_[1]
| write[1] -> INC_VARIABLE_[1]
| write[0] -> INC_VARIABLE_[0]
| write[2] -> INC_VARIABLE_[2] ),
INC_VARIABLE_[2] = ( read[2] -> INC_VARIABLE_[2]
| write[2] -> INC_VARIABLE_[2]
| write[1] -> INC_VARIABLE_[1] ).
Type in your INC_VARIABLE program from the tutorial and
compile it. Add the INC_VARIABLE_ code above to the Edit
pane and re-compile. Use the tool to verify that the two programs
have equivalent LTS graphs.
- Download and compile the file Race.java.
The usage of the program is:
in which p threads will try to increment a (shared) variable
n times.
Inspect the code. Locate the parts of the program where the
threads get created and started. Also inspect the run() method
that each thread executes. Don't worry if you don't understand the API
at this point - we will cover that later.
The program aims to demonstrate interference when
each thread tries to increment a shared counter ctr - if there
was no interference, the final value of the counter would be n.
You will notice that the main program waits until all threads have
entered their run method (and incremented the
threadsReady shared variable) - and the threads in turn wait until
the main program `signals' it has reached this point
(by setting the mainReady shared variable - all this is
to maximize the amount of interference!
- Run the program repeatedly with say n=100000
and p=1. There should be no surprises: the final
values of the counter should be 100000. Now repeat this
with p=2,3,4. What trends do you observe for the runs
showing the maximum interference?
- In your copy of Race.java, uncomment the call
to Thread.yield() in the run() method. Recompile and
repeat the above experiments. What do you observe? Why might it be
different from the above case?
- Now comment out the keyword volatile in the definition
of threadsReady and mainReady. This keyword
instructs the compiler that the variable may be modified at any
time by other threads, forcing the corresponding memory location
to be accessed every time the variable is (otherwise the compiler
might try optimizations such as accessing only once the memory location
of an variable which is not changed by the loop).
Re-compile and run with 4 and 8 threads. You should (sometimes)
observe a deadlock. Where and how does this occur?
(ask your tutor for an explanation if need be).
- Download and compile the file race.c.
The usage of the program is:
in which p threads scheduled on c cores (processors)
will try to increment a (shared) variable
n times.
Inspect the code. Again locate the parts of the program where
the threads get created and started. Also inspect
the unsafeSum() function that each thread executes.
You will note that the API is considerably more complex -
this represents a trade-off of simplicity for greater control.
The program is like the Java version but also uses
thread affinity to instruct the operating system
to only schedule a thread on a specific core.
Again don't worry if you don't understand the API
at this point - we will cover that after the break.
- Execute the command x86info. This will display the
number of cores (`CPU's) available on your system (among other things).
On the machines in the CSIT labs, the machines should have 4
cores.
Note: the interference behaviour of this and the previous program
is highly system dependent. You may observe other trends on
different machines. In particular, it behaves differently on the sever
partch .
- Generate the assembly language code for the program with the command
and inspect the resulting file race.s. Search
for unsafeInc:, the entry point point of the function
that increments the counter. You will observe that the body of
the function loads the variable (the address of which is in
register %rdi) into register %rax, increments
this register, and stores the incremented value back into the
variable.
Now compile the program with the command
gcc -Wall -O -o race race.c -lpthread
Run the program with one thread, e.g. ./race 100000 1, and now
vary the number of threads from 1 to 4. Here, all threads run on a single
core. What do you observe this time?
Try a much longer run, e.g. ./race 10000000000 4;
do you observe any interference? Why might short runs see no interference
when longer ones do? (Hint: on sufficiently short runs, the
threads can complete within a single timeslice),
- Finally run the program in parallel, that is
with threads running on multiple cores
(say c = p = 2,3,4), e.g.
How much interference do you get now?
Comparing these results with that of the Java program,
would you expect that the Java threads are running on one core
or on several?