[Prev][Next][Index][Thread]

Fw: Beyond Java?




----- Original Message ----- 
From: Scott McKay <swm@mediaone.net>
To: Maxim Kizub <M.Kizub@post.skynet.lt>
Cc: <swm@functionalobjects.com>; <jason@functionalobjects.com>
Sent: Monday, March 05, 2001 5:03 PM
Subject: Re: Beyond Java?


> To: Scott McKay <swm@mediaone.net>
> Date: Monday, March 05, 2001 10:08 AM
> 
> 
> >-----BEGIN PGP SIGNED MESSAGE-----
> >Let me try to explain, why I make assumptions that
> >compiler is 'stupid' enough...
> >First, let's take a real-world application.
> >For a small application - about 90%-99% of code -
> >is standard library, right? For big and complex
> >project - it's about 50% of code.
> >
> >Now, most of optimization a compiler may make are
> >for those cases, when it does 'global optimization'.
> >Really - compiler can't replace any call to a member
> >method (that can be overriden) to direct call, unless
> >it's absolutly sure, that all run-time types are
> >known at compile time. A agree, that when you compile
> >an application - you may use global optimization, and
> >most of methods will have known (at compilation time)
> >run-time types. But when you compile a library - nothing
> >is known Most of types can be extended and about any
> >method may be overwrited - and, because libraries are
> >most of the code of real-world application, we have
> >slow method dispatching. Of cause, compiler may add
> >some hidden 'hint' parameter of a call, and those
> >hints will, offten, dramatically increase method
> >dispatching perfomance, but the maintaining of hints
> >at run-time will itself cause slowdown... And run-time
> >support of dispatch code may cause a lot of problems
> >for dynamic languages (that can eventually load new types
> >at run-time, like java language).
> 
> 
> You need to look at Dylan more closely (and probably Java,
> too).  Dylan has a 'sealed' adjective (a lot like Java's 'final'
> adjective) that can:
>  - assert that a class may have no further subclasses
>  - assert that a slot may not be overridden
>  - assert that a generic function may have no more methods added
>  - assert that a multi-method over a particular "domain" may not
>    be specialized any further
> 
> Dylan has a per-library compilation model -- you compile the
> whole library at once.  This means that intra-library optimizations
> can be done very aggressively in the scope of the language.
> This turns out not to be true in Java -- its per-class compilation
> model means that it has to be less aggressive about optimization
> even within a notional library.  So Dylan can beat out Java here.
> 
> Now let's look at inter-library compilation.  This is where Dylan
> can *really* kick butt, and without having to resort to a JIT.  In
> Dylan, when a class, slot, generic function, or method is known
> to be sealed, the compiler can use combine this knowledge
> with type information to safely do "direct dispatch" to the most
> specific methods.  BTW, you can again see this in action using
> Fun-O's "optimization coloring".  Write a program that spans
> two libraries, do a reasonable job of specifying the classes
> in the client, and notice that the client calls will safely use direct
> method call.
> 
> Note that changing the contract of any class or function in a
> shared library means that you have to recompile clients, just like
> in C++ or Java.  Dylan does no magically solve this.
> 
> <aside>
> You might claim that all this sealing and final stuff complicates
> the task of doing library design.  You would be right -- it does.
> Deciding what parts of a library ought to be extensible is a
> difficult task that requires careful thought.
> 
> I feel obliged to point out that making a language class-
> centered makes doing this basically impossible to get right.
> For example, consider a new function 'stringPluralize' in Java.
> How to do this?  You can't subclass 'String', because it's final.
> And the only way to add a 'stringPluralize' method is to
> introduce a hackish utility class, and write a 'stringPluralize'
> static method.  Fooey.
> 
> 
> In Dylan, there's an "open" abstract class called <string> with
> a sealed subclass <byte-string>.  You can't add any subclasses
> to <byte-string>, nor can you specialize any of the sealed
> methods on <byte-string>.  But you *can* introduce a new
> 'string-pluralize' generic function on <string> and then add
> a sealed method on <byte-string> with no loss of modularity.
> 
> If you really want to learn about how modularity interacts with
> multi-methods, I highly recommend the Cecil papers by Craig
> Chambers.  Their work is just great.
> </aside>
> 
> >Now, about primitive types.
> >First, there is a good solution only for an 'int' type -
> >by the price of switching to 31 or 30 bit integers.
> >But what will you do with 'long' (64-bit ints) and
> >float types? 'long' type do not fit into standard (32 bit)
> >hardware word - thus you need always box it.
> >and you can't borrow any bit from 'float' type -
> >because it will mean slow float arithmetic and
> >non-IEEE compliant language (implementation).
> 
> 
> You only need to box them when both of the following are
> true:
>  - they are stored in the heap
>  - and no declared type was given
> 
> Here's the reason:
>  - If it's stored on the stack, it's because it's an intermediate
>    value from an expression, and the compiler can determine
>    at compile-time what it is, so no tagging is needed
>  - If there's a type-declaration for a value on the heap, you
>    only need to type-check when you store into the heap, and
>    then only when you provably "don't know" what the object is
>  - When the float is in a vector or is a store in a slot, well, you
>    can make a specialized vector that stores only floats, or you
>    can declare the type of the slot.  In these cases, the tag
>    is stored just once per class.
> 
> Note that in the above, you still may need to type-check when
> you store a "don't know", but you have to in Java, too.  And the
> compiler usually has enough info to avoid this.
> 
> >Another example. Let's take a 3D application. Most
> >of time it works with Vector and Matrix types, like
> >class Vector { float x,y,z; }
> >class Matrix { float m[4][4]; }
> >First, enforsing boxing of all float values into
> >references will increase memory usage in a few times,
> >plus it will cause significant slowdown because of
> >CPU cash misses, plus, all modern extensions to CPU
> >instructions (like 3DNow! or SSE/SSE2) are made
> >from assumption that float values are primitive types,
> >and are stored in memory consecutevly.
> 
> 
> This is exactly why you make a type like this:
>   limited(<vector>, of: <single-float>)
> This tells the compiler and run-time that this vector must
> contain only single floats, and the compiler can optimize
> accordingly, avoiding all boxing and unboxing.  Both Fun-O's
> and Gwydion's Dylan compiler do this optimization.
> 
> The issues you raise are, in fact, all valid, but we thought
> about that when we designed Dylan.  I think for the most
> part, we got the design right, and the compilers have mostly
> gotten it right, too.
> 
> >Don't take me wrong - I'm not againts multiple-argument
> >dispatching or primitive types wrapping. In my
> >extended java compiler I try to define both of them.
> >But I'm strictly against approach that limits
> >programmers.
> 
> 
> Since single-arg dispatch is provable a special case of
> multi-arg dispatch, I think you might be arguing from the
> wrong side of the courtroom here.  As for primitive types,
> very occasionally, they are useful (there, I admitted it),
> but very rarely for the reasons you give above.
> 
> >If Dylan have automatic wrapping of primitive types
> >_plus_ allowing usage of primitive types - I'll welcome
> >this. Let those operations with primitive types be
> >(syntacticaly) not very convinient - it's ok, but
> >let them exists!
> 
> 
> Fun-O Dylan has objects called <machine-word> that you can
> do primitive arithmetic on, but they are not type-safe.
> 
> 




Follow-Ups: