So let's look more closely at what is called cache-to-cache transfers,
which is what happens when core one has block B in a modified state,
and another core puts a read request on the bus, so
that it wants to basically bring the block in its own cache.
At this point, C1 has to react.
Because C1 is the one that has to provide the data,
if we have the block in the M state,
that means that the only up to date copy in the system is our copy.
Even the memory doesn't have an up to date copy.
So if the data is going to be sent to C2, it had better be our data, but how,
and we have briefly touched on this when we were discussing the inside protocol.
There are really two solutions.
The first solution is abort and retry.
The idea here is that C1 somehow cancels C2's request,
and for that, we need some sort of an abort bus signal,
that when our request is placed on the bus, another core can assert the abort
signal that tells the requesting core to kind of back off and try again later.
Now that C2's request has been aborted, C1 can do a normal write back to memory.
At this point, the memory has the up to date data, and
when C2 retries its read request, it will get the data from memory.
The problem with this approach is that really,
from the time C2 makes the request If this data
was normally coming from memory, we will have a memory latency,
so if this is just a read miss, we will have a memory latency.
But if it is a read miss where another core has the data,
we really have to have two memory latency's before C2 can get the data.
One for the write-back to memory to happen on C1, and then for
us to actually get the data from memory.
So the read miss from C1 has a latency that is twice the memory latency,
plus some.
So this is why this approach is not ideal.
The second solution is called intervention.
In this case, C1C is the read request is being made, and
it tells the memory somehow that C1 will supply the data instead of the memory.
So normally the memory would respond to a read miss request,
now C1 kind of tells memory that I will do that, and for
that we need an intervention bus signal through which C1 by
asserting the interventions signal, signals to the memory not to respond.
So the memory now doesn't respond with the data, but
C1 does, and now the memory must pick up the data that C1 was
responding with in order to put it in the appropriate memory block.
The reason the memory has to pick up the data is that once C1 responds
with the data, both C1 and C2 will have the block in the shared state.
So both of them will think that the block is not dirty, and that means that
if the memory doesn't get the data now, it will never get the fresh data.
So by picking up the data,
the memory ensures that it gets the data that will not be written back anymore.
So the disadvantage of this approach, is that it needs more complex hardware.
Now our cache needs to be able to kind of insert its data, into what would
normally be the memory sending data, and also the memory has to have enough
intelligence to pick up the data when a core is responding to an intervention
request, instead of just picking up the data when there is a write back request.
In modern processors we nowadays mostly use
a variant of an intervention approach.
And the reason is that, yes the hardware is slightly more complex, but
fancier snooping protocols actually eliminate the problem of memory picking
up the data, and the complexity to respond with the data is not too large.
In contrast, here the performance suffers.
So pretty much, between having a fancier protocol and having enough
transistors from Moore's law to actually add this capability to caches,
we pretty much can do this and not have to suffer in performance like this.