Jump to content

Future inefficiencies in design


Mollymews
 Share

You are about to reply to a thread that has been inactive for 1024 days.

Please take a moment to consider if this thread is worth bumping.

Recommended Posts

this is a thought of the week thing

how design decisions can cause future code inefficiencies which can't be eliminated without breaking existing content

in the Scripting Library I posted a code which contains this:

return (string)carry + result;

then in a subsequent code it goes:

integer i;
for (i = 1; i < 9; i++)

had I designed it as:

return result + (string)carry;

then

integer i;
for (i; i < 8; i++)

which is more efficient as the assignment i = 1 would not be necessary, saving some bytecode instructions

this came about because I didn't contemplate a future where a rolled version of the code could co-exist with a previously designed unrolled version   

 

other designs can sometimes be made more efficient without breaking existing content

in another code post I wrote:

ordb += carry;
result = (string)(10 * (orda < ordb) + orda - ordb) + result;
carry = (orda < ordb);

when it can/should be written:

ordb += carry;
carry = (orda < ordb);
result = (string)(10 * carry + orda - ordb) + result;

containing one (orda < ordb) which is more efficient and can be recoded this way and not break existing content

 

so the thought of the week is to give the future a bit more contemplation than we might be doing

 

Link to comment
Share on other sites

16 minutes ago, Mollymews said:

for (i; i < 8; i++)

can't this just be:

for(;i<8;i++)

also, if there's no specific reason to do things in the forward direction, it should be a bit more efficient to loop backwards rather forwards
 

integer i = 9;
do
{   // something
}while(--i);

 

Link to comment
Share on other sites

1 minute ago, Quistess Alpha said:

if there's no specific reason to do things in the forward direction, it should be a bit more efficient to loop backwards rather forwards

 

integer i = 9;
do
{   // something
}while(--i);

 

there was a big chat about this in another post on here

it turned out that with LSL Mono is more efficient to use addition in loops rather than subtraction

from this then the most efficient Do While loop in this case would be

integer i = -8; 
do {  
   ... 
} while (i++);

 

  • Thanks 1
Link to comment
Share on other sites

37 minutes ago, Quistess Alpha said:

While we're micro-optimising, does ++i vs i++ make much difference?

++i is more efficient than i++. Pedro Oval's CIL byte code dump shows this

http://wiki.secondlife.com/wiki/User:Pedro_Oval/Mono_code_memory_usage/CIL

// ++x;
11 00 ldloc.s 0
17    ldc.i4.1
58    add
25    dup
13 00 stloc.s 0
26    pop

// x++;
11 00 ldloc.s 0
11 00 ldloc.s 0
17    ldc.i4.1
58    add
25    dup
13 00 stloc.s 0
26    pop
26    pop

this is because x++ makes x available then x+1 is made available
 

x = 1;
while (x++)
{
  x is 1 on the first loop  
}

x = 1;
while (++x)
{
  x is 2 on the first loop
}

is the same with --x vs x--. --x is more efficient

// --x;
11 00 ldloc.s 0
17    ldc.i4.1
59    sub
25    dup
13 00 stloc.s 0
26    pop

// x--;
11 00 ldloc.s 0
11 00 ldloc.s 0
17    ldc.i4.1
59    sub
25    dup
13 00 stloc.s 0
26    pop
26    pop

 

we can see from this why ++ is more efficient than --

58 add on typical hardware processors use less instructions than does 59 sub 

 

 

  • Like 1
Link to comment
Share on other sites

 ended up contradicting my ownself 😺

the most efficient Do While in the case mentioned would be

integer i = -8;
do
{
  ...

} while(++i);


 
like my motobike, it can take me a while to get where I want/need to be.  My motobike started at version 0.01 and is now at version 1.24.  124 edits later and I am still not happy with it. Is one of the reasons why I don't make stuff commercially. People still be waiting for the product like 10 years after the promo advert came out 😸

  • Like 1
Link to comment
Share on other sites

There's an overlap with another topic here about porting 32-bt code to 64-bit.

Somebody codes

integer ii = 1;

while(ii++ < 0) {/* do stuff like check ii is prime or whatever */}

They're happy with the result which is nice and terse and will go through all the positive integers. Very efficient. Saves testing for an upper value. (testing if something is or ain't 0 is typically optimised to a register operation). You'd see it used when checking for primes or examining memory.

