Re: sleepless night & UFOs

new topic     » goto parent     » topic index » view thread      » older message » newer message

I concur, Gabriel.  Your routine looks like it would execute faster than
mine for more than a couple of recursions.  I knew that passing the extra
variables would make it slower (although I didn't know by how much).  I
think my routine is slightly more readable than yours, but for long
sequences, yours would be much faster than mine.

IMHO, unless speed is critical, I think it's best to opt for readability.
 If it executes too slowly, you can always optimize it.

Anyway, sorry if I wasted your time.  I've never written a program in
Euphoria.  I just saw an opportunity to try to help someone, so I wrote my
posting off the top of my head without testing it...

Joe

-----Original Message-----
From:   Boehme, Gabriel [SMTP:gboehme at MUSICLAND.COM]
Sent:   Tuesday, April 20, 1999 5:23 PM
To:     EUPHORIA at LISTSERV.MUOHIO.EDU
Subject:        Re: sleepless night & UFOs

Colin Taylor wrote:

>I don't see how a complex function like Gabriel's can possibly be faster
>than a straight-forward approach such as:
>
>function remap(sequence s, sequence index, object newvalue)
>    if length(index) > 1 then
>        s[index[1]] = remap(s[index[1]], index[2..length(index)],
newvalue)
>    else
>        s[index[1]] = newvalue
>    end if
>    return s
>end function  -- remap

In my tests, both my original and modified routines are almost always
faster
than Joe Otto's routine. Colin's routine (shown above) is almost always
*slower* than Joe's. There are several good reasons why my two "complex"
routines are generally faster than either Joe's or Colin's. Code efficiency
doesn't always result from code simplicity; there are other factors which
come into play.


1) Calling length() multiple times vs. storing it once in a variable

If you're going to use a sequence's length value more than once in a given
section of code (and the sequence's length isn't changing), it's almost
always faster to define another integer variable, store the length() value
there, and then reference the variable instead. This is one reason why
Colin's routine is slower than Joe's, and this is the main change I made to
my original routine to gain 5+% in execution speed.


2) Subscripting a sequence vs. storing the value in a variable

If you're going to access a given sequence element more than once in a
given
section of code (and the sequence element isn't changing), it's almost
always faster to define another variable, store the sequence element there,
and then reference the variable instead. This is another reason why Colin's
routine is slower than Joe's -- Colin's routine references "index[1]" twice
in the recursive call, while Joe's routine stores "index[1]" in a variable
and uses that instead.


3) Recursive calls: parameter passing vs. local variables

When writing recursive functions, there is usually only one parm actually
being treated recursively -- the others are just along for the ride. This
is
quite true with remap() -- newvalue only actually gets used on the final
recursive call, the rest of the time it just takes up space. Instead of
requiring the interpreter to continually reallocate space for a variable
that will only be used once, it makes more sense just to store it in a
local
variable until you actually need it. This is also true for the index_list
--
instead of passing index_list[2..length(index_list)] along as a parm, it's
much faster to store the list in a local variable and grab the first
element. Then, *only* if you have another recursive call coming up, you
truncate index_list to index_list[2..len] before making the call.


4) The test data

This is an often-overlooked part of any benchmarking procedure. The data
being used to test your routines can have a *major* impact on your results.
Whatever routines you're testing, it's a good idea to vary the test data.
Look closely at your code, and choose data that will test very specific
parts of the routines (if conditions, recursive calls, etc.). Also, keep in
mind the most likely real-world uses of your routines, to avoid testing
unnecessary or unlikely situations.

In my remap() tests, I found that my routines always run faster with an
index_list length of 3 or more. Joe's routine runs faster with an
index_list
length of 2, and Colin's runs the fastest with an index_list length of 1.
The longer index_list is, the more recursive calls the routine has to make,
and the more points 1-3 above come into play.

Now, it's unlikely that a programmer will use remap() very often on
length-1
index_lists -- it may occur incidentally as part of an algorithm, but it's
not likely to be the primary use of remap(). So, if you'll primarily be
using length-2 index_lists in your program, Joe's routine will be faster.
If
you'll primarily be using length-3 or greater index_lists in your program,
then my modified routine is the faster. My assumption (which may be wrong)
is that general use of remap() would regularly involve length-3 or longer
index_lists. I therefore conclude that my modified routine is probably the
best choice for general-purpose use.


No matter what coding tips or suggestions you follow, it's *always* a good
idea to run some benchmarks and check things out for yourself. I have
included my remap benchmark program below. It's not too flashy, but it gets
the job done. (I don't trust call_func() anymore, since the exact *same*
code will come back with *different* execution times, depending on the
code's physical location in the program!)

I hope all of this has been helpful and/or educational (and I hope I got
all
my facts right!).


Be seeing you,
   Gabriel Boehme


 -----------------------------------------------------------------
 -- remap.exw by Gabriel Boehme
 --
 -- This benchmark program will run under Windows or DOS.
 --
 -- However, Windows tickcounts are more reliable, since they
 -- only count the time the program is actually executing.
 -- (Somebody correct me if this statement is inaccurate!)
 -----------------------------------------------------------------

include get.e
include machine.e -- for running under DOS
tick_rate(10000)  -- does nothing under Windows

 -- execution parms
constant
 ITER = 1e6     -- benchmark iterations
                -- test sequence depth determined by LEVELS length
,LEVELS = {5,10,3} -- length of each level in the test sequence
,LIST = {3,5,2} -- *must* all be integers >= 1 and <= LEVELS[x]


 -----------------------------------------------------------------

 -- Gabriel Boehme's original routine
sequence list
object val

function remap_recursive_old(sequence s)
integer i
   i = list[1]
   list = list[2..length(list)]
   if length(list) then
      s[i] = remap_recursive_old(s[i])
   else
      s[i] = val
   end if
   return s
end function

function remap1(sequence s, sequence index_list, object newvalue)
   list = index_list
   val = newvalue
   return remap_recursive_old(s)
end function
 -----------------------------------------------------------------

 -- Joe Otto's routine
function remap2 (sequence s, sequence indexList, object newValue)
    integer listLen, i1

    listLen = length (indexList)
    i1 = indexList [1]

    if listLen > 1 then
        s [i1] = remap2 (s [i1], indexList [2..listLen], newValue)
    elsif listLen = 1 then
        s [i1] = newValue
    end if

    return s
end function
 -----------------------------------------------------------------

 -- Gabriel Boehme's modified routine
sequence remap_list
object remap_value

function remap_recursive(sequence s)
integer i, len
   i = remap_list[1]
   len = length(remap_list)
   if len > 1 then
      remap_list = remap_list[2..len]
      s[i] = remap_recursive(s[i])
   else
      s[i] = remap_value
   end if
   return s
end function

function remap3(sequence s, sequence index_list, object new_value)
   remap_list = index_list
   remap_value = new_value
   return remap_recursive(s)
end function
 -----------------------------------------------------------------

 -- Colin Taylor's routine
function remap4(sequence s, sequence index, object newvalue)
    if length(index) > 1 then
        s[index[1]] = remap4(s[index[1]], index[2..length(index)],
newvalue)
    else
        s[index[1]] = newvalue
    end if
    return s
end function  -- remap
 -----------------------------------------------------------------


procedure msg_wait(sequence msg)
   puts(2, msg & "\n\nPress any key to continue . . .")
   if wait_key() then end if
   puts(2, "\n")
end procedure

 -- verify execution parms
if length(LIST) = 0 or length(LIST) > length(LEVELS) then
   msg_wait("Invalid LIST sequence length")
   abort(1)
end if

for i = 1 to length(LIST) do
   if not integer(LIST[i]) or LIST[i] < 1 or LIST[i] > LEVELS[i] then
      msg_wait("Invalid LIST sequence - index values out of bounds")
      abort(1)
   end if
end for

 -- build the test sequence
sequence s
s = repeat(0, LEVELS[length(LEVELS)])  -- start with the innermost level
for i = length(LEVELS)-1 to 1 by -1 do -- and work your way outward
   s = repeat(s, LEVELS[i])
end for

printf(1, "A remap() benchmark program by Gabriel Boehme\n"
        & "Sequence depth %d, remap depth %d\n\n",
          {length(LEVELS), length(LIST)})

puts(1, "[Smaller values are better]\n---------------------------\n")

 -----------------------------------------------------------------
atom t

 -- the moment of truth
puts(1, "Gabriel's original routine: ")
t = time()
for i = 1 to ITER do
   s = remap1(s, LIST, 1)
end for
? time() - t

puts(1, "Joe's routine:              ")
t = time()
for i = 1 to ITER do
   s = remap2(s, LIST, 1)
end for
? time() - t

puts(1, "Gabriel's modified routine: ")
t = time()
for i = 1 to ITER do
   s = remap3(s, LIST, 1)
end for
? time() - t

puts(1, "Colin's routine:            ")
t = time()
for i = 1 to ITER do
   s = remap4(s, LIST, 1)
end for
? time() - t


msg_wait("")
 -----------------------------------------------------------------

________________________________________________________
NetZero - We believe in a FREE Internet.  Shouldn't you?
Get your FREE Internet Access and Email at
http://www.netzero.net/download.html

new topic     » goto parent     » topic index » view thread      » older message » newer message

Search



Quick Links

User menu

Not signed in.

Misc Menu