jzakiya
3/22/2005 7:09:00 AM
Isaac Gouy wrote:
> > The modification consists of unrolling the loops in
> > 4 methods in the Vector3D class which do vector math.
>
> Manual loop unrolling is one optimization too many.
>
> We have a preference for plain vanilla programs - we're trying to
> compare language implementations, not programmers.
I'm a litlle tired, and will respond more fully later, but I felt
a prompt response was needed now.
There is no requirement in any language, or as a paradigm of
coding practice, that a looping structure be applied to every
potential instance where its use is possible. To "unroll" a
"loop" is a coding "optimization" only if you start from the
reference point of the use of a loop structure. If you never
use a loop structure in the first place, and instead perform a
"direct implementation" of a function from the start, then that
is the reference point of further modification.
When engaging in the exercise of "benchmarking" inherent in
that exercise is to identify coding techniques and paradigms
within the idiom of the language being used to perform the
stated tasks as fast as possible.
Benchmarking is not about predetermining an arbitrary coding
structure paradigm and unilaterally applying that paradigm
to a every language, irrespective of the negative consequences
it has to producing the fastest program in that language.
Ruby is a native dynamic interpretive language. A user, to
acquire the highest performance possible from the language, has
to understand the consequences of this fact. One thing this mean
is that a user has to be "the compiler" at times, in order to
squeeze the highest possible performance out of the language.
Ruby is not C/C++, with highly optimized compilers, which do
a lot of the thinking for the programmer to extract performance
from source code.
Thus, as in the original nbody code, you could produce the
magnitude of a 3-dimensional vector as follows:
def magnitude
d = self.inject(0) {|a,v| a + v*v}
sqrt(d)
end
but the original code took 41 minutes to run on my system
(AMD K-7, 600Mhz, 640MB, Mandrake 10.1 Ofl, Ruby 1.8.2)
However, the inject method is not "performance optimum".
So, I first rewrote the looping structure to make it faster:
def magnitude
d = 0.0: (0..2).each {|i| d += self[i]**2}
sqrt(d)
end
and this was even a little faster
def magnitude
d = 0.0; 3.times {|i| d += self[i]**2}
sqrt(d)
end
At this point, by optimizing the most performance efficient
Ruby looping structure, I got the benchmark task down from
41 minutes to 27 minutes. Then the little lightbulb in my
head turned on.
Instead of looking at the task as an exercise in the use of
programming structures, I looked at what was the physics that
was trying to be done. After I realized what the physics
was, I just wrote code to "directly implement" the physics.
def magnitude
x=self[0]; y=self[1]; z=self[2]
sqrt(x*x + y*y + z*z)
end
Not only is this code accurate, its simple, understandable, AND
creates a MUCH faster program, which is the main point for a
benchmark. Using "direct implementation" of just 4 function methods
reduced the revised benchmark (which I posted) time to 17 minutes,
from the 'original code' time of 41 minutes.
Further testing has reduced the time to 15 minutes, using the
following code for the magnitude, tested to be the fastest so far.
def magnitude
sqrt(self[0]**2 + self[1]**2 + self[2]**2)
end
Besides executing the fastest in Ruby 1.8.2, you can't
produce code any shorter and simpler than this.
So by what codified set of programmnig rules can anyone say, with
sustainable validity, that this coding paradigm is inappropriate?
In fact, in the "real world" where money, time, efficiency, and
people's lives matter, "direct implementation" of functions, in
my experience, is standard procedure, especially where speed
matters. This is especially true with languages such as Forth,
my native software language.
Now the Ruby nbody benchmark performs 1 million iterations in
15 minutes instead of 41 minutes, which is 2.5 faster than the
original code!
Ruby is a great language to get real work done with. It isn't
natively the fastest, but it needn't be naively coded to create
unnecessarily slow performance.
I've just shown one simple technique to speed up Ruby 1.8.2.
Hopefully, with Ruby 2.0 and beyond, native performance will
inherently increase. Until then, if performance matters, then
you have to think a little bit about what you are really trying
to do, and do it the "best" way to optimize Ruby performance.
Thus, I disagree that benchmarks are about applying some
arbitrary programming paradigm to a set of languages, just so
the "look" of the programs are similar. Benchmarks should
illustrate how a given language can be used to perform a
given task (the benchmark) in that language, particularly
using the best programming techniques and idioms unique to
that language.
Thus, to me, the primary essence of benchmarking is comparing
how different languages can be uniquely applied to optimally
perform the same tasks, and comparing those results.
I'll expand more on these points in the future, but I hope these
comments explain my point-of-view, and reasoning, on this issue.
Jabari Zakiya