Author Topic: Which is 'Cleaner' and why?  (Read 1077 times)

Legacy_JediMindTrix

  • Sr. Member
  • ****
  • Posts: 383
  • Karma: +0/-0
Which is 'Cleaner' and why?
« on: May 20, 2012, 02:40:44 pm »


               Just a quick question '<img'>
Which out of these two scripts is the cleaner?

By clean, I mean, which would theoritically use less system resources out of these two and why.

]void main()
{

object oPC = GetEnteringObject();
int DoOnce = GetLocalInt(oPC, GetTag(OBJECT_SELF));
if (DoOnce==TRUE) return;
SetLocalInt(oPC, GetTag(OBJECT_SELF), TRUE);
AssignCommand(oPC, ActionSpeakString("Looks like someone left the door open for us."));
}


void main()
{
int DoOnce = GetLocalInt(GetEnteringObject(), GetTag(OBJECT_SELF));
if (DoOnce==TRUE) return;
SetLocalInt(GetEnteringObject(), GetTag(OBJECT_SELF), TRUE);
AssignCommand(GetEnteringObject(), ActionSpeakString("Looks like someone left the door open for us."));
}


               
               

               


                     Modifié par NineCoronas2021, 20 mai 2012 - 01:41 .
                     
                  


            

Legacy_henesua

  • Hero Member
  • *****
  • Posts: 6519
  • Karma: +0/-0
Which is 'Cleaner' and why?
« Reply #1 on: May 20, 2012, 03:06:27 pm »


               The top one appears to have less methods and should be more efficient.

So you should also replace GetTag(OBJECT_SELF) with sTag
and add at the top
string sTag = GetTag(OBJECT_SELF);
               
               

               
            

Legacy_Shadooow

  • Hero Member
  • *****
  • Posts: 7698
  • Karma: +0/-0
Which is 'Cleaner' and why?
« Reply #2 on: May 20, 2012, 03:10:53 pm »


               To answer you, the difference is insignificant. At least in such script. The difference in scripts is almost always invisible. One could use profiler to see how long takes script to be performed but results are not realiable as it depends on CPU extension and other scripts performed at the same time (yes while one script is being performed, other are executed). In the end its more important how readable the script is. Ive said it numerous times: scripters here are extraordinary obsessed with efficiency, while they doesn't care about default bioware scripts - spells, AI that runs much more often and are absolutely inefficient.

Few tips however:

