Why Eiffel hasn't met its potential 
Author Message
 Why Eiffel hasn't met its potential

Quote:

> :Just as with handguns, I find that very few legs are actually
> :in danger after the first few times.
> :
> :All this goes back to the snipped discussion regarding costs of C++ copy
> :constructing, versus Eiffel's pass by reference semantics. I don't know that
> :we have a clear winner.

> Perhaps the person left with 2 legs? :-)

=======
:) It grows back.

Quote:
> The whole thrust of Eiffel is for the language environment to do as much of the
> work for you as it possibly can to allow you to concentrate creative effort on
> abstracting the problem domain. The developer is saying "Well, I probably can't
> do a significantly better job than the compiler/run-time-system (because I know
> it has been carefully designed to do the job efficiently), so I willingly
> relinquish responsibility for such tasks as optimising calls and memory
> management. I have the assurance, at least, that these tasks will be performed
> safely which is something I cannot guarrantee myself".

===========
Without getting too heated on the subject, I can't agree with you
entirely on this. I find quite a bit of appeal in the guarantees
that GC can give you. OTOH, determinism in resource management is a
powerful tool when we talk about pragmatics: managing those
artifacts of implementation that seemingly will never fade into
obscurity.

Memory is but one resource issue; GC is not symmetric when it comes
to this. If it were, I would, like you, gladly hang my hat up and
say the war is won. Even then, I still reserve the right bear arms
when the situation warrants. Eiffel doesn't offer this option; C++
does.

In regards to qualifying "absolutes" about safety, I have to admit I
recently caught one of my "leak-proof" demo programs dripping all
over itself. The problem was identified, and in the spirit of
iterative refinement, version 3 of ObjectRef<T> is now closer than
ever to perfect, efficient protection. :)

With the STL and a reference-counting genericized smart-pointer at
hand, GC is not necessary at all. In terms of efficiency, I would
guess access is at least on par with Eiffel's object ref's. Beyond
this, it carries no overhead in terms of sweeping and marking. In
addition, it can guard any resource, not just memory, with no or
very little additional effort.

The advantage is, I can choose to protect resources this way when I
deem it appropriate. The disadvantage is, I'm still not sure that I
guarded everything that needs to be guarded. Java and Eiffel both
have an advantage in this regard: you cannot choose to not protect
yourself. The problem with Java and Eiffel is, ... well, you get the
idea.

(This was the leak I mentioned; my smart pointer implementation was
still rather inefficient at the time. I chose to not use it where I
should have seen the need to. Now, with reference counting added,
I have very little reason not to.)

In this corner of the world, victory over resources is at hand.

Quote:

> If we reckon we can do a much better job ourselves and are willing to accept
> our mistakes and are prepared to suffer the consequences, then we wouldn't
> bother using Eiffel.

===========
Perhaps. I'm still exploring the ways that Eiffel's object model can
prove an advantage over C++ in building the business logic. Its
covariance and anchored types appear to be just the things C++
lacks.

I haven't forgotten the initial point of this discussion
(efficiency, and relative performance). More than ever, I'm looking
forward to a quantitative study of *what* Eiffel is doing that can
be more efficient than an appropriately coded C++ version. I suspect
that one of the earlier posters in this thread hit on the real
reason: inefficient copying when references were more appropriate.

Mike.



Sun, 04 Oct 1998 03:00:00 GMT  
 Why Eiffel hasn't met its potential

Quote:

> But how does a language that uses automatic GC prevent me from doing the same
> thing?

>         x.shut_down;    -- release resources, etc.
>         x := Void;      -- let GC pick up the memory when it feels like it.

> What's the problem?

===========
One might say the same of memory:
   delete x;    // release memory, no GC needed
   x = 0;       // force safe state

One of the problems is with exceptions. Your rescue needs to release any
resource allocated, as does your normal execution path. Local objects
(somewhat like expanded types in Eiffel) are reliably and predictably
destroyed when they leave scope, regardless of presence or absence of
exceptions. This quality affords an opportunity to encapsulate
allocation/release in the object's behavior.

One other reason is simple convenience. The same mechanism is available
to reliably release the resource; you might say the object *is* the
resource. Its behavior is simple, coherent, and consistent -- those same
qualities we look for when deciding what to abstract in the problem
domain. The only difference is that this sits entirely in the solution
domain.

Mike.



Sun, 04 Oct 1998 03:00:00 GMT  
 Why Eiffel hasn't met its potential

