Some questions
Do all instructions have the same execution duration?
If not, how much of a difference is required to be significant?
In terms of machine code instructions ( which I assume we are discussing), there are probably significant differences in number depending on which nwscript functions is used (as your results suggest). Has anyone studied this before and shared results?
............
I know that your post implies these questions, ShaDoOoW. I wanted to clarify and amplify them because I am curious.
No all instructions do not have the same duration. I feel that Funky did a good job of showing that with the example of SetLocalint vs CreateObject.
How much of a difference is required to be significant? LoL I think we have been fighting to find an answer to that question since the first day I posted on the forums here. I doubt that we will come up with a concise answer in this thread over and above what we already have form the others.
I also agree that this thread is more about instruction count then it is about efficiency.
So when we talk about instruction count in NWN what are we talking about?
We are of course talking about VM instructions, Or Virtual Machine Instructions. The Virtual Machine being the part of the NWN engine that executes NWN compiled Scripts.
Every nwn scripting statement can take anywhere from 0 to a couple hundred VM instructions to execute. You my already be questioning how useful a statement that uses 0 VM instructions can be. Well outside of compiler directives that are not really statements, I can only think of one type of statement that has 0 VM instructions. That type is constant declarations,
example: const int nStable = 1;
But since this type of statement does not generate any code and can not be placed in a code block you can view it as a compiler directive if you like. After all it reacts more like a directive then a statement anyway.
Getting back on track.
Lets look at a script to see why there was no difference between shadows results using OBJECT_SELF vs a var with OBJECT_SELF assigned to it.
Here is a simple script that does, well nothing. It simply has everything striped away except what we want to look at.
void main()
{
object oSelf = OBJECT_SELF; oSelf; OBJECT_SELF;}
and here is the Compiled code generated from the above script, color coded to match the statement that generated it. ( decompiled with nwn explorer and trimed down to just what is needed,)
08 T 0000004D
0D JSR fn_00000015
13 RETN
15 RSADDO 17 CONSTO 00000000 1D CPDOWNSP FFFFFFF8, 0004 25 MOVSP FFFFFFFC 2B CPTOPSP FFFFFFFC, 0004
33 MOVSP FFFFFFFC 39 CONSTO 00000000
3F MOVSP FFFFFFFC 45 MOVSP FFFFFFFC 4B RETN
the numbers at the being of each line is simply the offset into the file in hex. The first 8 bytes that are simply not shown by NWN explorer discribe the file and are ".ncsv1.0 " the basic header to say that this is a .ncs version 1.0 file. Just trying to explaine why the nwn explorer shows all of the .ncs files starting at offset 0x08 instead of 0x00
line 0x08 just like the first 8 bytes is also not code or an VM instruction. It is simply states the size of the .ncs file. in this case the file is 0x4D bytes long. That includes the first 8 bytes not shown at the top all the way through the return that starts at 0x4B. The return instruction is two bytes long making the last byte in the file at 0x4C giving us a file that is 0x4D bytes long. ( 0x00 - 0x4C)
that brings us to the first VM instruction in the file at offset 0x0D.
0D JSR fn_00000015
With this first VM instruction being feed into the VM several thing happen. that make it necessary to quickly explain a couple parts of the VM. The first thing is the Instruction Pointer (IP) The IP points to the instruction to be executed by the VM. It would be pointing at 0x0d when it executed this instruction. any time an instruction is executed the IP is increased by the length of the instruction being executed, so that the IP will then point to the next instruction. So our IP is 0x0D before this instruction is executed. As soon as the JSR starts to execute the IP is increassed to 0X13 to point at the RETN instruction.
Boy going to be a quick script... well that bring me to the next part that I need to explain. The NWN VM has an array that is allocated as a Return stack. It holds the places for the IP to be restored to anytime a return is executed.
Ok, back to explaining this first instruction. JSR is the mnemonic(short hand) for Jump to SubRoutine.
00000015 is the operand ( parameter if you want to look at it as a function) that the JSR code is going to operate on. So we have a VM instruction that tells VM to jump to the instruction at offset 0x15. This instruction will add the current value of the IP to the return stack, The current value is 0x13 pointing at the RETN instruction since this instruction has already started executing. It will also move 0x15 into the IP so that the instruction at 0x15 will be the next instruction executed. And this by the way is the Jump into the "main" Function. just about every NWN script will start this way.
skipping the RETN at offset 0x13 for now since it is not being executed yet anyway. Simpler to just follow the course of execution. So our next instruction being executed is at. 0x15.
15 RSADDO By the color you can tell that this is part of the VM instructions for the
object oSelf = OBJECT_SELF; Statement. Well We have hit the time when I have to explain another Part of the VM in order to Have this statement make since.
VM Stack: A stack is basically a scratch pad for a program to store data in. It is called a stack because of the way the data is stored. when something is added to a stack it is viewed as throwing the data on top of the stack. when data is removed from the stack it is viewed as being pulled or Poped from the top of the stack. However since the nwn VM lacks the traditional Push and Pop commands that may not be a bit confusing. Lets just say the the stack consists of two things
1) a Data array of Dwords that the code uses to stores data .
2) a Stack Pointer (SP) that points to the top of the stack.
This brings us back to RSADDO:( Reserve Object Space on Stack) it is the code issued for the "object' part of our statement. It increases the stack pointer by 4 and typecasts this dword in the stack as an object data type. It also Gives the initial value of OBJECT_INVALID to the object. Anytime the label oSelf is used in our script it is this location, just now added to the stack, that will be accessed. the next instruction is:
17 CONSTO 00000000
This one is Place Constant Object ID Onto the Stack. This is the code issued bye "OBJECT_SELF". It will increase the Stack pointer by 4 and add the ID for OBJECT_SELF into the new top dword on the stack. At this point we have two dwords on the stack. The Stack pointer is pointing at the value on top, which is the ID of the object this script is running on. Under that is the Reserved dword for the oSelf object. .... next instruction.
1D CPDOWNSP FFFFFFF8, 0004
This instruction Is Copy Down Stack Pointer, It takes two operands The first one FFFFFFF8 ( or -8) is the location to copy the top of the stack to. The second on is how many bytes to copy off the top of the stack. So we are copying the top 4 bytes on the stack Top Dword into the location 8 bytes down in the stack. This is the dword just under the dword on top of the stack or the dword that has been reserved for our oSelf label. So the statement reads: Copy OBJECT_SELF constant just pushed onto the stack into the Dword reserved as the oSelf label. This by the way is the instruction that was issued by the "=" assignment operator. ....
25 MOVSP FFFFFFFC
Ok lets look at our stack before I give what this statement does. so far our stack has 2 dwords on it that has been added by the code. the bottom one is the dword that was reserved for the oSelf var currently holding the ID for the object that this script is running on. the top one also holds the same object ID from when the OBJECT_SELF constant was pushed onto the stack. well guess what, the Top byte was just scratch paper, We now hit the ; End of the statement, Clean up time. our VM instruction is Move SP FFFFFFFC or Move SP -4. This will decrease the SP pointer by 4 effectively removing the top dword from the stack. This leaves us with only one dword that this code has placed on the stack, That of our oSelf object.
oSelf is still on the stack because its Scope or name space has not yet ran out. As soon as it does it also will be removed. 2B CPTOPSP FFFFFFFC, 0004
Here we have the code that is given by the oSelf part of the next statement. it is Copy To Top of Stack. It has Two operands -4 and 4. So we have start at byte -4 from the top of the stack ( That would be the oSelf reserved dword) and copy 4 bytes to the top of the stack. This statement also increase the stack pointer by the number of bytes copied to the stack. So we once again have two dwords on the stack that we have added. and oSelf has been placed on the top of the stack for use. Well guess what we dont use it for anything!!!
33 MOVSP FFFFFFFC
; end of statement : cleanup! Decrease SP by 4 removing that copy of oSelf we copied to the top of the stack for use. 39 CONSTO 00000000 3F MOVSP FFFFFFFC Here is the OBJECT_SELF: statement. pretty much the same as above. Just instead of moving (Copying ) the oSelf from a stack location to the top of the stack we are pushing a constant onto the stack and again doing nothing with it and removing it. 45 MOVSP FFFFFFFC
Well here we are at last at the closing braked ')' for our main function. Sadly this is the end of the scope for our oSelf var it just has no meaning after this. Yep that is right decrease the SP by another 4 removing oself from the stack. 4B RETN
That closing bracket also gives an implied Return statement. This statement will simply load the IP with the value last placed into the return array. Of course it will also remove that value from the array. If you remember from above that value was 0x13, Therefore the next instruction executed is.
13 RETN
Poping yet another return value off the return stack and letting execution to return to whatever code started running this script to begin with.
well it is getting late and I need to wrap this up. Just to summarize Both OBJECT_SELF and an Object Var will use two VM instructions every time they are used. setting OBJECT_SELF to a var has an extra set up over head of 5 VM instructions for assignment and destruction.
Really not Much of an over head. but you never know when that 5 instructions may make a difference. lol
Hope I did not ramble to much.
Heading off for some rest now.
L8