[REBOL] Re: About Standards and Best Practices
From: greggirwin:mindspring at: 2-Sep-2003 9:18
Hi Reboler,
R> I know that Rebol is cool!!, but what I was thinking is that any programmer
R> has his view of Rebol. Some of them use a lot of objects, others use just
R> function calls. By the other hand, Rebol is so handy that we can also use a
R> procedural approach. But what are you missing here? the performance!!
Your questions aren't dumb at all, just hard to answer. :) The problem
is that, with REBOL, you can do things in so many different ways that
it's hard to define "general" optimizations, as Brett said. Sometimes
rethinking the solution in an entirely different way--maybe not
possible in other languages--will yield a simpler and much more
efficient implementation. That said, there are some obvious, and
not-so-obvious rules you can follow.
1) Premature optimization is a big problem is software development.
Only optimize things you *know* will make a significant difference. To
find those things, you have to profile and measure. I profile things
in different ways, but here's a little function I use to test isolated
blocks of code.
time-it: func [block /count ct /local t] [
if not count [ct: 1]
t: now/time/precise
loop ct [do block]
now/time/precise - t
]
print time-it [] 1'000'000
i: 0
print time-it [i: i + 1] 1'000'000
fn: func [/ref val][]
print time-it/count [fn] 1'000'000
print time-it/count [fn/ref none] 1'000'000
print time-it/count [make object! []] 1'000'000
print time-it/count [
make object! [a: none b: "test" c: copy []]
] 1'000'000
2) Optimize algorithms before you optimize code. In REBOL, we're
already working at a high level, but this can also mean using the
right data structure or datatype. For example, list! values are faster
than blocks if you are inserting and removing a lot of items, and
hash! values are much faster than blocks if you're doing a lot of
lookups (i.e. finding or selecting items).
3) Function calls take time. You will sometimes see people inline the
source of the APPEND function (which is basically INSERT TAIL) to save
a little time. If you have a loop that will make lots of calls, it can
*sometimes* be worth eliminating function calls this way, but remember
that it can also lead to more bugs and more code.
4) Path evaluation takes time. REBOL is interpreted, so sometimes it
can pay to cache values that are part of a deep path (in nested blocks
for example) if you're going to use them many times inside a loop.
This applies to functions inside objects as well.
5) For dynamic code, build blocks rather than strings if you're going
to DO them. It can be much easier, cleaner, and more efficient.
6) Use natives when you can. REBOL's natives are very fast. Rather
than looping over a series yourself, looking for values, try to figure
out a way to use natives like FIND and SELECT. Let REBOL do all the
heavy lifting when you can.
6.1) Get to know how some mezzanines work and which functions are
native. The FOR function is a good example. I see a lot of code that
uses FOR with a lower bound of 1 and a step size of 1. In those cases,
REPEAT is much more efficient. Loop constructs in REBOL are not all
native, and certainly not all equal in how they work, so they're worth
investigating.
7) If you're processing text, don't forget about PARSE (even if you
think it's not applicable to your situation).
R> 4) clear, reset vars..
This can make a substantial difference in some cases. If you're
creating buffers to use over and over again, particularly large ones,
rather than doing a "make string! 1'000'000" on every pass, do it once
and then just CLEAR the string on each pass.
For me, the big gains come when I can refactor a solution. Often times
stripping out my own code when I find a native alternative will be all
it takes.
HTH!
-- Gregg