Quote:
> Quite often, the "thing" selected has some crisp meaning:  
opening a file,
> changing the cursor shape, beginning a database transaction, or allocating
> some memory perhaps. Creation grabs the resource; destruction releases it.
> The resource is available so long as the object is accessible. Conversely,
> the object is inaccessible after the resource has been released. All code
> paths cause allocation/release to happen in pairs.

Heck, why does everybody assume an external resource cannot be  
freed before the object is garbage collected?

As soon as the program knows the file (or whatever) isn't needed  
anymore, it can use a disconnect() or close() or whatever() call,  
and the the resource will be freed, at a statically known point  
in the program flow.

To make the programs safe, a finalization method on the  
controlling memory object should still check wether the resource  
is allocated, and free it if appropriate.

Such an approach would combine the best of both worlds: Explicit  
deallocation when desired, and garbage collection when you don't  
care (or forget to deallocate - garbage collection will prevent  
your system from locking up due to exhausted resources in that  
case).

Quote:
> Significantly, at least
> one language guarantees order of destruction is opposite that of
> construction

This is a much more important issue. There are lots of cases  
where external resources have to be freed in a certain order  
(usually opposite order of allocation).
(Though I think such prescriptions are bad design on the side of  
the resource - when I release a resource, I clearly don't want to  
bother with any sub-resources that I may have allocated. But it's  
no use groaning about the mean resource designers - the language  
have to cope with the environment, not the other way round.)

Anyway, C++ wins on this - the Eiffel language standard does not  
have a finalization mechanism (yet, I hope).

-Joachim

--
Im speaking for myself here.



Sun, 04 Oct 1998 03:00:00 GMT  
 Why Eiffel hasn't met its potential

Quote:
> With the STL and a reference-counting genericized smart-pointer  
at
> hand, GC is not necessary at all. In terms of efficiency, I would
> guess access is at least on par with Eiffel's object ref's. Beyond
> this, it carries no overhead in terms of sweeping and marking. In
> addition, it can guard any resource, not just memory, with no or
> very little additional effort.

I don't know what you mean by STL, so I may miss the point.
Still, reference counting is not what I'd consider a good  
solution.

First of all, reference counting cannot totally replace GC  
because it cannot handle reference cycles. Of course, this point  
isn't valid if the cycles can be broken, e.g. if certain pointers  
are known to be backpointers that shouldn't increase the  
reference count. In practice, I have never met a case where I  
couldn't easily break the cycles this way, but it is easy to  
imagine cases where this is impossible.

But reference counting is also inefficient. Every time a  
reference variable gets assigned, you have two accesses to the  
reference count of the referred-to: one to read the old reference  
count, one to write the increased count. And we're talking memory  
references here that cannot be easily optimized by a compiler!
The same holds when a reference goes away: The routine has to  
read the old count and write the new decreased one.

For a highly variable graph, GC will outrun any reference count  
solution - provided that the GC algorithm is a decent one.

I really think there should be a better way than GC to get rid of  
unreachable objects - in 99% of all cases it is possible to  
statically determine when an object will become. (Of course the  
99% figure is one of the 67.34% of statistics that are made up  
<g>.) But I don't think the programmer should be bothered with  
memory deallocation - this type of work should be done by the  
optimization phase of a compiler.

-Joachim

--
Im speaking for myself here.



Tue, 06 Oct 1998 03:00:00 GMT  
 Why Eiffel hasn't met its potential

Quote:


> > With the STL and a reference-counting genericized smart-pointer
> at
> > hand, GC is not necessary at all. In terms of efficiency, I would
> > guess access is at least on par with Eiffel's object ref's. Beyond
> > this, it carries no overhead in terms of sweeping and marking. In
> > addition, it can guard any resource, not just memory, with no or
> > very little additional effort.

> I don't know what you mean by STL, so I may miss the point.

==========
STL is the C++ Standard Template Library, part of the pending
standard, properly known as the Standard Library; the reference
implementation was written by A. Stepanov at HP Labs. Among other
things, it defines a number of collections, various types of
iterators, and algorithms that work with the iterators (copy,
foreach, etc). It's actually a quite nice example of extension via
instantiation in C++; the idioms embodied there should become
required study for advanced students.

The significance of the STL in this discussion is that it easily
fills many of the complex data structure and access needs in most
applications. It is usable, offers wide coverage, and is standard
(or soon to be), thus forming a portable solution to the needs it
addresses.

In short, it means we can stop re-inventing the wheel for common
data structures and operations.

Quote:
> Still, reference counting is not what I'd consider a good
> solution.