- its a good practice to define anything used more than once to variable.
- compare true == true is needless, simply use if(DoOnce) which is same as if(DoOnce != 0) (its actually not equal to DoOnce == TRUE as TRUE is constant of value 1 while the compiler TRUE - that is if(bool) is true everytime value is not false.

This is how would I wrote it.

void main()
{
object oPC = GetEnteringObject();
string sTag = GetTag(OBJECT_SELF);
if (GetLocalInt(oPC, sTag)) return;
SetLocalInt(oPC, sTag, TRUE);
AssignCommand(oPC, ActionSpeakString("Looks like someone left the door open for us."));
}


               
               

               
            

Legacy__Guile

  • Hero Member
  • *****
  • Posts: 1308
  • Karma: +0/-0
Which is 'Cleaner' and why?
« Reply #3 on: May 20, 2012, 03:29:43 pm »


               void main()
{
  object oPC = GetEnteringObject();
  if(!GetIsPC(oPC)) return;
  string sTag = GetTag(OBJECT_SELF);
  if (GetLocalInt(oPC, sTag)) return;
  SetLocalInt(oPC, sTag, TRUE);
  AssignCommand(oPC, ActionSpeakString("Looks like someone left the door open for us."));
}

//Most of the time you would only want this script to run on the PC (not Associates)..., therefore I added the if(!GetIsPC(oPC)) return;   Otherwise everyone entering the trigger would trigger the script... As oPC = anyone entering the trigger...
               
               

               


                     Modifié par _Guile, 20 mai 2012 - 02:31 .
                     
                  


            

Legacy_JediMindTrix

  • Sr. Member
  • ****
  • Posts: 383
  • Karma: +0/-0
Which is 'Cleaner' and why?
« Reply #4 on: May 20, 2012, 04:03:50 pm »


               Hmm, thank you, all of you, for the advice. That was written with lilac soul since, my skills are super rusty at the moment, heh.
               
               

               
            

Legacy_Axe_Murderer

  • Full Member
  • ***
  • Posts: 199
  • Karma: +0/-0
Which is 'Cleaner' and why?
« Reply #5 on: May 24, 2012, 06:22:06 am »


               System resources consist typically of two things when you are scripting:  memory space, and cpu cycles. There is almost always a trade-off between the two. Less cpu cycles leads to more memory use and vice-versa, within limits. Also you are often concerned with readability because it has much to do with maintainability (ease of change).

Second script invokes the function GetEnteringObject three times to compute the exact same thing every time. So the first script is more efficient in this regard because it calls the function once to compute it, then stores the result in a variable for use throughout the script wherever it is needed. So that it need not ever be computed again. The variable also adds readability to the script because, presumably, you know from its name and datatype what it stores. However, that script uses more memory since it must reserve (i.e. allocate from the system's pool of unused memory) a space to hold and remember the result of the function call. Memory allocation is not a zero cpu operation. Neither is storing something in it or retrieving something from it. It is significantly faster than recomputing things in virtually every situation however. So there is a balancing act in play in this example, as there is in most.

The determining factor here, I would say, is the frequency that the function is called. I don't imagine that GetEnteringObject is a particulary complex function in its implementation, although it being a core function I cannot be 100% sure when scripting. Still my expectation is that it runs to completion extreemly quickly, and practice betrays this to be true. It computes its result almost as fast as a simple memory read from a saved variable. There is some overhead in calling the function and communicating its parameters and result, but the code it runs when you do, in order to compute your request, is not significant. Calling it superfulously 2 times is no massive problem. Still, if the information was available in a variable, it would be several times cheaper to read it from there than to call the function to compute it again. Measurably cheaper, but not noticably cheaper in this case. If a memory read takes, say, 2 milliseconds, then several times that would be on the order of what? 20 milliseconds at worst maybe? Measurable, but not noticeable.
It could add up if the script was called very very often and then possibly become significant enough to be noticeable. So the question of efficiency becomes "How frequently must the script run before it becomes noticeable?" Something you can only answer by experimenting. I am guessing you will never be able to work up an experiment that runs the script frequently enough that the difference will become noticeable. Not in this particular case. Because GetEnteringObject is such an efficient core function. The difference between calling it and reading from a variable is awfully small. The gain is so small that the frequency would need to be higher than the game engine can produce. That's all just a guess though. But, now consider how different things would be if it was a custom function that was 500 lines long being called too often instead!

What catches my eye instead is this following bit, which is horribly wasteful (although again not noticeable), simply because it is unnecessary waste which accomplishes nothing:
[quote]
...
if( doOnce == TRUE ) return;
...
[/quote]
This is an inefficiency shared by both scripts. They are both making the double blunder of wasting both memory and cpu cycles in order to make this decision only once in the script. First off is the waste of memory. And remember, when you waste memory you are also forced into wasting cpu cycles because memory allocation, access, and release requires the cpu to be involved. If you did not need the memory, then you also did not need to waste the cpu cycles obtaining it, reading and writing to it, and finally giving it back.
In both these scripts, there is no need to save the result of the GetLocalInt function into a variable since it is only ever used in one place in the whole script. You don't need to remember it later in order to avoid calling a function to recompute it over and over again because it is only ever needed once in one place. So the doOnce variable is a waste of memory space and cpu time allocating space for it and later releasing that space when the script is done. All to remember something that will be used in the very next line of code and nowhere else. This waste can be resolved by eliminating the variable and calling the function directly in the if-statement's conditional clause...
[quote]
void main()
{ object oPC = GetEnteringObject();
  if( GetLocalInt( oPC, GetTag( OBJECT_SELF )) == TRUE ) return;
...
[/quote]
That's better memory-wise for sure. Although it is harder to read...isn't as clear what is being tested. If readability was important, I would recommend wasting the memory in this case because the effect is not noticeable, yet the improvement in readability is great. If however efficiency was critical, I would abandon the variable. I will also take an opportunity here to mention that readability can be greatly improved by using a wrapper function. Wrappers are cpu users, but they are very cheap. If you are trying to conserve memory, like in this case, you can get your lost readability back inexpensively by wrapping the GetLocalInt function in a wrapper with a name that makes sense. Say, HasDoneThisBefore for example. So you get this:
[quote]
int HasDoneThisBefore( object oPC )
{ return GetLocalInt( oPC, GetTag( OBJECT_SELF ));
}

void main()
{ object oPC = GetEnteringObject();
  if( HasDoneThisBefore( oPC ) == TRUE ) return;
...
[/quote]
This is far more readable, doesn't waste the memory on a doOnce variable, but is minutely more costly cpu-wise than the more unreadable alternative. Because this DoOnce operation/decision is very common in NWN, the usefulness of creating such a wrapper and throwing it into a library which you can use throughout your module far outweighs the tiny cpu hit you will get from calling it. Because you will be using it to save on wasted memory, along with its inherent cpu use, you will probably break close to even cpu-wise anyway by doing it. Thus the net result is saved memory...little to no change in cpu usage...perhaps even an improvement in readability. A win because you get to avoid, mostly, the typical trade off (memory vs cpu).

Now let's talk about the cpu waste. It involves testing anything against TRUE. It is almost always pointless to do it. In this case, a variable, which is a boolean value (i.e. true/false, yes/no, etc.) drives the decision making. What is stored there is a value TRUE or FALSE. The if-statement's conditional clause, the stuff between the parens, must always evaluate at run-time to a boolean value. It must represent a formula which computes down to a true or false value at run-time. The function GetLocalInt, being used on a variable which is only ever given the value TRUE or FALSE, will therefore, on its own, just by viritue of calling it while knowing it will return a boolean value, resolve to the boolean value required by the if-statement at run-time.
Now if you have any boolean value, and you compare to see if it equals the value TRUE, you are creating a boolean expression. The value of the expression, the equality test, will be used to drive the decision made by the if-statement. But by testing against TRUE, you will find that the value of the resolved expression will always be the same as the value of the thing you are testing for truth. So if you ask "Is X == TRUE?", where X is known to be a boolean value, you will see that the answer to that question will always match the value of X. If X is set to the value of TRUE, then the expression will be computed to mean TRUE at run-time. If X is set to FALSE, the expression will likewise become FALSE at run-time. So why waste the cpu time making the comparison? already represents the answer you're looking for!

It is very similar logically to multiplying or dividing by 1, adding or subtracting by 0. Here's an example to make it even clearer. Which of these statements would you expect to use more cpu?
[quote]
int X = 7;
int Y = 7 * 1;
int Z = (3 * 1) + (4 * 1) + 0;
[/quote]
The second one has to perform a multiplication before it knows what value to put at memory location Y, the first one does not. Yet they do exactly the same thing. You would never write a line of code that looks like that second one because it obviously makes no sense to waste cpu like that. And the third one is nothing short of absurd. It's the same thing when, in the boolean world, you say == TRUE. By writing it explicitly in there, you are forcing the script to perform a useless test, using cpu cycles, in order to compute a value you already know. There is no reason to ever ask "Is X == TRUE" when you can ask "Is X" and obtain the same answer.

Combining this revelation with the wrapper notion used to save the memory and improve readability/maintainability, it becomes quite obvious what the best way to go with in this case in order to save memory, minimize cpu, and maintain readability, which are the ultimate goals of scripting 'efficiently':

1. Make a library script for the code which accomplishes commonly needed tasks.
[quote]
// My Library script named mylib.nss
//

// int HasDoneThisBefore( object oPC )
//   Determines if a given PC has interacted with OBJECT_SELF before.
int HasDoneThisBefore( object oPC );
int HasDoneThisBefore( object oPC )
{ return GetLocalInt( oPC, GetTag( OBJECT_SELF ));
}

// void SayThisOnce( object oPC, string sObservation )
//   Causes a given PC to make an observation verbally unless he has done it
//   already once before by interacting with OBJECT_SELF some time earlier.
void SayThisOnce( object oPC, string sObservation );
void SayThisOnce( object oPC, string sObservation )
{ if( GetLocalInt( oPC, GetTag( OBJECT_SELF )) || (sObservation == "") ) return;
  SetLocalInt( oPC, GetTag( OBJECT_SELF ), TRUE );
  AssignCommand( oPC, ActionSpeakString( sObservation ));
}
[/quote]

2. Use it in all your main scripts.
[quote]
#include "mylib"

void main()
{ SayThisOnce( GetEnteringObject(), "Looks like someone left the door open." );
}
[/quote]
Look how readable the main script has become!!! It practically screams "Make the entering creature say this but only once." And it does it in only one line instead of five. No oPC variable in there cluttering things up anymore either. Notice also that neither library function is required to explicitly create a variable in order to do its job!!! That's all handled by function parameters now. Parameters require memory just like variables. You aren't saving any memory by using them like I have here. It's just handled automatically behind the scenes for you and the explicit reference which causes the allocation/release is in the function header instead of directly in the code all over the place. It would not surprise me if parameter memory was managed a tad more efficiently than explicit variables are since parameters are typically combined into a single block of memory for ease of handling in virtually every modern programming language. However, parameter memory is allocated and released every time you call the function while explicit locals are allocated and released only once each time the script is called. It makes sense when the code is commonly used to do it this way. If it isn't common, a library is a waste of time and effort. In that case add all your efficiency and readability directly in the script as best you can. Adding functions local within the main script can still be quite useful, in a like manner to this example, when the code is not common, especially to improve readability and therefore maintainability.

Notice I did not use HasDoneThisBefore inside SayThisOnce. I could have written it this way to be more readable:
[quote]
void SayThisOnce( object oPC, string sObservation )
{ if( HasDoneThisBefore( oPC ) || (sObservation == "") ) return;
  SetLocalInt( oPC, GetTag( OBJECT_SELF ), TRUE );
  AssignCommand( oPC, ActionSpeakString( sObservation ));
}
[/quote]
So why didn't I? Well, functions in libraries ought to be ultra efficient. As efficient as possible. Maximize efficiency in library functions at the expense of readability. The whole purpose of having a library is to centralize commonly needed code. Since it is common, it has a great impact on the whole of the resulting system. So efficiency is paramount over readability in a library. The names of functions and their parameters that the library provides is where the readability comes from. You don't gain the advantage until you use the library. The objective is to make all main scripts very efficient, readable, and simpler to write by transferring the more complex code into a centralized location where it can be made very efficient without concern for readability. Also by centralizing common things, you increase mantainability because when a change is called for, you need only do it in one place and the whole system gets changed. If the code is common but scattered about (copied) in 60 different scripts, then to change the entire system means changing all 60 places. And that requires remembering that there are 60 places to do it, as well as where all those 60 places are. And then, additionally, physically make the exact same change correctly 60 times in a row. I've never met a human who can do that. So by not calling HasDoneThisBefore within the other library function, I am not wasting the overhead of a function call for the only reason to improve readability. It's a library and that's not what it is there for. HasDoneThisBefore is simply included for use if ever some script needs it independently of SayThisOnce.

Of course, in practice, most scripts use oPC all over the place so you won't be able to get away with what I've done here with that variable every time. It will be more frequent that you will want an explicit local variable for that one, among others. You might also note that by making the SayThisOnce code into a general purpose function, it 'required' (optionally) an additional test to ensure that should it be called with an empty string to speak, it would bail out and do nothing. This isn't absolutely necessary, and you could take it out if you didn't mind the function forcing a player say a blank line when told to do so, but it seemed like a reasonable safety check in this instance. And it illustrates also that overuse of functions can actually lead to widespread inefficiencies if you aren't careful. Testing for empty speech, which was never done before, wasn't necessary, is now being done everywhere at a cpu cost every time, because by making it into a general purpose function it suddenly became possible to have a blank statement to say. Another trade-off. Programming is always about trade-offs. Again, not even noticable in this example no matter what you choose. At least if you decide to change it though, you only need to do it in one place.

Maintainability is sometimes a bigger deal than efficiency is. And you usually trade a little efficiency to get it. If you're doing an online world it is an absolute necessity. A script that gets called infrequently but gets upgraded, morphed, and modified regularly needs to be readable more than it needs to be efficient. Concentrate your efficiency efforts on scripts that run frequently and never need changing once you get em working. Be most careful with scripts that run frequently and also need to be changed a lot. The end objective is to get the project completed. Tweaking things to make them more efficient but that nobody will ever notice does not get you there. Planning ahead using things like library functions is a much wiser use of time. They give you efficient customized tools, ready to use in every main script you tackle, that improve readability and therefore maintainability, so you need not worry nearly as much about efficiency when writing the main scripts. That allows you to concentrate on getting on with the project objectives when authoriing the main scripts, while still having confidence it will work right, remain reasonably efficient, and be easy to change when you're all done.

When planning ahead, or even when just starting on a new script, two great questions to ask yourself are "What things do I want to happen?" and "What information will I need to know in order to make this happen?". The answers will usually lead you to identify custom functions that you can create to gather that information efficiently or to cause something to happen (like an action sequence for example). And if it is commonly needed information, or a commonly needed sequence, those functions can be leveraged in a library for easy reuse throughout the module.

If you're just looking for the thinnest way to do this without regard for reuse or readability. I'd say this is probably it:
[quote]
void main()
{ object oPC = GetEnteringObject();
  string sTag = GetTag( OBJECT_SELF );
  if( GetLocalInt( oPC, sTag)) return;
  SetLocalInt( oPC, sTag, TRUE );
  AssignCommand( oPC, SpeakString( "They left door open." ));
}
[/quote]

Which brings up an interesting situation. The TAG of OBJECT_SELF is determined by calling GetTag twice in two different spots. However, depending on the variable's value the second call may or may not happen when the script is called because it will be bypassed by the if-statement sometimes. So, a decision to make. Do I put it into a variable to save having to call the GetTag function twice, when that situation occurs, or do I leave in the multiple function calls to avoid using a variable I might not need? The answer is easy in this case. What condition do you expect to see most often when the script is called? Well, the first time the script is called the condition is FALSE and it gets set to TRUE. That's also when the GetTag function gets called twice. Every other time the script is called from that point onward, the condition will be TRUE and the second GetTag call will be bypassed by the if-statement. So the best solution is to not concern yourself with the extra function call. Because it will only happen once per PC per OBJECT_SELF. What I've done above is actually worse than calling the function twice because of how the script is expected to work during play. If the reverse had been true and you wanted him to speak his line every time except the first time, so the typical scenario would be to call the function twice, then the variable would be technically I think the best way to go. And similarly for the oPC variable. Memory isn't as valuable as CPU most of the time today.

Therefore this is it:
[quote]
void main()
{ if( GetLocalInt( GetEnteringObject(), GetTag( OBJECT_SELF ))) return;
  object oPC = GetEnteringObject();
  SetLocalInt( oPC, GetTag( OBJECT_SELF ), TRUE );
  AssignCommand( oPC, SpeakString( "They left door open." ));
}
[/quote]

               
               

               


                     Modifié par Axe_Murderer, 24 mai 2012 - 03:47 .
                     
                  


            

Legacy_Morbane

  • Jr. Member
  • **
  • Posts: 79
  • Karma: +0/-0
Which is 'Cleaner' and why?
« Reply #6 on: May 24, 2012, 08:54:59 am »


               Efficiency?
Quote

(A code example would have saved a lot of time)
Quote
               
               

               
            

Legacy_meaglyn

  • Hero Member
  • *****
  • Posts: 1451
  • Karma: +0/-0
Which is 'Cleaner' and why?
« Reply #7 on: May 24, 2012, 03:45:56 pm »


               Very nice write up Axe_Murderer. Most of that applies to any kind of programming not just nwscripting too.

I have one nit with your examples with numbers (int X = 7 etc...). Using any reasonably competent compiler all three of those statements would use exactly the same amount of CPU. I don't know if the nwscript compiler is that competent though... '<img'>
               
               

               
            

Legacy_Axe_Murderer

  • Full Member
  • ***
  • Posts: 199
  • Karma: +0/-0
Which is 'Cleaner' and why?
« Reply #8 on: May 24, 2012, 05:28:45 pm »


               That's a good point if you're using a full blown language. But, yeah the default compiler that came with the game does not do much optimization. Some of the 3rd party ones out do a better job of that. Since I never checked it out that deeply, and I know NWScript is a pretty restrictive limited form of C, I always took the approach that it was safer to assume the worse and program to avoid the need for sophistication in the compiler. At least those places where it is easy and trivial to do it.

For example, I know that constant expressions are not considered constant in NWN. This will not compile:
[quote]
const int START = 7;
const int START_PLUS_ONE = START +1;
[/quote]
NWN forces you to make START_PLUS_ONE a variable if you want to write that. Now if it can't deal with a simple constant expression, I'd be surprised to learn it was optimizing those lines you pointed to. Maybe since they are using only immediate values, no name references, it does.

In those specific lines I just don't know for sure if they'd be optimized to the same thing or not. Maybe they are. Its possible I suppose. But I can tell you I am not nearly confident enough about it to recklessly write sloppy code like that either. In the hopes that the compiler will be smart enough to sort it all out for me. The point being the necessity of risking it is, or ought to be if you are interested in learning how to write 'efficient' code, nonexistant. Because you don't have to and it is trivial and wise to avoid. Ain't no jestifyin' usin' a crutch wen yo' legz not broke, even tow yo' could if'n yo' wanted to.

And, naturally, it will compile faster if no optimization need be performed. Even on a fancy one. So that's another plus. Good point tho.
               
               

               


                     Modifié par Axe_Murderer, 24 mai 2012 - 05:33 .
                     
                  


            

Legacy_meaglyn

  • Hero Member
  • *****
  • Posts: 1451
  • Karma: +0/-0
Which is 'Cleaner' and why?
« Reply #9 on: May 24, 2012, 05:47:23 pm »


               Indeed, I was not arguing in favor of writing sloppy code. The maintenance aspect alone makes it worth writing good clean code. I've been pulling in all these parts of systems while building a module
and find myself needing to rewrite much of it. (Your KSO stuff being one of the happy exceptions btw).
               
               

               
            

Legacy_Lightfoot8

  • Hero Member
  • *****
  • Posts: 4797
  • Karma: +0/-0
Which is 'Cleaner' and why?
« Reply #10 on: May 24, 2012, 09:44:37 pm »


               

meaglyn wrote...

Very nice write up Axe_Murderer. Most of that applies to any kind of programming not just nwscripting too.

I have one nit with your examples with numbers (int X = 7 etc...). Using any reasonably competent compiler all three of those statements would use exactly the same amount of CPU. I don't know if the nwscript compiler is that competent though... '<img'>


Axe is 100% correct in what he said here.  Your guess that the NWCompiler is not competent is also correct.  The boiware compiler would insert the VMCode to calculate out the result every time the script was ran.   Sadly enough I do not think the compiler even know how to add to do it for you.  
               
               

               
            

Legacy_BelowTheBelt

  • Hero Member
  • *****
  • Posts: 699
  • Karma: +0/-0
Which is 'Cleaner' and why?
« Reply #11 on: May 25, 2012, 03:47:49 pm »


               Great.  Axe just ruined my whole Memorial Day weekend.  I now have to spend the whole holiday pouring through my scripts looking for wasted CPU cycles and memory hogs...

Seriously, though, excellent response to a great question and the topic deserves continuation for those of us self-taught scripters.

I would suggest this be made a sticky...or possibly moved into another thread about scrpting best practices.  This info shouldn't be lost.  

Lastly, can someone explain structs/arrays (I know NWN doesn't do arrays, whatever they are)?  I'd lke to understand their benefit, how to use them, and how to simulate them.  The Lexicon and other posts I've read haven't been very clear to me.

Thanks!
               
               

               
            

Legacy_acomputerdood

  • Sr. Member
  • ****
  • Posts: 378
  • Karma: +0/-0
Which is 'Cleaner' and why?
« Reply #12 on: May 25, 2012, 07:44:08 pm »


               http://en.wikipedia...._data_structure

or if you have the time, this seems to be a decent video series:


data structures are one of the most common and fundamental topics covered in any computer science curriculum.  (note i said "computer science", not "computer programming").

what i wouldn't give for proper data structues in nwscript.  the closest i've gotten to an array is setting a bunch of local variables (VAR_1, VAR_2, .... VAR_N) and then keeping N as another variable.  then i can index anywhere from 1-N.

though i now see that structs are available:
http://nwn.wikia.com...ction_to_struct

so it stands to reason that you could implement a linked list to approximate an array.  though i doubt there is a pointer type object.  still seems possible.


and hopefully you were kidding about pouring over your code.  while Axe's post is fantastic, and right on a lot of points, you're unlikely to find any appreciable speedup.  and while i disagree fundamentally with the following statement (i myself am ultra conservative about efficiency in my code), the reality is that computers are fast enough these days to handle the wasted cycles.

as my embeded systems professor told us in school: "we used to write code at the assembly level to acheive the cleanest, fasted code we could.  and what did the GUI guys do with those extra cycles?  they showed a little piece of paper floating over from one folder to another.".

but i digress.  however trite the above example is, i believe it is still important to write good code.  though if you want to acheive any meaningful results, i'd first start with a profiler (http://www.nwnx.org/...id=doc_profiler) to monitor the module for a few days to find out where it's actually spending its time.

that way you know what to concentrate on.
               
               

               


                     Modifié par acomputerdood, 25 mai 2012 - 06:50 .
                     
                  


            

Legacy_JediMindTrix

  • Sr. Member
  • ****
  • Posts: 383
  • Karma: +0/-0
Which is 'Cleaner' and why?
« Reply #13 on: May 28, 2012, 02:09:15 am »


               I can tell a lot of time went into that response, and it helped quite a bit, though I admit some of it was over my head.

I love the idea of creating a library of wrappers for commonly performed tasks for my module. So to that end, I have a few more questions with the goal of better understanding what is happening in some of your examples and how to implement a library.

First few questions:

[code]
int HasDoneThisBefore( object oPC );
int HasDoneThisBefore( object oPC )

void SayThisOnce( object oPC, string sObservation );
void SayThisOnce( object oPC, string sObservation )
{ if( GetLocalInt( oPC, GetTag( OBJECT_SELF )) || (sObservation == "") ) return;
  SetLocalInt( oPC, GetTag( OBJECT_SELF ), TRUE );
  AssignCommand( oPC, ActionSpeakString( sObservation ));
}[/code][/quote]

1) Why are "int" and "void" lines repeated like that with the semi-colon the only difference?
2) What effect does "||" have in the if-clause? Why are some other conceivable ways I could use it? (just trying to wrap my mind around it, no pun intended)

[quote][code]{ return GetLocalInt( oPC, GetTag( OBJECT_SELF ));
}

3) Why is 'return' at the beginning of this line? What effect does that have as opposed to have it at the end?
               
               

               


                     Modifié par NineCoronas2021, 28 mai 2012 - 01:09 .
                     
                  


            

Baaleos

  • Administrator
  • Hero Member
  • *****
  • Posts: 1916
  • Karma: +0/-0
Which is 'Cleaner' and why?
« Reply #14 on: May 28, 2012, 02:50:09 am »


               I havent read all the replies but yeah
Basically if you can re-use already defined objects, then that is better.

eg- Why call the GetEnteringObject() function, 10 times, when you could just call it once, and reference the same object the other 9 times.

What some people forget is that when the script is compiled, its file size directly links to the complexity of the functions being used.
Eg - imagine you were calling a function from an include - and this function was like 300 lines long.
It doesnt store a call to the function in your compiled script, it actually rips the function out of the include, and embeds a static copy of it, within the void using script (non include).

This being the case, if you were to call such a function 10 times, it would be duplicated 10 times in the compiled script, which could increase file size of the script.

This being the case, recalling functions like GetEnteringObject- albeit being small functions, or so we would think, would have at least a few lines of code in the background, being added to your own script per call.

Referencing the already existing oPC Object, would remove the need for the compiler to add the extra lines of code.


In answer to your questions

1. int and void lines ended with a ;    are called function prototypes.
In nwnscript, you may have noticed that if you try to call function 1 from inside function 2, you will have to have function 1 being defined above function 2. Otherwise the compiler doesnt know where Function 1 is - it compiles from top down.
Prototypes allow you to move the single line prototype of the function, to the top of the script, without needing to move the contents.
The below example wouldnt work without the prototype

int MyNumber();  // < prototype

void MyFunction()
{
 MessageBox(MyNumber());
}

int MyNumber()
{
   return 1;
}



2. || is another way of saying OR
   && is  AND  and || is OR

3. if we know that the local int is going to be the return value, and we arent going to do any processing on it, then it is easier to return it straight from the GetLocal function, instead of taking the time to assign it to a variable. Variables do take up a little bit of memory - so this kinda saves memory - albeit its not that important to do so in this example.
You can assign to a variable if you want, this just saves you time and effort.