by Julien Langou » Tue May 06, 2008 12:04 am
The four xNRM2 routines that you can find in the reference BLAS are
reference implementations. Therefore they are not meant to be
state-of-the-art implementations but they are meant to be robust and
simple (understandable, readable) pieces of code that execute the
functionnality. Maybe you will find better implementations in some
optimized BLAS libraries (ATLAS, Goto BLAS for the open source ones).
That said, the current implementation of xNRM2 in the reference BLAS
(one of the many contributions of Sven Hammarling) is not that simple.
First, note that a code like: sqrt(x^Tx) is not acceptable. Assume that the
largest number, we can store in our computer is 1.8e308. Assume that the
vector X is made of two entries: 3e200 and 4e200. The Euclidean norm of
X is 5e200 which is a perfectly legitimate number on this system (i.e. you
can store it since it is smaller than 1.8e308). However, the computation
of sqrt(x^Tx) leads in first instance to x^TX=25e400 which would
overflow on our computer. Consequently, our result would be +Inf and not
5e200.
Fine, the solution is easy. In case of overflow, we need to scale our data
accordingly. In our case, simply pre-divide X by 1e200, find 5 as the
norm, and post-multiply by 1e200 to get 5e200. That's what the netlib
code is doing.
Since xNRM2 is a Level-1 BLAS subroutine, the efficiency of this routine is
all about memory access, for each memory reference you will perform
about three operations. Not a lot. Therefore the reference BLAS
implementation of xNRM2 uses a trick to perform as few as possible
passes on the original data. In this matter, the proposed algorithm is
optimal: it provides the solution in only one pass on the original data
(obviously the minimum bound). The trick is to compute the scaling factor
on the fly while computing the norm.
The original code and some comments are in the LAPACK routine xLASSQ,
SSQ standing for 'sum of squares'.
Some variants have recently been proposed by Fred Gustavson. The idea
is to use what Fred calls the 'fast path'. The problem of the current xNRM2
(and xLASSQ) routine is that there is a branching (IF statement) inside the
loop (DO statement) and therefore the code is almost impossible to
optimize correctly (by hand or by a compiler). This is the price for
robustness. Since the overflow cases are not supposed to happen all that
much in practice, Fred Gustavson proposed to use by default sqrt(x^Tx)
code, this code does not have any branching, can be easily vectorized,
performs one path on the data, etc., so it is fast. This code however
might overflow so if this code overflows (result is +Inf) then go back to
the current algorithm.
Why not, looks like a good idea.
There are other cases where the conservativeness of LAPACK has a cost
on the performance of the algorithm, while this conversativeness is only
justified in a few particular cases. Some fast path algorithms could used
there too. (Fred Gustavson told me at some points about DLATRS.) Those
algorithms would be faster in most of the cases, and would fail in some
cases. So they might be considered overall better. On going conversation.