> First of all, reference counting cannot totally replace GC
> because it cannot handle reference cycles. Of course, this point
> isn't valid if the cycles can be broken, e.g. if certain pointers
> are known to be backpointers that shouldn't increase the
> reference count. In practice, I have never met a case where I
> couldn't easily break the cycles this way, but it is easy to
> imagine cases where this is impossible.

==========
Cycles and backpointers refer back to the re-invented data
structures. Backpointers point back to other nodes, which might
contain references to your data objects. The container contains a
counted reference to your data object. No cycles or backpointers
are involved at this level.

Quote:

> But reference counting is also inefficient. Every time a
> reference variable gets assigned, you have two accesses to the
> reference count of the referred-to: one to read the old reference
> count, one to write the increased count. And we're talking memory
> references here that cannot be easily optimized by a compiler!
> The same holds when a reference goes away: The routine has to
> read the old count and write the new decreased one.

===========
Quite true. Assignment is somewhat rare compared to use, though.
(Statistically speaking, that is. :) Also, it's difficult for me
to imagine a GC scheme where reference counting is not used. If
you know of specifics on one, I'd be very interested to hear it.

Quote:

> For a highly variable graph, GC will outrun any reference count
> solution - provided that the GC algorithm is a decent one.

=========
Again, I'm assuming that GC similarly requires a reference count.
Whether it marks and sweeps indepedently, or simply deallocates
immediately when count reaches zero, is unspecified. I haven't
looked much at GC science since looking at Smalltalk's, 8 years
ago.

My implementation for generic reference counting is about 120
lines of very simple C++ code. Any resource type can be "garbage
collected" by providing deallocation and management functions,
written once for each resource type. For normal heap memory
objects, this amounted to 10 lines of code. Protection for file
handles and mutex's were also implemented in about 10 lines of
code each. Very few resource types will require something much
more complex.

Quote:

> I really think there should be a better way than GC to get rid of
> unreachable objects - in 99% of all cases it is possible to
> statically determine when an object will become. (Of course the
> 99% figure is one of the 67.34% of statistics that are made up
> <g>.) But I don't think the programmer should be bothered with
> memory deallocation - this type of work should be done by the
> optimization phase of a compiler.

==========
I agree in principle, and extend that to include all resource
types, not just memory on the heap. I think I took matters in the
opposite direction: instead of asking the compiler to treat all
resources as it does memory, I simply treated memory as I would
any other resource. Those same mechanisms that worked to
guard everything else can also guard memory. In fact, I count on
the statically known properties of C++ memory management to
accomplish this.

The "close to metal" property of C++ allows me to do this quite
easily and naturally. (Closure on the article that started this
thread. This is not the same as saying I don't find anything to
like in Eiffel.)

Mike.



Wed, 07 Oct 1998 03:00:00 GMT  
 The myth of C++ efficiency (reaffirmed! :-)

| Anyway, C++ wins on this - the Eiffel language standard does not  
| have a finalization mechanism (yet, I hope).

This is incorrect: the ELKS (Eiffel Library Kernel Standard) specifies
a class MEMORY with a feature `dispose' which, if redefined, is called
when the garbage collector decides to "free" an object. This sort of
finalization has been part of the TowerEiffel and ISE implementations
from the very beginning.  

In June 1995, TowerEiffel 1.5.0 introduced an alternate type of
finalization mechanism (see our MEMORY class, feature
`set_global_dispose') which is called when _any_ Eiffel object is
collected.  At least one customer is using this sort of finalization
for managing a persistent store (database handle <-> Eiffel object
map).

=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
Never express yourself more clearly than you think. -- Neils Bohr


Tower Technology        WWW:     http://www.twr.com/



Wed, 07 Oct 1998 03:00:00 GMT  
 The myth of C++ efficiency (reaffirmed! :-)
 >
 > With the STL and a reference-counting genericized smart-pointer at
 > hand, GC is not necessary at all. In terms of efficiency, I would
 > guess access is at least on par with Eiffel's object ref's.

Very surprising opinion.  With tracing type of GCs, accessing an
object is just a pointer dereference and no special operation is
needed when passing object refs.  Smart pointer schemes require an
indirection on accessing objects and expensive arithmetics on
duplicating references.

 > Beyond
 > this, it carries no overhead in terms of sweeping and marking.

On the other hand, tracing collectors carry no overhead in terms of
reference count manipulation.

