Hello, this is Dr. Miller, and this
is Episode nine point two of
Assembly.
Today, we're going to talk about
pipelines, the order of execution
and conditional execution.
Pipelines.
So pipelines are what allows a
multiple instructions to be in
different stages of processing
all at the same time,
and this is important because things
like fetching and storing are going
to be much slower.
So RAM is several
orders of magnitude slower than the
processor and the hard drive in
network or even slower than that.
So here are some example times.
So, for example, a register takes
point four nanoseconds to access,
whereas RAM takes over 100
nanoseconds.
So we're going to have well over 100
clock cycles before RAM
is going to be able to access
something.
And then even if you pick some of
the fastest SSD and Vme
SSD, those are twenty
five thousand nanoseconds.
And so, again, orders of
magnitude difference in all of
these.
And so pipelines help us to
optimize our processors.
And you can see that this is
something that we've had processors
have done for quite some time,
so, for example, with Penny Amphoras
and Penny LMDs, the number of
pipeline stages they have are
between 20
and 30. And so it's something that
processors have been doing for quite
some time.
So here's what happens when we have
sort of normal execution and we
don't have a pipeline.
So we can kind of see here
in the diagram we fetch an
instruction, then the next cycle we
decode it, then we execute it,
and then we write the data.
Then we fetch our next instruction,
decode, execute and
then write back either data
or Flagg's.
And so when we have something like
pipelining, we can see here that
while we're fetching one
instruction, we do that
and then we will decode that
instruction at the same time, will
fetch the next instruction.
And then while we are fetching this
instruction, we're decoding
this instruction. We're executing
this one.
And so, as we can see, if we
look at this eight nanoseconds
versus eight nanoseconds,
we can see that here.
We got two instructions done.
And here we ended up getting four
or five, depending on if you're at
eight, eight nanoseconds or nine
nanoseconds.
And so we can see that
there is a benefit to that where
we're getting more instructions
executed in the same amount
of time if we add pipelines.
And so the thing with branching
or jumping is the case.
We may have
the this will cause the processor
to flush its pipeline.
So as it's going, if this
this instruction does
a branch, then the next
instruction or all these
instructions in the pipeline are
going to have to be gotten rid of
because they are not correct.
So what we have is we have what
modern processors do and they
speculate about which branch is
going to be taken and then they'll
actually
change the order of execution
additionally.
So there are a couple of things that
our processor is doing.
So for speculative execution,
basically we have two different
types.
We have the Eger where we basically
try and do both branches and then
whichever one is wrong, then we undo
all of those changes or don't commit
those.
And then we also have predictive
predictive is basically
the processor has a
processor within it that is trying
to predict which branch has taken
the most often.
And then it will go ahead and
do that prediction and
guess and then if it's wrong,
it has to backtrack.
But if it's right, then it
just goes through the pipeline and
it speeds up our execution
out of order execution.
So one of the things the processors
also do is if we think
about just in order processor
refection instruction, if it's
available or if it's in
on the CPU, then we continue.
Otherwise, we basically have to wait
until we can retrieve that
data from RAM.
And so this fetch can take a long
time and then we go
ahead and execute it and then store
the results.
But what happens is with modern
processors, what they do
is they determine which instructions
depend on which other ones.
And so if they keep track
of things like what instructions
it's already fetched, what stores
it's done, the results of each
instruction and then dependencies
as to which instruction is dependent
on another one, then basically
we can do out of order execution.
And so without out of order
execution, we go ahead and fetch an
instruction.
We add it to the queue of things
that can be executed
and then you check the dependencies.
And so if one register depends
on another one, then that
instruction is going to have to
wait. But if two
instructions are independent of each
other, then basically you can do
either one and either either order.
And so if you are waiting on
dependencies when those are finally
met or when all the instructions
that precede this instruction
that it requires of met, then we
go ahead and continue.
And then we can
execute instore the instructions.
So conditional instructions.
So with conditional instructions,
they basically have the ability to
execute an instruction based on
flags, so either do the
instruction or not, depending on
which flags are set,
so often we do something like
compare and then we might move some
data depending
on the result of that comparison.
And we've done this using jumps, so
compares and jumps.
But basically we are able to
reduce branching and increase
efficiency. If we can
go ahead and do this without having
to do
jumps, which again are going to be
inefficient as far as our processor.
So a conditional move.
So this is going to check the state
of the E flags or the set
of flags that we have on the
processor, and it's only
going to do the move.
So the C part stands for conditional
and then move and then we have the
condition and so it'll only
move it. The flag is set.
And again, we have the typical
we have our mnemonic, we have the
destination, we have the source.
And then this key part is the
condition.
So here are a couple of examples.
There's a whole list here, but we
have conditional move above, some
move if above.
So if we did a compare
and then the
part on the left is greater than
the part on the right, then we'll go
ahead and actually do the move from
the source to the destination.
Otherwise we won't actually do that.
Or you can do something like move
greater than or equal to or
move less than again, any
of these will only get executed if
the correct flag is set for
that condition, just like we did
with jumps.
So here are some examples, so
we're comparing yak's and SBX
and then we can say conditional move
above EDC.
And so we can see that if this one
is greater than this one, then we're
going to copy its value into ITCs.
And then you can see in this
example, we're doing vice versa.
So we have above and then below are
equal.
So then if this one is below or
equal, then we copy SBX into
IDEX and that's only if that
is is true.
So with these set
of three instructions, this compare
and then these two moves, one of
these moves will get executed and
the other one will not.
And then here's another compare and
then maybe a below are equal.
So those are some examples of
conditional moves.
So today, we talked about pipelines,
the order of execution and then
conditional execution.
Looking forward, we're going to take
our max of three numbers and we're
going to go ahead and use
conditional execution for that.
So here's our quiz wire, but
jumps bad for processers.
Because they cause a pipeline to be
flushed, which will take more time.
How do you copy data based on
Flagg's?
Use conditional instructions, for
example, the seed move
and then some condition.
If you have questions, you can
contact me at Miller, MJ,
at you and Keda, Edu, and you can
find me on Twitter at now has 30.