Re: RAD vs. performance



Dr Jon D Harrop wrote:
Curtis W wrote:
Dr Jon D Harrop wrote:
You're looking at the wrong example.

The other examples you gave were almost identical in functionality to
mine. I thought you didn't like my approach?

That's the point. You can write in your abstracted style easily in ML
but people do not do this because it weakens the typing, slows the code
and is rarely used in practice (in the context of numeric types, at
least).
I wasn't arguing that it's not possible in OCaml, but rather that it's
underused and undersupported (by the compiler).

If you want it to handle something
else, you have to /rewrite/ it.

No, and I just gave you the code.

Of course, yours would actually be slower than mine, since yours has
abstractions that will be there, whereas my compiler can optimize them
away most of the time.

No, inlining the function arguments to the HOF recovers the original.
The only difference between the specialised and generic implementations
in OCaml is the passing of operators as function arguments.
Only in the cases when the type is known at compile time. Actually, I'd
forgotten about this--especially in something like math routines,
functions are generally going to be inlined. Inlining, unlike
specialisation, doesn't require full program optimisation, so your
point is moot 90% of the time.

For factorial, yes. For functions in general, in the presence of
higher-order parameterised types, you're talking about n^m growth in
the complexity of your compiler, in the slowest part (the optimiser),
so your compile times must go through the roof in order to optimise
away the unwanted abstraction that you have imposed upon us.

Again, compile time doesn't matter so much when you only do it once per
release.

During development, you do it all the time.
You wouldn't use the full program compiler when you're developing.

It is empirical evidence that you are solving a problem that doesn't
need solving.

You mean, besides the fact that your standard library is so poorly
written because of this fact? Take a look at the array and list
functions--half of them are identical.

Again, can you provide any examples? I'm looking and I don't see what
you're claiming. For example, here is Array.map:

let map f a =
let l = length a in
if l = 0 then [||] else begin
let r = create l (f(unsafe_get a 0)) in
for i = 1 to l - 1 do
unsafe_set r i (f(unsafe_get a i))
done;
r
end

and here is List.map:

let rec map f = function
[] -> []
| a::l -> let r = f a in r :: map f l

where is all of this commonality and code duplication?!
This is even worse than I thought. A map function only requires an
iterable object--whether or not the object is an array or list is
irrelevant. Specifically, in my language map looks like this (types
present for clarity):
[code]
def map(xs:iterable, f):
ret = []
for x in xs:
ret.append_ip(f(x))
return ret
[/code]
(an iterable is any object with an 'iter' function, which returns an
iterator over the object; 'for' automatically handles all this)

It may not be immediately obvious, but the reason the two versions
above are different is solely because Array and lists don't expose the
same interface--namely, being able to deconstruct via the '::' operator
in pattern matching.

note: this is assuming the array version isn't in-place. It doesn't
look like it, although it's not particularly easy to understand. If it
is in place, then you're comparing apples to oranges.

Again, where is all of this code duplication?

In many, many pieces of OCaml code.

Give some examples, please.
The standard library (see above). Or, a real world example:
http://www.gamedev.net/community/forums/topic.asp?topic_id=401651

Only slightly:
def factorial(x:int):
if x == 1: return x
else: return x*factorial(x-1)

3 characters, wow, that's really verbose!

Do you want to compete with static typing or don't you?

Write down the algorithm that the programmer must execute in order to
determine which code hot spots require type annotation. It's hard and
time consuming.

Profiling is?

Reading a profile is time consuming, even if we assume that your
profile will contain all of the type information that is required.
True. This wouldn't be necessary in the two serious solutions I
proposed.

Right, but you want the error catching that you can only get with whole
program recompilation when you're doing development, and that isn't
feasible.

What are you talking about? The type checker doesn't require whole
program compiling--the only reason the optimiser needs that is because
/it's changing a function/,

... according to its type.
As well as usage information, which can be in another module.

whereas a type checker simply verifies the
statements against publicly available type information from that
function.

This is a question of specificity. In ML, the factorial function has
the type int -> int and the type checker will unify this type with any
uses of this factorial function in order to type check the rest of the
program. A type-specific version in your language will have the same
type checking done. A type-generic (e.g. int | float) version in your
language can only have the same type checking guaranteed if whole
program analysis is used to examine every call site (monomorphisation).
Alternatively, you can type check the union type int|float but this is
weaker type checking (the type is not as specific) so it will not catch
as many bugs.
Do you have any examples of how it "weakens type checking"?

In the vast majority of cases, we don't want that flexibility. It is
too loose.

How can you be "too loose"? I've already shown you that performance
would rarely be an issue and that type checking isn't harmed.

Performance is an issue. This abstraction in OCaml costs about an order
of magnitude in performance when applied to int vs float, which is
equivalent to the case of your compiler being unable to optimise away
your abstraction, i.e. the worst-case scenario for dynamic typing.
Which would happen rarely with a whole program optimiser. Specifically,
it would only happen when either 1) there's no way to optimise it,
period or 2) when the function is in a library and its usage isn't
known at compile time. The latter can be offset through the use of the
other method I posted.

The
parser doesn't even do anything other than instantiate types, so there
isn't even a problem there.

But the parser and evaluator might be mutually recursive (e.g. having
"load" function) unless the expr type is declared somewhere shared by
the two.
So? Even then, it would still only be instantiating objects.

Despite the fact that there is a lot of evidence to back up what I am
saying (e.g. performance of compilers and compiled dynamically typed
languages, utility of static checking) and little to back you up (e.g.
no code duplication, no aggressive factoring into HOFs).

What kind of response is that? I say premature optimisation is a bad
design decision and you throw me statistics on the performance of
compilers?

As I've said, we don't do this to optimise our code, we do it to
statically check our code to remove more bugs. Over-abstracting is
every bit as bad as premature optimisation. People just haven't
realised it yet...
Only in the sense that over-abstraction is needless abstraction.
Over-abstraction doesn't apply to libraries, since it's impossible to
know what people will or will not need.

It seems to me you're just throwing stuff out there,
now, since that doesn't even really make any sense.

What doesn't make sense?
Your assertion that type checking union types is somehow "more weak."

.



Relevant Pages

  • Re: RAD vs. performance
    ... whereas my compiler can optimize them ... optimise away your abstraction because you have thrown away the type ... check as effectively because you do not have the type information. ... away the unwanted abstraction that you have imposed upon us. ...
    (comp.lang.misc)
  • Re: RAD vs. performance
    ... be no slower than the specialized version, simply because the compiler ... so your compile times must go through the roof in order to optimise ... but they don't because you rarely need that abstraction. ... I'd have an int argument and generalise the result using ...
    (comp.lang.misc)
  • Re: RAD vs. performance
    ... compilers do nothing to optimise such code. ... maybe the OCaml people should improve their compiler:) ... OCaml code is not usually written in that form, ... It is a union type (an expression is ...
    (comp.lang.misc)
  • Re: AVR Beginner Questions - Ports and Speed
    ... On bigger processors, a good compiler will ... while the assembly programmer might use a full-blown divide ... > 3) One of the most productive strategies is to code in C, but optimise ... Chosing the optimal form for loops, ...
    (comp.arch.embedded)
  • Re: C99 exact width integers usage - informal survey
    ... You might declare it an ... but the compiler will optimise it to ... the maximum int size. ...
    (comp.arch.embedded)