Well, there are certainly pros and cons on both sides.  With garbage
collectors, your program can be much safer with smaller efforts.  On
the other hand, with manual storage allocation, you do not have to
worry about unintended resource retention caused by conservatism of a
particular GC implementation.  We must not try to overly generalize;
discussions like "GC is not necessary at all" or "Manual storage
management is too dangerous" are hardly fruitful.  I would like to see
more case studies.

There is an interesting case study here:

Benjamin Zorn
The Measured Cost of Conservative Garbage Collection
Software---Practice and Experience, vol 23(7) pp733--756 (July 1993)

 >
 > I haven't forgotten the initial point of this discussion
 > (efficiency, and relative performance). More than ever, I'm looking
 > forward to a quantitative study of *what* Eiffel is doing that can
 > be more efficient than an appropriately coded C++ version.

GC is certainly one of the possibilities.  Tracing collectors are
usually faster than reference counting (even if the reference count is
appropriately coded in C++).  I am looking forward to a quantitative
study on your statement about efficiency of smart pointers.  I
strongly recommend you to take a look at the Boehm's conservative
collector (ftp://parcftp.xerox.com/pub/gc) and have a comparison with
your smart pointer schemes.  You don't have to rewrite your things in
Eiffel.  Just link a good collector with your C++ programs.

--
                Kenjiro Taura
                Yonezawa Laboratory, Department of Information Science
                Tokyo University



Fri, 09 Oct 1998 03:00:00 GMT  
 The myth of C++ efficiency (reaffirmed! :-)

Quote:
>> Actually, Eiffel generics are simpler to compile than C++ templates, and allows for both
>> shared and efficient code to be produced in most cases.
>> Clearly, if the actual generic is a reference type, there is no problem, and all generic code
>> can be shared. Now if the actual generic is an expanded type, things get a little bit more
>> complex, but not too much. If the expanded object has the same size as a reference
>> (mostly usual on 32 bits architectures), then again the same generic code can be reused.

>This is not quite correct. Example:

>class A [G]

>feature
>    g: G
>    proc (a: G) is
>       do
>          g := a           -- *
>       end
>end -- class A

>In the marked line proc has to call copy in case G is expanded and assign references
>otherwise.

yes, but it does not contradict what I said before.

The instruction marked --* can be compiled to the very same C code:
                g = a;
whether or not actual for G is of a reference or of an expanded (value) type.
Then  it's up to the C compiler to produce the same code or not depending on the target
computer. Basically, if sizeof(actual for G) <= sizeof(register), there's no problem.

In practice this  allows  a fair amount of code to be shared.

---
Jean-Marc Jezequel                 | Tel : +81 (3) 3812-2111 ext. 4116
IRISA/CNRS, currently visiting:    | Fax : +81 (3) 5689-4365


The University of Tokyo            | http://www.irisa.fr/pampa/PROF/jmj.html  
Hongo Bunkyo-Ku, Tokyo 113, JAPAN



Fri, 09 Oct 1998 03:00:00 GMT  
 The myth of C++ efficiency (reaffirmed! :-)

Quote:

> >> Actually, Eiffel generics are simpler to compile than C++ templates, and allows for both
> >> shared and efficient code to be produced in most cases.
> >> Clearly, if the actual generic is a reference type, there is no problem, and all generic code
> >> can be shared. Now if the actual generic is an expanded type, things get a little bit more
> >> complex, but not too much. If the expanded object has the same size as a reference
> >> (mostly usual on 32 bits architectures), then again the same generic code can be reused.

> >This is not quite correct. Example:

> >class A [G]

> >feature
> >       g: G
> >       proc (a: G) is
> >          do
> >             g := a           -- *
> >          end
> >end -- class A
> yes, but it does not contradict what I said before.
> The instruction marked --* can be compiled to the very same C code:
>            g = a;
> whether or not actual for G is of a reference or of an expanded (value) type.
> Then  it's up to the C compiler to produce the same code or not depending on the target
> computer. Basically, if sizeof(actual for G) <= sizeof(register), there's no problem.

I was wrong. I thought, expanded assignment calls copy, but it ``calls''
standard_copy, for which the C assignment is OK.

Still, I'm not quite sure, whether you're right in general.

Let's change the example a bit:

class A [G]

feature
        g: ANY                 -- here I changed to ANY
        proc (a: G) is
           do
              g := a           -- *
           end
end -- class A

Now we have standard_clone for G expanded, which should allocate heap-storage, but
a simple reference assignment for G referential.



Fri, 09 Oct 1998 03:00:00 GMT  
 The myth of C++ efficiency (reaffirmed! :-)

Quote:
Jon S Anthony writes:


:
Quote:
:> Mike Young writes:

:>
:> [...]
:>
:> :... Ada has what I think is
:> :the most rational, model: it tracks call nesting to determine if passing a
:> :reference might be unsafe. In almost all cases, the nesting count adequately
:> :protects the programmer while allowing reasonable use.
:
:> Creating a dangling pointer or an object referenced by nothing is a
:> piece of cake in Ada. Here is an example of the former courtesy of
:> Bob Duff from a thread in c.l.a.:
:>...
:>     X: Some_Pointer := new Whatever;
:>     Y: Some_Pointer := X;
:>     ...
:>     Free(Y);
:>     ... -- Now, X is a dangling pointer, and you better not say X.all.
:
:Ah, Don.  This is not the point.  The two things being compared are
:apples and oranges.  Mike is correctly refering to the fact that Ada
:ensures that passing a reference is safe.

I'm sure it's true.

:  Bob's example is about how
:you _can_ create a dangling pointer in Ada.  Which is broken whether
:you pass the thing somewhere or not.  And passing a pointer (by value)
:is not the same as passing by reference.  People often confuse these -
:as you appear to have.  So, Mike's point stands and your point is
:analogous to, say, a bug in your GC where it effectively does the
:erroneous Free(Y) _for you_.  Of course, that latter case is a lot
:more "horrifying"...

It was a little off-topic but the two issues are opposite sides of the same coin:
mismanaged reference semantics.

a) dangling references - reference without objects, and
b) memory leaks - objects without references.

