| |
Why is it correct?
- In a non-speculative setting there is no order in which the threads execute the CS
- Even if there is an order that must be enforced by the program itself
- In speculative synchronization some threads are considered safe (depends on time of arrival) and there is exactly one safe thread at a time in a CS
- The speculative threads behave as if they complete the CS in some order after the safe thread(s)
- A read from a thread (spec. or safe) after a write from another speculative thread to the same cache line triggers a squash
- It may not be correct to consume the speculative value
- Same applies to write after write
Performance concerns
- Maintaining a safe thread guarantees forward progress
- Otherwise if all were speculative, cross-thread races may repeatedly squash all of them
- False sharing?
- What if two bins of a hash table belong to the same cache line?
- Two threads are really not accessing the same address, but the speculative thread will still suffer from a squash
- Possible to maintain per-word speculative state
Speculative flags and barriers
- Speculative flags are easy to support
- Just continue past an unset flag in speculative mode
- The thread that sets the flag is always safe
- The thread(s) that read the flag will speculate
- Speculative barriers come for free
- Barriers use locks and flags
- However, since the critical section in a barrier accesses a counter, multiple threads venturing into the CS are guaranteed to have conflicts
- So just speculate on the flag and let the critical section be executed conventionally
Speculative flags and branch prediction
P0: A=1; flag=1;
P1: while (!flag); print A;
Assembly of P1’s code
Loop: lw register, flag_addr
beqz register, Loop
…
- What if I pass a hint via the compiler (say, a single bit in each branch instruction) to the branch predictor asking it to always predict not taken for this branch?
- Isn’t it achieving the same effect as speculative flag, but with a much simpler technique? No.
|