Recompile that on a 64-bit system where integers by default are 64 bit and see how long it takes to complete.

I have an intrinsic distrust of terse code for the very same reason I distrust the macros in c, what you read in the source isn't what gets given to the compiler.

Sadly, with out 64K memory limit in LSL, we're pushed towards trying to do the same compact codings that caused early programmers to develop thhis memory-saving tricks...

Edited by Profaitchikenz Haiku
Link to comment
Share on other sites

2 hours ago, Profaitchikenz Haiku said:

Sadly, with out 64K memory limit in LSL, we're pushed towards trying to do the same compact codings that caused early programmers to develop thhis memory-saving tricks...

I think that we have a human tendency to expand and fill the space that is available. Not all of us, but a significant number of us can be pretty wasteful sometimes, often just from not thinking about it a bit beforehand. Whether that be 16K, 64K or 32G. We find a way to fill it all up

 

Link to comment
Share on other sites

Bellies too, I've noticed, they tend to reach the limits of the envelope quickly...

Going back to design efficiency/inefficiency, I have used several languages during my working lifetime and I've noticed that, although any language can be used in a good as well as a clumsy way, certain languages encourage a better coding practice than others. Forth is one example, code tends to be more compact, more able to be re-used or incorporated into other bits of code. The languages I think lead one into less elegant coding styles are Assembler, for obvious reasons, and C. The language with the biggest failings I found to be Fortran, for although it was supposed to allow direct translation from formulae into code, I would often spend ages on explaining to the users just how their formula had actually ended up in the resultant code and why it couldn't look exactly as they were used to understanding it.

What isn't often realised is that, despite our high-level code looking very elegant and concise, the conversion by the compiler front-end or byte-code generation results in some extremely messy code, which nevertheless is just as fast when it comes to execution. There are two distinct audiences we have to perform to, other coders and reviewers who see one thing in a high-level construct, and the compilers/execution hardware, who see something quite different.

Edited by Profaitchikenz Haiku
  • Like 1
Link to comment
Share on other sites

13 hours ago, Profaitchikenz Haiku said:

What isn't often realised is that, despite our high-level code looking very elegant and concise, the conversion by the compiler front-end or byte-code generation results in some extremely messy code, which nevertheless is just as fast when it comes to execution. There are two distinct audiences we have to perform to, other coders and reviewers who see one thing in a high-level construct, and the compilers/execution hardware, who see something quite different.

i agree with your general sentiment

a thing that most interns and new grads get told when they go to work for a commercial codeshop is know your compiler

which leads into the question: In a commercial codeshop who are we writing the code for?  The compiler or the person who will follow us into the chair

typically in commercial codeshop the emphasis is on the compiler, as the thought is that the person who follows us into the chair will be as good/experienced as us, if not better. And if they aren't then they can ask a more senior team member what the code means

a compiler emphasis that doesn't always work out too well in other settings.  Like a Computer Science 101 classroom. A Programming for Beginners book, etc

when the emphasis is on the compiler we start seeing standard code like this

// stay in range [0..9] with carry
integer sum = x + y;
integer carry = sum / 10;
integer remainder = sum % 10;

written like:

// stay in range [0..9] with carry
integer sum = x + y;
integer carry = (sum > 9);
integer remainder = (sum < 10) * 10 + sum - 10;

the question from the person reading this code is: Why would they do that?

the answer depends on the compilation. For example in LSL Mono
 
(sum > 9) is about 5-6 times faster than sum / 10
(sum < 10) * 10 + sum - 10 is about 2-3 times faster than sum % 10

 

  • Like 1
Link to comment
Share on other sites

17 minutes ago, Mollymews said:
integer remainder = (sum < 10) * 10 + sum - 10;

Took me a bit to parse that. Wouldn't it be minutely better to do:

integer remainder =  sum - (carry * 10);

 

Edited by Quistess Alpha
reused carry variable.
  • Like 2
Link to comment
Share on other sites

You are about to reply to a thread that has been inactive for 1024 days.

Please take a moment to consider if this thread is worth bumping.

Please sign in to comment

You will be able to leave a comment after signing in



Sign In Now
 Share

×
×
  • Create New...