It's more difficult to have either in Eiffel (assuming the GC works).

:/Jon
:--
:Jon Anthony
:Organon Motives, Inc.
:1 Williston Road, Suite 4
:Belmont, MA 02178
:
:617.484.3383

:

Don.



Sat, 10 Oct 1998 03:00:00 GMT  
 The myth of C++ efficiency (reaffirmed! :-)

Quote:
Mike Young writes:


[...]

:> The whole thrust of Eiffel is for the language environment to do as much of the
:> work for you as it possibly can to allow you to concentrate creative effort on
:> abstracting the problem domain. The developer is saying "Well, I probably can't
:> do a significantly better job than the compiler/run-time-system (because I know
:> it has been carefully designed to do the job efficiently), so I willingly
:> relinquish responsibility for such tasks as optimising calls and memory
:> management. I have the assurance, at least, that these tasks will be performed
:> safely which is something I cannot guarrantee myself".
:
:===========
:Without getting too heated on the subject, I can't agree with you
:entirely on this. I find quite a bit of appeal in the guarantees
:that GC can give you. OTOH, determinism in resource management is a
:powerful tool when we talk about pragmatics: managing those
:artifacts of implementation that seemingly will never fade into
:obscurity.

In a sequential language, we might reasonably worry about determinism but in
in a concurrent one, it loses it's significance. You don't know exactly what
will execute when. Instead, (in Ada, at least), you prioritise threads so the
less important ones are allowed to run after the more important threads have
had a go and are blocked, waiting for an event to occur before they resume.

Note that the event required to trigger resumption of a higher priority thread
may be the completion of work performed by the lower priority one.

This is the most efficient way of managing things, IMO, and how GC should work.
The GC is just such a low priority thread and executes at the most opportune
time to cause minimum interference to the application proper. With such an
approach, collection proceeds in a non-deterministic but optimal way. Compared
with such a scheme, manual deallocation would be less efficient, IMO.

Note that although Eiffel is not yet concurrent explicitly, it is concurrent
behind the scenes and we might expect that implementations would perform GC
at appropriate windows of opportunity such as waiting on user input etc.

:
:Mike.

Don.



Sun, 11 Oct 1998 03:00:00 GMT  
 The myth of C++ efficiency (reaffirmed! :-)

Quote:
> | Anyway, C++ wins on this - the Eiffel language standard does not
> | have a finalization mechanism (yet, I hope).

> This is incorrect: the ELKS (Eiffel Library Kernel Standard) specifies
> a class MEMORY with a feature `dispose' which, if redefined, is called
> when the garbage collector decides to "free" an object.

Oops - I stand corrected.

BTW where can I find the ELKS, preferably on the Net? (Seems like  
every good Eiffelist should have OOSC, ETL, *and* ELKS <g>...)

-Joachim

--
Im speaking for myself here.



Sun, 11 Oct 1998 03:00:00 GMT  
 
 [ 53 post ]  Go to page: [1] [2] [3] [4]

 Relevant Pages 
 

 
Powered by phpBB® Forum Software