[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: CMU CL 15c performance
|Python will infer that a DOTIMES (or other loop with a fixnum count and a
|inequality exit test) ranges only over fixnums. However, due to
|limitations of the current flow analysis algorithm, it is confused when
|the body contains a nested loop. Ideally it should also infer that a
|bounds check is unnecessary, but I haven't even thought about how to do that.
What about pulling out _all_ array bounds checks from DOTIMES and
similar loops:
(dotimes (i n) ... (aref a i) ...)
=>
(assert (>= (array-dimension a 0) n))
(dotimes (i n) ... (unchecked-aref a i) ...)
Then, the compiler could eliminate any assertion that is obviously
true.
I don't know what the semantics of CommonLisp errors are; if an
implementation is not allowed to signal an error early (i.e.,
before all side-effects leading up to the error have been carried
out), I think the semantics are wrong. But even then, it would
probably be profitable to convert this into something like:
(cond ((>= (array-dimension a 0) n)
(dotimes (i n) ... (unchecked-aref a i) ...))
(t
(warning "I'm going to signal an error real soon now...")
(dotimes (i n) ... (aref a i) ...)))
|Hmmmn. I would guess that MIN did work. However, there is a problem with
|arithmetic operators like +, *, etc. The initial result type assumption
|for these operators is NUMBER. In order to realize that the value is
|always a float, it would have to first postulate that the value was a float
|and then see if this solution were consistent, in effect do an inductive proof.
Couldn't you express the fact that (+ <single-float> <single-float>)
must always yield a <single-float>? This sort of comes back to the
"polymorphic constraint" question below.
|Dynamic type inference is a somewhat subtle thing. Python will infer that
|the value is a FIXNUM, but only downstream of the array reference.
|Sometimes this is enough to prove that a loop variable is a fixnum, and
|sometimes not. In a dynamically typed language one can write:
| (dotimes (i n)
| (if (typep i 'fixnum)
| (aref v i)
| (write-line "We got a big one!")))
Yes, but in my case, there were no conditionals, type predicates, or
assignments to "i". I think it would be very useful if such type
constraints could propagate backwards since this is a very common
case.
For example, in the case of
(dotimes (i n)
(foo i)
(bar (aref v i)))
it seems to be legal to infer that "i" must be a fixnum (and smaller
than (array-dimension v 0)), or else the program is in error. The same
comments about errors and side-effects as above also apply here, of course:
I don't know whether the implementation can signal the error early. But
it can always generate two loops: an efficient one that uses fixnums,
and an unoptimized one that may even warn about the impending error
condition before all side-effects have occurred.
|The idea of explicitly declaring polymorphic types is an interesting one,
|and would probably fit better in Common Lisp and a ML-like inference
|scheme. One semantic problem is the need to define what it means to be
|"the same type" when you are substituting in a pattern.
Probably, the most natural style of declaration would say something
like "when applied to two single-float values, this function yields
a single float". Again, the CL type system is a little too subtle for
me to know whether that can be expressed within the current
type system, but I think Lucid used to interpret FTYPE declarations
like this.
Thanks, Thomas.