Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Mailing lists / fpc-devel / 2005-02

Modernising Pascal

54 messages · 17 people · –

Reply by email Subscribe to this list Your subscription

Read-only archive. Use j and k to move between messages.

Jamie McCracken

Hi,

Just wandering if any of you are interested in modernising Pascal which is looking quite dated when compared to modern languages like Python. I note free pascal supports a variety of pascal dialects but none of them are particular modern.

My main gripes with Delphi/pascal is its additional verbosity and somewhat tedious coding practices which seem superfluous in some cases. Now I dont mind typing a bit extra to make code cleaner and more legible but I have a few ideas which would reduce needless typing and improve clarity at the same time.

(feel free to shoot down any of these ideas)

1. Memory management. Delphi is quite incosistent here by allowing component classes to be auto managed (via their owner) whilst non-component class have to be manually managed. The best solution I can think for this is to reference count non-component classes. This should be safe for TObjects but obviously not for Tcomponent descendants (cf circular reference problem) so a protected variable could be added to TObject to specify whether to ref count it or not (with TComponent turning it off). This will improve legibility of code too as there will no longer be a need for having loads of try..finally statements to free stuff. Backwards compatibility should not be affected so if you call a free method it will free it whatever its ref count.

2. For Each. Its in Delphi 2005 and every modern language implements it.

3. Increasing the number of base types to include common stuff like lists (Tlist, TStringList). Python does this nicely and theres no reason Pascal cant. Base types are dynamically allocated and reference counted so theres no need to explicitly create or destroy them. The following code would therefore work :

var strlist : Stringlist;
begin
   strlist.add('mystring');
end;

(note I did not use a T in front of StringList so as to distinguish it from non-base types and also to maintain backwards compatibility with existing code that uses TStringList conventionally)

jamie.

Reply by email

Jose Manuel
to Jamie McCracken
Show quoted text 6 lines
(note I did not use a T in front of StringList so as to distinguish it from non-base types and also to maintain backwards compatibility with existing code that uses TStringList conventionally) jamie.

Are you always drunk?

Reply by email

peter green
to Jamie McCracken: Just wandering if any of you are interested in modernising Pascal which is looking quite…
Show quoted text 11 lines
1. Memory management. Delphi is quite incosistent here by allowing component classes to be auto managed (via their owner) whilst non-component class have to be manually managed. The best solution I can think for this is to reference count non-component classes. This should be safe for TObjects but obviously not for Tcomponent descendants (cf circular reference problem) so a protected variable could be added to TObject to specify whether to ref count it or not (with TComponent turning it off). This will improve legibility of code too as there will no longer be a need for having loads of try..finally statements to free stuff. Backwards compatibility should not be affected so if you call a free method it will free it whatever its ref count.

there would be quite some performance penalty to this. Refcounting is actually quite expensive especially because of the way it adds loads of extra try-finally blocks to the asm.

furthermore how do you propose to do such a thing while maintaining the capability to typecast safely back and forward between object types and integer types which so much code relys on.

Reply by email

Jamie McCracken
to peter green
peter green wrote 15 lines
>1. Memory management. Delphi is quite incosistent here by allowing >component classes to be auto managed (via their owner) whilst >non-component class have to be manually managed. The best solution I can >think for this is to reference count non-component classes. This should >be safe for TObjects but obviously not for Tcomponent descendants (cf >circular reference problem) so a protected variable could be added to >TObject to specify whether to ref count it or not (with TComponent >turning it off). This will improve legibility of code too as there will >no longer be a need for having loads of try..finally statements to free >stuff. Backwards compatibility should not be affected so if you call a >free method it will free it whatever its ref count. there would be quite some performance penalty to this. Refcounting is actually quite expensive especially because of the way it adds loads of extra try-finally blocks to the asm.

not much more cause you would still need try finally blocks in most cases for robust code. In other cases you can use a pointer to avoid overhead (IE in cases where you are not actually creating instances).

furthermore how do you propose to do such a thing while maintaining the capability to typecast safely back and forward between object types and integer types which so much code relys on.

not sure I understand the problem here. Can you give an example?

jamie.

Show quoted text 7 lines
_______________________________________________ fpc-devel maillist - http://lists.freepascal.org/mailman/listinfo/fpc-devel

Reply by email

DrDiettrich
to Jamie McCracken: Just wandering if any of you are interested in modernising Pascal which is looking quite…
Jamie McCracken wrote
Just wandering if any of you are interested in modernising Pascal which is looking quite dated when compared to modern languages like Python. I note free pascal supports a variety of pascal dialects but none of them are particular modern.

I'd suggest Oberon or a similar language. I know that some companies would use Oberon, if only that system/language came with an interactive debugger. These companies are willing to spend real money for a modernised Oberon system.

I never really used Oberon or Modula myself, due to problems with the systems in those old days, but an implementation shouldn't be a real problem these days. I also would be interested in contributing to such a project.

Show quoted text 5 lines
My main gripes with Delphi/pascal is its additional verbosity and somewhat tedious coding practices which seem superfluous in some cases. Now I dont mind typing a bit extra to make code cleaner and more legible but I have a few ideas which would reduce needless typing and improve clarity at the same time.

IMO the best solution for (almost) all of your problems were garbage collection. GC is part of Oberon, and it would fit into .NET/DotGNU as well.

1. Memory management. Delphi is quite incosistent here by allowing component classes to be auto managed (via their owner) whilst non-component class have to be manually managed.

In a GUI a management overhead is acceptable, in other classes IMO it's not a good idea: the management costs time and requires rigid design rules, what makes it not very convenient and safe to use.

no longer be a need for having loads of try..finally statements

GC ;-)

2. For Each. Its in Delphi 2005 and every modern language implements it.

Might be found in Oberon?

3. Increasing the number of base types to include common stuff like lists (Tlist, TStringList). Python does this nicely and theres no reason Pascal cant. Base types are dynamically allocated and reference counted so theres no need to explicitly create or destroy them.

Some people will kill you for introducing additional implicit try-finally blocks ;-)

Again: GC, everything else is a matter of the class library.

var strlist : Stringlist; begin strlist.add('mystring'); end;
Perhaps a simple extension:
var strlist: new {T}Stringlist;

DoDi

Reply by email

Marco van de Voort
to Jamie McCracken: Just wandering if any of you are interested in modernising Pascal which is looking quite…
Just wandering if any of you are interested in modernising Pascal which is looking quite dated when compared to modern languages like Python. I note free pascal supports a variety of pascal dialects but none of them are particular modern.

Could you define modern? Nearly all "modern" languages seem to leverage early seventies C syntax, except Python of course, which, as Florian already adeptly remarked is more a sixties syntax.

My main gripes with Delphi/pascal is its additional verbosity and

Ah, verbosity is one of the things I actually like about it.

somewhat tedious coding practices which seem superfluous in some cases.

.... because it saves bugs, and costly debugging.

Show quoted text 9 lines
Now I dont mind typing a bit extra to make code cleaner and more legible but I have a few ideas which would reduce needless typing and improve clarity at the same time. (feel free to shoot down any of these ideas) 1. Memory management. Delphi is quite incosistent here by allowing component classes to be auto managed (via their owner) whilst non-component class have to be manually managed.

They are not really auto managed, nor refcounted. They simply exist for the near entire life of the program (except startup and shutdown), or are manually created.

Show quoted text 6 lines
The best solution I can think for this is to reference count non-component classes. This should be safe for TObjects but obviously not for Tcomponent descendants (cf circular reference problem) so a protected variable could be added to TObject to specify whether to ref count it or not (with TComponent turning it off).

This is impossible. 90% of the language wouldn't work anymore as it does now, and besides, it would be dog slow. Could be that it gets slower than Boehm GC even.

This will improve legibility of code too as there will no longer be a need for having loads of try..finally statements to free stuff.

How do you free objects then? Those try..finally's are there actually because of the refcounting.

2. For Each. Its in Delphi 2005 and every modern language implements it.

Yeah, and I still wonder why. There is nothing to gain with it.

3. Increasing the number of base types to include common stuff like lists (Tlist, TStringList). Python does this nicely and theres no reason Pascal cant.

I partially agree with you that the container types of Delphi aren't the strongest point. However the solution is to make a lib with better types.

Pulling it in the compiler with ref counting however is something totally else, and will make them dog slow.

Keep in mind that I can walk through 5-6 million (real world) objects in just under 2 secs in Pascal/Delphi on an already old 2GHz P IV, and inspect them all. Try that in Python.

Reply by email

Jamie McCracken
to Marco van de Voort
Marco van de Voort wrote 14 lines
>The best solution I can >think for this is to reference count non-component classes. This should >be safe for TObjects but obviously not for Tcomponent descendants (cf >circular reference problem) so a protected variable could be added to >TObject to specify whether to ref count it or not (with TComponent >turning it off). This is impossible. 90% of the language wouldn't work anymore as it does now, and besides, it would be dog slow. Could be that it gets slower than Boehm GC even.

Thats very pessimistic!

Why wouldn't existing code work with this? If you call free on an object it would ignore the ref count and free it - so it wont alter how existing code works so it certainly should not break anything. You can always create a new dialect that has this if you're worried about existing code (I take it most of the existing code is using FPC mode dialect).

Is'nt the overhead for reference counting negligible compared to having try..finally blocks which the programmer would have to add anyway if we didn't refcount them?

(there also pointers for cases where you dont need try..finally)

Show quoted text 8 lines
>This will improve legibility of code too as there will no longer be a need >for having loads of try..finally statements to free stuff. How do you free objects then? Those try..finally's are there actually because of the refcounting.

The system frees them automatically - there no need to manually add try..finally statements. The benefit is clearer code and less chance of a memory leak.

Show quoted text 5 lines
>2. For Each. Its in Delphi 2005 and every modern language implements it. Yeah, and I still wonder why. There is nothing to gain with it.

one less variable to manually declare

Show quoted text 16 lines
>3. Increasing the number of base types to include common stuff like >lists (Tlist, TStringList). Python does this nicely and theres no reason >Pascal cant. I partially agree with you that the container types of Delphi aren't the strongest point. However the solution is to make a lib with better types. Pulling it in the compiler with ref counting however is something totally else, and will make them dog slow. Keep in mind that I can walk through 5-6 million (real world) objects in just under 2 secs in Pascal/Delphi on an already old 2GHz P IV, and inspect them all. Try that in Python.

I know python is dog slow and its far from perfect but it does have clearer and more compact code than pascal as a result of ref counting stuff and improving the base container types.

jamie.

Reply by email

Florian Klaempfl
to Jamie McCracken
Jamie McCracken wrote 31 lines
Marco van de Voort wrote: > >> The best solution I can think for this is to reference count >> non-component classes. This should be safe for TObjects but obviously >> not for Tcomponent descendants (cf circular reference problem) so a >> protected variable could be added to TObject to specify whether to >> ref count it or not (with TComponent turning it off). > > > > This is impossible. 90% of the language wouldn't work anymore as it does > now, and besides, it would be dog slow. Could be that it gets slower than > Boehm GC even. > > Thats very pessimistic! Why wouldn't existing code work with this? If you call free on an object it would ignore the ref count and free it - so it wont alter how existing code works so it certainly should not break anything. You can always create a new dialect that has this if you're worried about existing code (I take it most of the existing code is using FPC mode dialect). Is'nt the overhead for reference counting negligible compared to having try..finally blocks which the programmer would have to add anyway if we didn't refcount them? (there also pointers for cases where you dont need try..finally)

Ref. counting creates an huge amount of implicit try .. finally blocks which have a heavy impact on speed.

Consider

function f1(myobject : tobject) : tobject;
   begin
   end;
function f2(myobject : iunknown) : iunknown;
   begin
   end;
var
   o : tobject;
   i : iunknown;
begin
   f1(o);
   f2(i);
end.

lets have a look at the ouput (IUnknown is ref. counted):

# [2] begin
.globl P$PROGRAM_F1$TOBJECT$$TOBJECT
P$PROGRAM_F1$TOBJECT$$TOBJECT:
# Temps allocated between ebp+0 and ebp+0
pushl %ebp
movl %esp,%ebp
# Var myobject located in register
# Var $result located in register
movl %eax,%edx
# [3] result:=myobject;
movl %edx,%ecx
# [4] end;
movl %ecx,%eax
leave
ret

.section .text
.balign 4
.balign 4
# [7] begin
.globl P$PROGRAM_F2$IUNKNOWN$$IUNKNOWN
P$PROGRAM_F2$IUNKNOWN$$IUNKNOWN:
# Temps allocated between ebp-48 and ebp-4
pushl %ebp
movl %esp,%ebp
subl $48,%esp
movl %ebx,-48(%ebp)
# Var myobject located in register
# Var $result located at ebp-4
movl %eax,%ebx
movl $0,-4(%ebp)
leal -16(%ebp),%eax
movl %eax,%ecx
leal -40(%ebp),%eax
movl %eax,%edx
movl $1,%eax
call FPC_PUSHEXCEPTADDR
call FPC_SETJMP
pushl %eax
testl %eax,%eax
jne .L15
# [8] result:=myobject;
movl %ebx,%edx
leal -4(%ebp),%eax
call fpc_intf_assign
.L15:
call FPC_POPADDRSTACK
popl %eax
testl %eax,%eax
je .L16
# [9] end;
movl $INIT__SYSTEM_IUNKNOWN,%edx
leal -4(%ebp),%eax
call fpc_finalize
call FPC_RERAISE
.L16:
movl -4(%ebp),%eax
movl -48(%ebp),%ebx
leave
ret

[...]

# [16] f1(o);
movl %esi,%eax
call P$PROGRAM_F1$TOBJECT$$TOBJECT
# [17] f2(i);
leal -44(%ebp),%edx
movl %edx,%eax
call FPC_INTF_DECR_REF
movl %ebx,%eax
call P$PROGRAM_F2$IUNKNOWN$$IUNKNOWN
movl %eax,-44(%ebp)
leal -44(%ebp),%edx
movl %edx,%eax
call FPC_INTF_DECR_REF
movl $0,-44(%ebp)

So if you look at this, you know why classes aren't ref. counted. And there is no chance to avoid the code generated for the interface.

Reply by email

Jamie McCracken
to Florian Klaempfl
Florian Klaempfl wrote
So if you look at this, you know why classes aren't ref. counted. And there is no chance to avoid the code generated for the interface.

Yes that does look very bad but if you passed a pointer to the ref counted object as the function arguement then you would not get all that overhead, yes?

jamie.

Show quoted text 6 lines
_______________________________________________ fpc-devel maillist - http://lists.freepascal.org/mailman/listinfo/fpc-devel

Reply by email

Marco van de Voort
to Jamie McCracken: Thats very pessimistic! Why wouldn't existing code work with this? If you call free on an…
Show quoted text 16 lines
Marco van de Voort wrote: > >>The best solution I can >>think for this is to reference count non-component classes. This should >>be safe for TObjects but obviously not for Tcomponent descendants (cf >>circular reference problem) so a protected variable could be added to >>TObject to specify whether to ref count it or not (with TComponent >>turning it off). > > > This is impossible. 90% of the language wouldn't work anymore as it does > now, and besides, it would be dog slow. Could be that it gets slower than > Boehm GC even. Thats very pessimistic!

No, that is reading literature.

Why wouldn't existing code work with this? If you call free on an object it would ignore the ref count and free it

That messes up the ref counts. Make some sample code that throws some objects to different procs and keep track of ref counts.

Show quoted text 5 lines
Is'nt the overhead for reference counting negligible compared to having try..finally blocks which the programmer would have to add anyway if we didn't refcount them? (there also pointers for cases where you dont need try..finally)

Which one?

Show quoted text 7 lines
> How do you free objects then? Those try..finally's are there actually because > of the refcounting. > The system frees them automatically - there no need to manually add try..finally statements. The benefit is clearer code and less chance of a memory leak.

Which system? The ref counting system pretty much _is_ the try finally :)

Show quoted text 6 lines
>>2. For Each. Its in Delphi 2005 and every modern language implements it. > > > Yeah, and I still wonder why. There is nothing to gain with it. one less variable to manually declare

Implement something in lazarus that auto-adds the variable to the local var section. No need for language extensions.

D2005 reasons for it lie in the .NET, but those don't apply to the non .NET world. (yes it works in d2005/win32 too, but it is useless there)

Show quoted text 13 lines
> I partially agree with you that the container types of Delphi aren't the > strongest point. However the solution is to make a lib with better types. > > Pulling it in the compiler with ref counting however is something totally > else, and will make them dog slow. > > Keep in mind that I can walk through 5-6 million (real world) objects in > just under 2 secs in Pascal/Delphi on an already old 2GHz P IV, and inspect > them all. Try that in Python. I know python is dog slow and its far from perfect but it does have clearer and more compact code than pascal as a result of ref counting stuff and improving the base container types.

I do not agree. /me thinks Python looks messy.

Reply by email

Florian Klaempfl
to Jamie McCracken: Yes that does look very bad but if you passed a pointer to the ref counted object as the…
Jamie McCracken wrote 10 lines
Florian Klaempfl wrote: > > So if you look at this, you know why classes aren't ref. counted. And > there is no chance to avoid the code generated for the interface. > Yes that does look very bad but if you passed a pointer to the ref counted object as the function arguement then you would not get all that overhead, yes?

An interface _is_ a pointer? You have always to increase the ref. count when creating a new reference to ref. counted class. Ref. counting isn't that easy as you might think. Multi threading and exceptions make it rather complicated.

Reply by email

Jamie McCracken
to Marco van de Voort: No, that is reading literature. That messes up the ref counts. Make some sample code that…
Marco van de Voort wrote 11 lines
>>>2. For Each. Its in Delphi 2005 and every modern language implements it. >> >> >>Yeah, and I still wonder why. There is nothing to gain with it. > >one less variable to manually declare Implement something in lazarus that auto-adds the variable to the local var section. No need for language extensions.

My mistake it actually avoids initialising the loop variable rather than not declaring it:

for i in myarray do
   myarray[i] := 0;

as opposed to

for i := low(myarray) to high (myarray) do
    myarray[i] := 0;

I think the for..in is much clearer and more compact (it works for sets, arrays and other enumerated types)

jamie.

Show quoted text 5 lines
_______________________________________________ fpc-devel maillist - http://lists.freepascal.org/mailman/listinfo/fpc-devel

Reply by email

Jonas Maebe
to Jamie McCracken
Jamie McCracken wrote 14 lines
My mistake it actually avoids initialising the loop variable rather than not declaring it: for i in myarray do myarray[i] := 0; as opposed to for i := low(myarray) to high (myarray) do myarray[i] := 0; I think the for..in is much clearer and more compact (it works for sets, arrays and other enumerated types)

I'm not sure whether it's that clear. In case of a set, I agree, but for an array it would seem more logical to me if i iterated over all values in the array, not over all indexes.

Jonas

Reply by email

Marco van de Voort
to Jamie McCracken: My mistake it actually avoids initialising the loop variable rather than not declaring it:
Show quoted text 14 lines
My mistake it actually avoids initialising the loop variable rather than not declaring it: for i in myarray do myarray[i] := 0; as opposed to for i := low(myarray) to high (myarray) do myarray[i] := 0; I think the for..in is much clearer and more compact (it works for sets, arrays and other enumerated types)

The constructs are not that frequent, so typing is not limited. I do not agree on the "clearer" part also.

From low to high is more clear than IN, since that says nothing about order.

Reply by email

Jamie McCracken
to Marco van de Voort
Marco van de Voort wrote 20 lines
>My mistake it actually avoids initialising the loop variable rather than >not declaring it: > >for i in myarray do > myarray[i] := 0; > >as opposed to > >for i := low(myarray) to high (myarray) do > myarray[i] := 0; > > >I think the for..in is much clearer and more compact (it works for sets, >arrays and other enumerated types) The constructs are not that frequent, so typing is not limited. I do not agree on the "clearer" part also. >From low to high is more clear than IN, since that says nothing about order.

Well for..in is not a replacement for the conventional For syntax in all circumstances but nevertheless it is a convenience to have (a performance advantage as well perhaps?).

If FPC is committed to Delphi compatibility then it surely must add this at some point?

jamie.

Show quoted text 8 lines
_______________________________________________ fpc-devel maillist - http://lists.freepascal.org/mailman/listinfo/fpc-devel

Reply by email

Marco van de Voort
to Jamie McCracken
Show quoted text 13 lines
Marco van de Voort wrote: > The constructs are not that frequent, so typing is not limited. I do not agree on > the "clearer" part also. > >>From low to high is more clear than IN, since that says nothing about order. Well for..in is not a replacement for the conventional For syntax in all circumstances but nevertheless it is a convenience to have (a performance advantage as well perhaps?). If FPC is committed to Delphi compatibility then it surely must add this at some point?

FPC is committed to Delphi compat, but explicitely made an exception for .NET related stuff, even if it is available in Delphi/win32 too, like dotted unit paths.

Of course, no decision in a project of this magnitude is final, but there are enough useful things left to do.

Reply by email

Anton Tichawa
to Jamie McCracken: My mistake it actually avoids initialising the loop variable rather than not declaring it:
Jamie McCracken wrote 49 lines
Marco van de Voort wrote: > >>>> 2. For Each. Its in Delphi 2005 and every modern language >>>> implements it. >>> >>> >>> >>> Yeah, and I still wonder why. There is nothing to gain with it. >> >> >> one less variable to manually declare > > > > Implement something in lazarus that auto-adds the variable to the > local var > section. No need for language extensions. My mistake it actually avoids initialising the loop variable rather than not declaring it: for i in myarray do myarray[i] := 0; as opposed to for i := low(myarray) to high (myarray) do myarray[i] := 0; I think the for..in is much clearer and more compact (it works for sets, arrays and other enumerated types) jamie. > _______________________________________________ > fpc-devel maillist - > http://lists.freepascal.org/mailman/listinfo/fpc-devel > > _______________________________________________ fpc-devel maillist - http://lists.freepascal.org/mailman/listinfo/fpc-devel

What about cases where the index should run from low(myarray) to pred(high(myarray)), e. g. when bubble-sorting? In such cases, you would still have to use the "classical" form, and end up with two different syntaxes.

And please note: It's often the more difficult, and more "intelligent" algorithms like sorting, comparison, ..., where i does -not- run from 0 to n-1 but, e. g. from 0 to n-2.

I mean, the for-statement has a long but very clear syntax, and there's nothing superfluous in it. It's an assignment plus a bound, with an implicit increment.

Maybe in 90 % of the cases a "for i in blah" would be shorter, but you'd lose control over what actually happens, as the next step would probably be that the compiler may determine the direction or even order of the iterations.

IMHO, this general critics about losing control holds for your other proposals, too. Pascal is and will always be a -procedural- language with OO extensions, and is thus well suited for writing things like drivers or OSs, too; whereas your proposals, as far as I understand them, would lead to something like a compiled spreadsheet language like MS excel.

Reply by email

Jamie McCracken
to DrDiettrich: I'd suggest Oberon or a similar language. I know that some companies would use Oberon, if…
DrDiettrich wrote 10 lines
>My main gripes with Delphi/pascal is its additional verbosity and >somewhat tedious coding practices which seem superfluous in some cases. >Now I dont mind typing a bit extra to make code cleaner and more legible >but I have a few ideas which would reduce needless typing and improve >clarity at the same time. IMO the best solution for (almost) all of your problems were garbage collection. GC is part of Oberon, and it would fit into .NET/DotGNU as well.

GC is very inefficient with memory and current implementations tend to cost a lot performance wise too.

GC gets a lot slower with the more objects you create.

Mixing unmanaged with managed code cause severe performance issues (marshalling penalties calling ummanaged c functions)

Interoperability with C gets increasingly difficult. Writing bindings becomes much more complicated and time consuming.

GC is therefore a poor choice for a high performance language IMHO.

Show quoted text 16 lines
>1. Memory management. Delphi is quite incosistent here by allowing >component classes to be auto managed (via their owner) whilst >non-component class have to be manually managed. In a GUI a management overhead is acceptable, in other classes IMO it's not a good idea: the management costs time and requires rigid design rules, what makes it not very convenient and safe to use. >no longer be a need for having loads of try..finally statements GC ;-)

It might be better to do this in an IDE and get it to add the try..finally crap.

EG if I say use the @ symbol to indicate a variable should be auto created and destroyed then I could have :

var st@, st2@ : tstringlist;
begin
   st.add('some text');
   st2.add('some more text');
end;

which the IDE would translate behind the scenes to :

var st, st2 : tstringlist;
begin
    st := tstringlist.create;
    try
      st2 :=  tstringlist.create;
      try
        st .add('some text');
        st2.add('some more text');
     finally
       st2.free;
     end;
   finally
     st.free;
   end;
end;

I do need an IDE anyhow for container based GTK2/Gnome2/Glade apps so maybe I ought to start writing one that implements this.

jamie.

Reply by email

Michael Van Canneyt
to Jamie McCracken
Jamie McCracken wrote 36 lines
It might be better to do this in an IDE and get it to add the try..finally crap. EG if I say use the @ symbol to indicate a variable should be auto created and destroyed then I could have : var st@, st2@ : tstringlist; begin st.add('some text'); st2.add('some more text'); end; which the IDE would translate behind the scenes to : var st, st2 : tstringlist; begin st := tstringlist.create; try st2 := tstringlist.create; try st .add('some text'); st2.add('some more text'); finally st2.free; end; finally st.free; end; end; I do need an IDE anyhow for container based GTK2/Gnome2/Glade apps so maybe I ought to start writing one that implements this.

Why don't you see if you can get this implemented as an add-on in Lazarus ? You get the rest of the IDE for free.

Michael.

Reply by email

Jonas Maebe
to Jamie McCracken: GC is very inefficient with memory and current implementations tend to cost a lot…
Jamie McCracken wrote 6 lines
> IMO the best solution for (almost) all of your problems were garbage > collection. GC is part of Oberon, and it would fit into .NET/DotGNU as > well. GC is very inefficient with memory and current implementations tend to cost a lot performance wise too.

It's *much* faster than reference counting everything. Reference counting is more or less the slowest garbage collection technique there is (except if only very few objects have to garbage collected). It also can't deal with circular references.

GC gets a lot slower with the more objects you create.

Reference counting too.

Show quoted text 5 lines
Mixing unmanaged with managed code cause severe performance issues (marshalling penalties calling ummanaged c functions) Interoperability with C gets increasingly difficult. Writing bindings becomes much more complicated and time consuming.

You get the same problem if you pass a reference counted structure to C (or any other language).

Jonas

Reply by email

Jamie McCracken
to Michael Van Canneyt: Why don't you see if you can get this implemented as an add-on in Lazarus ? You get the…
Show quoted text 7 lines
> I do need an IDE anyhow for container based GTK2/Gnome2/Glade apps so > maybe I ought to start writing one that implements this. Why don't you see if you can get this implemented as an add-on in Lazarus ? You get the rest of the IDE for free.

In the short term yes (although I use delphi 5 under Wine for editing stuff - its nicer on my eyes than GTK1) but long term I would love to have a GTK2 IDE integrated with Glade3 for writing HIG compliant apps as I do all my UI stuff using Glade.

jamie.

Reply by email

Jamie McCracken
to Jonas Maebe: It's *much* faster than reference counting everything. Reference counting is more or less…
Jonas Maebe wrote 16 lines
On 24 feb 2005, at 13:51, Jamie McCracken wrote: >> IMO the best solution for (almost) all of your problems were garbage >> collection. GC is part of Oberon, and it would fit into .NET/DotGNU as >> well. > > > GC is very inefficient with memory and current implementations tend to > cost a lot performance wise too. It's *much* faster than reference counting everything. Reference counting is more or less the slowest garbage collection technique there is (except if only very few objects have to garbage collected). It also can't deal with circular references.

Thats why I would want ref count for Tobjects and not Tcomponents. Partial ref counting should be faster than doing GC on everything.

Show quoted text 5 lines
> GC gets a lot slower with the more objects you create. Reference counting too.

GC is worse here because the pointers are not static in GC so referencing an object means looking up the actual address in a lookup list (so more overhead whenever you call a method or pass an object reference). GC does not use the stack efficiently and has to use a lot of heap so its extremely inefficient. Ever see GC compact swap?

Show quoted text 10 lines
> Mixing unmanaged with managed code cause severe performance issues > (marshalling penalties calling ummanaged c functions) > > Interoperability with C gets increasingly difficult. Writing bindings > becomes much more complicated and time consuming. You get the same problem if you pass a reference counted structure to C (or any other language).

yes but with GC everything would have to be managed whilst just ref counting Tobject would not cause these problems (you cant access a tobject from C anyhow). Tobject is the only weak point in Delphi's memory management so I would say using GC to correct that would be overkill.

jamie.

Show quoted text 10 lines
Jonas _______________________________________________ fpc-devel maillist - http://lists.freepascal.org/mailman/listinfo/fpc-devel

Reply by email

Sebastian Kaliszewski
to Jamie McCracken: GC is very inefficient with memory and current implementations tend to cost a lot…
Jamie McCracken wrote 6 lines
> IMO the best solution for (almost) all of your problems were garbage > collection. GC is part of Oberon, and it would fit into .NET/DotGNU as > well. GC is very inefficient with memory and current implementations tend to cost a lot performance wise too.

This is simply not true.
If you don't belive then check the following:
1. Look with google for Quake rewrittiend into .Net (i.e. GC stuff) -- surprise surprise -- difference is neglibile (<20%),
2. Look for Ocaml -- it's a heavyly GC-ed compiled language, and it's fast.

GC gets a lot slower with the more objects you create.

No worse that normal memory allocation, if GC is implemented properly.

Show quoted text 5 lines
Mixing unmanaged with managed code cause severe performance issues (marshalling penalties calling ummanaged c functions) Interoperability with C gets increasingly difficult. Writing bindings becomes much more complicated and time consuming.

I did wrote GC in C++ itself. So it binds rather well...

GC is therefore a poor choice for a high performance language IMHO.

There are live examples to the contrary. If anything, then refcounting is a poor choice for such language -- it's really slow, and when multithreading avareness is needed then is terribly slow. Once I compared multithreading aware and surely well optimised boost++ refcounter with my primitive GC, the very samy app using unoptimised GC was 2 times faster.

In case of FPC main advantage of GC for autotatically managed stuff would be avoiding of putting the unwinding code by the compiler (those implicit try .. finally thigs). Main disadvantage is that moment of resource freeing is indefinite, and this would break exiting code (hence it's unacceptable).

rgds

Sebastian Kaliszewski

Reply by email

Sebastian Kaliszewski
to Jamie McCracken: Thats why I would want ref count for Tobjects and not Tcomponents. Partial ref counting…
Jamie McCracken wrote 7 lines
> It's *much* faster than reference counting everything. Reference > counting is more or less the slowest garbage collection technique there > is (except if only very few objects have to garbage collected). It also > can't deal with circular references. Thats why I would want ref count for Tobjects and not Tcomponents. Partial ref counting should be faster than doing GC on everything.

You could do partial GC as well. No one forces yo to do GC on everything.

Show quoted text 5 lines
>> GC gets a lot slower with the more objects you create. > > Reference counting too. GC is worse here because the pointers are not static in GC.

Not true. There are GC's with static as well as unstatic pointers (compacting GC's move pointers, but there are not compating GC's as well). For example Bohm GC (for C) does not move pointers. And it's GC.

so referencing an object means looking up the actual address in a lookup list

Nonsense. It's clear you don't know how typical GC works.

(so more overhead whenever you call a method or pass an object reference).

Nope.

GC does not use the stack efficiently

Complete nonsense. GC has nothing to statck. Zero, null, nada...

and has to use a lot of heap so its extremely inefficient.

More of the same... It's simply not true.
Besides compacting GC supported directly by the langauge allows for extremely effective use of heap -- as effective as that of stack.

Ever see GC compact swap?

Better go learn more:
http://www.iecc.com/gclist/GC-faq.html

yes but with GC everything would have to be managed

Nope. See above.

Show quoted text 5 lines
whilst just ref counting Tobject would not cause these problems (you cant access a tobject from C anyhow). Tobject is the only weak point in Delphi's memory management so I would say using GC to correct that would be overkill.

Whatever. GC has different problems but you didn't touch any.

PS. there exists real-time capable GC (i.e. good for (soft)real time applications, such thing can't be said about even most non GC memory allocation systems).

rgds

Sebastian Kaliszewski

Reply by email

Nico Aragón
to Sebastian Kaliszewski
Sebastian Kaliszewski wrote
You could do partial GC as well. No one forces yo to do GC on everything.

Free Pascal object model is very flexible. If I'm not mistaken, it's compatible with Delphi so it has the same construction mechanism so it's possible to override InstanceSize, InitInstance, NewInstance and FreeInstance.

There is also a reference counting hooks for interfaces. Combining all these facilities, it's possible to create a parallel hierarchies of objects that use reference count or garbage collection.

saludos, Nico Aragón

Reply by email

Jamie McCracken
to Sebastian Kaliszewski: You could do partial GC as well. No one forces yo to do GC on everything. Not true. There…
Sebastian Kaliszewski wrote 12 lines
On Thursday 24 February 2005 14:57, Jamie McCracken wrote: >>It's *much* faster than reference counting everything. Reference >>counting is more or less the slowest garbage collection technique there >>is (except if only very few objects have to garbage collected). It also >>can't deal with circular references. > >Thats why I would want ref count for Tobjects and not Tcomponents. >Partial ref counting should be faster than doing GC on everything. You could do partial GC as well. No one forces yo to do GC on everything.

You can but that would suck! Mixing unmanaged with managed code needs a lot of effort and the performance would be detrimental (marshalling of unmanaged objects/types).

Show quoted text 13 lines
>>>GC gets a lot slower with the more objects you create. >> >>Reference counting too. > >GC is worse here because the pointers are not static in GC. Not true. There are GC's with static as well as unstatic pointers (compacting GC's move pointers, but there are not compating GC's as well). For example Bohm GC (for C) does not move pointers. And it's GC.

I was referring to compacting GCs - non compacting ones are impractical to use unless you have tons of free memory and all the modern ones including .Net's generational one is incremently compacting.

Show quoted text 9 lines
>so >referencing an object means looking up the actual address in a lookup >list Nonsense. It's clear you don't know how typical GC works.

It does in compacting GCs (how else can you get the address of an object?)

Show quoted text 8 lines
>(so more overhead whenever you call a method or pass an object >reference). Nope.

Yes for compacting GCs

Show quoted text 7 lines
>GC does not use the stack efficiently Complete nonsense. GC has nothing to statck. Zero, null, nada...

Exactly it has to use a managed heap which is slower! Unmanaged code can use the stack for greater speed. There is a lot of talk about GCs being almost as efficient as unmanaged code but I have never seen an implementation that comes close to that. C# and Java both have poor performance in large apps where you have lots of objects (delphi 2005 is also written in .net and it is very sluggish too).

Show quoted text 10 lines
>and has to use a lot >of heap so its extremely inefficient. More of the same... It's simply not true. Besides compacting GC supported directly by the langauge allows for extremely effective use of heap -- as effective as that of stack.

But heap is slower than stack.

Show quoted text 8 lines
>Ever see GC compact swap? Better go learn more: http://www.iecc.com/gclist/GC-faq.html
Show quoted text 7 lines
>yes but with GC everything would have to be managed Nope. See above.

In practice yes as above unless you can live with all the extra inefficiency.

jamie.

Reply by email

Jamie McCracken
to Sebastian Kaliszewski: This is simply not true. If you don't belive then check the following: 1. Look with…
Sebastian Kaliszewski wrote 15 lines
On Thursday 24 February 2005 13:51, Jamie McCracken wrote: >>IMO the best solution for (almost) all of your problems were garbage >>collection. GC is part of Oberon, and it would fit into .NET/DotGNU as >>well. > >GC is very inefficient with memory and current implementations tend to >cost a lot performance wise too. This is simply not true. If you don't belive then check the following: 1. Look with google for Quake rewrittiend into .Net (i.e. GC stuff) -- surprise surprise -- difference is neglibile (<20%), 2. Look for Ocaml -- it's a heavyly GC-ed compiled language, and it's fast.

Ocaml is a functional lanaguge. Procedural languages tend to not work as efficiently with GCs from what I have seen.

Show quoted text 8 lines
>GC gets a lot slower with the more objects you create. > No worse that normal memory allocation, if GC is implemented properly.

Show me one that works "properly" with procedural lanaguages (java, C#, pascal etc) otherwise it just appears hypothetical. I dont doubt some languages are more suitable for this like LISP.

Show quoted text 12 lines
>Mixing unmanaged with managed code cause severe performance issues >(marshalling penalties calling ummanaged c functions) > >Interoperability with C gets increasingly difficult. Writing bindings >becomes much more complicated and time consuming. I did wrote GC in C++ itself. So it binds rather well...

not a compacting one then - if we use compacting then you lose pointers and Pchars ergo all the existing bindings would be useless and would need to be rewritten manually (without the aid of H2pas!)

Show quoted text 10 lines
>GC is therefore a poor choice for a high performance language IMHO. There are live examples to the contrary. If anything, then refcounting is a poor choice for such language -- it's really slow, and when multithreading avareness is needed then is terribly slow. Once I compared multithreading aware and surely well optimised boost++ refcounter with my primitive GC, the very samy app using unoptimised GC was 2 times faster.

Okay maybe its not so clear cut but Im fairly sure that to implement things correctly we would have to GC everything and the overall overhead might cost more than a partial ref counting of some Tobjects.

However if your confident you can write a modern GC that would work very efficiently with FPC then by all means be my guest - I would be very interested to see it. Im not opposed to GC ing pascal if the performance/memory usuage is reasonably good.

jamie.

Reply by email

Florian Klaempfl
to Jamie McCracken
Jamie McCracken wrote
> I did wrote GC in C++ itself. So it binds rather well... not a compacting one then -

Ref. counting isn't compacting either?

if we use compacting then you lose pointers and Pchars ergo all the existing bindings would be useless and would need to be rewritten manually (without the aid of H2pas!)

Reply by email

Jamie McCracken
to Florian Klaempfl
Florian Klaempfl wrote 10 lines
Jamie McCracken wrote: >> I did wrote GC in C++ itself. So it binds rather well... > > > > not a compacting one then - Ref. counting isn't compacting either?

Not an issue cause memory allocation is conventional when ref counting.

GCs allocate memory from a managed heap which fragments heavily ergo it needs compacting to be able to recycle free memory blocks more efficiently. (most GCs achieve fast memory allocation by always allocating from fresh untouched memory so you can quickly run out of memory if they dont compact exisiting used memory).

jamie.

Reply by email

Eric Grange
to Sebastian Kaliszewski: This is simply not true. If you don't belive then check the following: 1. Look with…
This is simply not true. If you don't belive then check the following: 1. Look with google for Quake rewrittiend into .Net (i.e. GC stuff) -- surprise surprise -- difference is neglibile (<20%),

Though if you study more what they compare, you'll find that their .Net version is actually running at about half-speed of the commercial version, as they only compare their .Net version against a non-.Net version of the same code running under their compiler, and surprise surprise, to do that they had to change enough of the code to register a significant perf drop.

Also, Quake isn't really a GC test as the code allocates most of its data only once, and then almost never uses dynamic memory.

No worse that normal memory allocation, if GC is implemented properly.

Actually, that's incorrect, manual memory allocation has a constant complexity cost if done properly (each alloc/free has a constant CPU cost, whatever the amount of objects allocated or their size, just don't use old fashioned linked-lists memory managers), while theoretically- best-case-GC still isn't constant time (and practical ones are more like O(n²)), GC memory allocation and heap compaction patterns are quite cache unfriendly and will require partial or total freezes while they happen.

Eric

Reply by email

Marco van de Voort
to Eric Grange
Show quoted text 9 lines
> This is simply not true. > If you don't belive then check the following: > 1. Look with google for Quake rewrittiend into .Net (i.e. GC stuff) -- > surprise surprise -- difference is neglibile (<20%), cost, whatever the amount of objects allocated or their size, just don't use old fashioned linked-lists memory managers), while theoretically- best-case-GC still isn't constant time (and practical ones are more like O(n?)), GC memory allocation and heap compaction patterns are quite cache unfriendly and will require partial or total freezes while they happen.

In the stories about old software that is updated or reimplemented with GC/.NET/Java there are typical several caveats:

- the working set with boehm GC's is typically at least twice as large(good rule of thumb) as manual allocation. Using non GCed types, explicit GC calls at timed points and setting refs to NIL can improve this slightly, but this is tuning, not a fair comparison.
- Startup time is also nearly always affected severely, even if normal program execution is bearable
- Critical parts are often handoptimized by using a lot of non GCed types (like int), this is not a typical programming method for these languages, but outright expert tuning.
- Older applications are often not CPU limited, but have a bottleneck somewhere else. So there is a hidden CPU performanceloss in the comparison, since the managed app _is_ CPU limited apparently.
- (rare) HT/SMP usage.

Same with GC faqs.

Reply by email

Jonas Maebe
to Marco van de Voort
Marco van de Voort wrote
- Critical parts are often handoptimized by using a lot of non GCed types (like int), this is not a typical programming method for these languages, but outright expert tuning.

Critical paths are also optimized in our compiler to not use ansistrings, because reference counting is also slow. The argument is not about whether or not we should make Pascal entirely GC'd, but about whether reference counting is better/worse than other garbage collection techniques if you have a significant amount of GC'd objects.

Jonas

Reply by email

Marco van de Voort
to Jonas Maebe
Show quoted text 9 lines
On 25 feb 2005, at 15:49, Marco van de Voort wrote: > - Critical parts are often handoptimized by using a lot of non GCed > types > (like int), this is not a typical programming method for these > languages, but outright expert tuning. Critical paths are also optimized in our compiler to not use ansistrings, because reference counting is also slow.

And I was talking more specific about the Quake II benchmark that was brought. Any tweaks would already be on top of the existing ones in the code.

The argument is not about whether or not we should make Pascal entirely GC'd, but about whether reference counting is better/worse than other garbage collection techniques if you have a significant amount of GC'd objects.

The Quake II benchmark was used to "prove" that full blown (I assume Boehm) GC was not slow. Q II, as tuned app, is probably already using primitive types heavily, thus not a poster child for this benchamark

Reply by email

Jamie McCracken
to Marco van de Voort
Marco van de Voort wrote 26 lines
>On 25 feb 2005, at 15:49, Marco van de Voort wrote: > > >>- Critical parts are often handoptimized by using a lot of non GCed >>types >> (like int), this is not a typical programming method for these >> languages, but outright expert tuning. > >Critical paths are also optimized in our compiler to not use >ansistrings, because reference counting is also slow. And I was talking more specific about the Quake II benchmark that was brought. Any tweaks would already be on top of the existing ones in the code. >The argument is not about whether or not we should make Pascal entirely >GC'd, but about whether reference counting is better/worse than other >garbage collection techniques if you have a significant amount of GC'd >objects. The Quake II benchmark was used to "prove" that full blown (I assume Boehm) GC was not slow. Q II, as tuned app, is probably already using primitive types heavily, thus not a poster child for this benchamark

And the same also applies to functional languages like ocaml and LISP which use primitive types a lot. I am sceptical because every app that I have seen that makes heavy use of *objects* is slow when GC'ed (cf Eclipse compiled with gcj - IE even with no bytecode it is sluggish).

However rather than having endless arguements about this is faster or slower than that what we really need is a benchmark for FPC with some GC'ing if some of the FPC developers are willing to investigate this (which is probably unlikely given they have enough on their plate with FPC 2.0).

jamie.

Show quoted text 7 lines
_______________________________________________ fpc-devel maillist - http://lists.freepascal.org/mailman/listinfo/fpc-devel

Reply by email

Sebastian Kaliszewski
to Eric Grange: Though if you study more what they compare, you'll find that their .Net version is…
Eric Grange wrote 6 lines
> No worse that normal memory allocation, if GC is implemented properly. Actually, that's incorrect, manual memory allocation has a constant complexity cost if done properly (each alloc/free has a constant CPU cost, whatever the amount of objects allocated or their size, just don't use old fashioned linked-lists memory managers),

1. This is not "entirely true" (the cost is at best logarithmic on the number of objects or your allocator has terrible fragmentation)
2. You have to initialise your allocated data anyway.
3. In real life often you have virtual memory system underneath and there is no guarantee whatosoever.

while theoretically- best-case-GC still isn't constant time

GC allocation (in case of compacting collecotr) is constant time trivially, all the cost is in compacting which is linear.

(and practical ones are more like O(n²)),

Huh? If anything tracing is (trivially) O(m) where m is number of pointers of memory and in all but few contrived examples m<n (where n is amount of allocated memory). Then most object die young so they are traced once. When you amortise the costs you'll see that tracing is allmost constant per object.

Then you can go for non tracing GC, use your "nearly" consttime alloc/dealloc and all the overhead is tracing (which is also nearly consttime per object)

GC memory allocation and heap compaction patterns are quite cache unfriendly and will require partial or total freezes while they happen.

compacting GC allocation is rather more cache friendly than non GC one. Compacting Collection is not cache friendly though.

Eric

rgds

Sebastian Kaliszewski

Reply by email

Florian Klaempfl
to Marco van de Voort: And I was talking more specific about the Quake II benchmark that was brought. Any tweaks…
Marco van de Voort wrote 11 lines
>The argument is not about whether or not we should make Pascal entirely >GC'd, but about whether reference counting is better/worse than other >garbage collection techniques if you have a significant amount of GC'd >objects. The Quake II benchmark was used to "prove" that full blown (I assume Boehm) GC was not slow. Q II, as tuned app, is probably already using primitive types heavily, thus not a poster child for this benchamark

Well, for a real Boehm test the FPC compiler should be used, I guess it makes a much more use of the heap than other apps :)

Reply by email

Eric Grange
to Sebastian Kaliszewski: 1. This is not "entirely true" (the cost is at best logarithmic on the number of objects…
1. This is not "entirely true" (the cost is at best logarithmic on the number of objects or your allocator has terrible fragmentation)

Not really, you can use a memory map and achieve much lower fragmentation than classic memory managers (cf. "FastMM" submissions in the FastCode project in the b.p.d.basm borland ng). In practice, you're memory usage will be a lot lower than that of a GC.

2. You have to initialise your allocated data anyway.

That's not a practical issue because:
- initialization may be a task of the user's code, thus doesn't fall to the memory manager
- you can use virtual memory facilities and have the initialization to zeroes happen automagically, no need to trash the CPU cache by explicitly filling with zeroes.

3. In real life often you have virtual memory system underneath and there is no guarantee whatosoever.

The management costs of the VM system are typically negligible if you don't enter the realm of disk swapping, at which point the performance of GC schemes completely collapses during garbage collections and compactions, while a classic allocator can live perfectly happily with part of its memory swapped out.

GC allocation (in case of compacting collecotr) is constant time trivially,

Allocation is alway constant time whatever the allocator if you do it right, that's not a comparison point, however, GC linear allocation isn't CPU-cache friendly, meaning that if you get that memory fast, the first actions you'll do on that memory will typically be an order of magnitude slower than if you had been returned cached memory (particularly noticeable for allocations of a few hundreds of kB of temporaries, string concatenations, growing arrays, etc.).

Another drawback of linear allocation is interleaving, which often reduces the cache efficiency by placing heterogeneous data together (depends on the application of course, but it can hit you rather severely on some composite data structures).

all the cost is in compacting which is linear.

Compacting might be linear, but it's cache unfriendly, which on a modern CPU means an order of magnitude slower memory operations, and that happens practically on the whole allocated memory. If some of it was swapped out, you can then expect hell (I've seen it happen), and as compaction is a necessity of linear allocation, you can never be 100% sure to avoid it with a linearly allocating GC.

Finally garbage collection isn't a linear phase, and it's also intrinsically not cache friendly (lots of random accesses all over the allocated memory, most of which are going to be cache misses).

Nowadays, cache inefficiency can result in a lot of hidden perf hits, with uncached accesses being 10 or more times slower (from CPU ratios, RAM latencies, controler/chipset latencies, that all add to the CPU's own L1/L2 latencies).
10 CPU cycles doesn't sound like much, but that's enough time for a dozen+ instructions under adequate circumstances, have it happen too often, and that's enough to make the difference between smooth and sluggish.

Eric

Reply by email

Sebastian Kaliszewski
to Eric Grange
Eric Grange wrote 5 lines
> 1. This is not "entirely true" (the cost is at best logarithmic on the > number of objects or your allocator has terrible fragmentation) Not really, you can use a memory map and achieve much lower fragmentation than classic memory managers

1. Memory map brings no quarantees of const time.
2. Same applies to GC managers. BTW GC can use the very same allocation/deallocation algorithm.

(cf. "FastMM" submissions in the FastCode project in the b.p.d.basm borland ng). In practice, you're memory usage will be a lot lower than that of a GC.

You're forgetting internal fragmentation. It typical allocator allocating block with power of 2 sizes (4, 8, 16, 32, up to page size or small multiply of page size) assuming flat size distribution you have ~33% fragmentation.

Show quoted text 6 lines
> 2. You have to initialise your allocated data anyway. That's not a practical issue because: - initialization may be a task of the user's code, thus doesn't fall to the memory manager

But must happen anyway.

- you can use virtual memory facilities and have the initialization to zeroes happen automagically, no need to trash the CPU cache by explicitly filling with zeroes.

No way. It does not. At best it's delayed up to the point when memory gets used actually rather than being just allocated. But then OS has to clear the page.

Show quoted text 5 lines
> 3. In real life often you have virtual memory system underneath and > there is no guarantee whatosoever. The management costs of the VM system are typically negligible

Neglibile but not const (they're mostly linear -- as memory must be cleared before porcess might use it -- due to basic security requirements).

if you don't enter the realm of disk swapping, at which point the performance of GC schemes completely collapses during garbage collections

Modern generational GC's are less suspectible to such problems.

and compactions,

Not every GC is compacting GC.

Show quoted text 8 lines
while a classic allocator can live perfectly happily with part of its memory swapped out. > GC allocation (in case of compacting collecotr) is constant time > trivially, Allocation is alway constant time whatever the allocator if you do it right,

Nope. See above & below.
And even of you artificially restrict yourself to small span of small object sizes yuo have to check quite a bunch of conditions, and conditional branches are not free. It's allwayes order or two of magnitude slower than adding constant to a heap pointer.

Show quoted text 6 lines
that's not a comparison point, however, GC linear allocation isn't CPU-cache friendly, meaning that if you get that memory fast, the first actions you'll do on that memory will typically be an order of magnitude slower than if you had been returned cached memory (particularly noticeable for allocations of a few hundreds of kB of temporaries, string concatenations, growing arrays, etc.).

1. ever heard of memory prefetch? You can do that explicitly, but most current CPU uarchitectures does that implicitly for simple access pattersn (and linear walki is one of the simplest).
2. sorry but if you allocate few hundred of KB or more at once it won't be cached anyway, regardless of the allocator.
3. temporaries should come from stack anyway.

Another drawback of linear allocation is interleaving, which often reduces the cache efficiency by placing heterogeneous data together (depends on the application of course, but it can hit you rather severely on some composite data structures).

Sorry, but this goes the opposite way. Randomly scattered allocations are worse than using large compact block. Those, who have to optimise their data for cache use know that, and allocate large memory pools and then arrange data the way they want. Cache colloring and stuff is easier to do when you do not have to deal with stuff scattered everywhere.

This is one of the *advantages* of compacting allocators, that they alocate with no internal fragmentation and that after compacting frequently used parts are close together, and with just somwehat smart design of compaction algorithm things used together are also placed together. This is certainly good for cache not bad (it's obvious when you look at typical CPU cache algorithm -- this minimises the amount of cache-line conflicts).

Show quoted text 6 lines
> all the cost is in compacting which is linear. Compacting might be linear, but it's cache unfriendly, which on a modern CPU means an order of magnitude slower memory operations, and that happens practically on the whole allocated memory.

Prefetch is your friend. Then GC might be made so that not every collection has to hit entire memory. And collection is often incremental (interleaved with user code execution).

If some of it was swapped out, you can then expect hell (I've seen it happen), and as compaction is a necessity of linear allocation, you can never be 100% sure to avoid it with a linearly allocating GC.

Well, I had once wait ~half a minute for one few hundred MB allocation to finish (that was on 4BG RAM, 4 CPU Tru64 box with code compiled with one of the best code-preformance-wise compilers around DEC CXX 6.x). This was explicit allocation of course (so far with allocation in constant time). So yes, shit happens.
The other thing is that especuially early Java implementations had primitive & poor GC. Incremental & generational collector won't try to access entire heap allmost at once.

Show quoted text 8 lines
Finally garbage collection isn't a linear phase, and it's also intrinsically not cache friendly (lots of random accesses all over the allocated memory, most of which are going to be cache misses). Nowadays, cache inefficiency can result in a lot of hidden perf hits, with uncached accesses being 10 or more times slower (from CPU ratios, RAM latencies, controler/chipset latencies, that all add to the CPU's own L1/L2 latencies).

Memory is 100-300 times slower latency wise than L1 cache.

10 CPU cycles doesn't sound like much, but that's enough time for a dozen+ instructions under adequate circumstances,

Todays OutOfOrder CPUs can find a lot of useful work during such relatively short (10 cycles is very little) periods of time -- btw even L2 cache hits take longer (out of PC processors only PentiumM has 10 cycle L2 latency, others are worse.

have it happen too often, and that's enough to make the difference between smooth and sluggish.

You didn't touch the other side. Non compacting allocation is either slow or has significant internal fragmentation, especially heap allocating tiny objects is very wasteful. You'll go out of small L1 cache quick while large portions of your cache lines will contain just unused holes.

rgds

Sebastian Kaliszewski

Reply by email

Jan Ruzicka
to Sebastian Kaliszewski

Enough guys

each camp can make distinct implementation.
Use this forum to discuss an interface.

Let the results speak for themselves.
Lets discuss test code.
Lets discuss benchmark code.

Instead of discussing bunch of what-ifs
let's see how the implementation does.

Is there some wiki[1] where we can collect mentioned ideas?

Jan

[1] (like www.twiki.org or http://c2.com/cgi/wiki)

Sebastian Kaliszewski wrote 205 lines
On Friday 25 February 2005 17:29, Eric Grange wrote: >> 1. This is not "entirely true" (the cost is at best logarithmic on >> the >> number of objects or your allocator has terrible fragmentation) > > Not really, you can use a memory map and achieve much lower > fragmentation than classic memory managers 1. Memory map brings no quarantees of const time. 2. Same applies to GC managers. BTW GC can use the very same allocation/deallocation algorithm. > (cf. "FastMM" submissions > in the FastCode project in the b.p.d.basm borland ng). > In practice, you're memory usage will be a lot lower than that > of a GC. You're forgetting internal fragmentation. It typical allocator allocating block with power of 2 sizes (4, 8, 16, 32, up to page size or small multiply of page size) assuming flat size distribution you have ~33% fragmentation. > >> 2. You have to initialise your allocated data anyway. > > That's not a practical issue because: > - initialization may be a task of the user's code, thus doesn't > fall to the memory manager But must happen anyway. > - you can use virtual memory facilities and have the initialization > to zeroes happen automagically, no need to trash the CPU cache > by explicitly filling with zeroes. No way. It does not. At best it's delayed up to the point when memory gets used actually rather than being just allocated. But then OS has to clear the page. > >> 3. In real life often you have virtual memory system underneath and >> there is no guarantee whatosoever. > > The management costs of the VM system are typically negligible Neglibile but not const (they're mostly linear -- as memory must be cleared before porcess might use it -- due to basic security requirements). > if you > don't enter the realm of disk swapping, at which point the performance > of GC schemes completely collapses during garbage collections Modern generational GC's are less suspectible to such problems. > and > compactions, Not every GC is compacting GC. > while a classic allocator can live perfectly happily with > part of its memory swapped out. > >> GC allocation (in case of compacting collecotr) is constant time >> trivially, > > Allocation is alway constant time whatever the allocator if you do it > right, Nope. See above & below. And even of you artificially restrict yourself to small span of small object sizes yuo have to check quite a bunch of conditions, and conditional branches are not free. It's allwayes order or two of magnitude slower than adding constant to a heap pointer. > that's not a comparison point, however, GC linear allocation isn't > CPU-cache friendly, meaning that if you get that memory fast, the > first > actions you'll do on that memory will typically be an order of > magnitude > slower than if you had been returned cached memory (particularly > noticeable for allocations of a few hundreds of kB of temporaries, > string > concatenations, growing arrays, etc.). 1. ever heard of memory prefetch? You can do that explicitly, but most current CPU uarchitectures does that implicitly for simple access pattersn (and linear walki is one of the simplest). 2. sorry but if you allocate few hundred of KB or more at once it won't be cached anyway, regardless of the allocator. 3. temporaries should come from stack anyway. > Another drawback of linear allocation is interleaving, which often > reduces the cache efficiency by placing heterogeneous data together > (depends on the application of course, but it can hit you rather > severely > on some composite data structures). Sorry, but this goes the opposite way. Randomly scattered allocations are worse than using large compact block. Those, who have to optimise their data for cache use know that, and allocate large memory pools and then arrange data the way they want. Cache colloring and stuff is easier to do when you do not have to deal with stuff scattered everywhere. This is one of the *advantages* of compacting allocators, that they alocate with no internal fragmentation and that after compacting frequently used parts are close together, and with just somwehat smart design of compaction algorithm things used together are also placed together. This is certainly good for cache not bad (it's obvious when you look at typical CPU cache algorithm -- this minimises the amount of cache-line conflicts). > >> all the cost is in compacting which is linear. > > Compacting might be linear, but it's cache unfriendly, which on a > modern > CPU means an order of magnitude slower memory operations, and that > happens practically on the whole allocated memory. Prefetch is your friend. Then GC might be made so that not every collection has to hit entire memory. And collection is often incremental (interleaved with user code execution). > If some of it was > swapped out, you can then expect hell (I've seen it happen), and as > compaction is a necessity of linear allocation, you can never be 100% > sure to avoid it with a linearly allocating GC. Well, I had once wait ~half a minute for one few hundred MB allocation to finish (that was on 4BG RAM, 4 CPU Tru64 box with code compiled with one of the best code-preformance-wise compilers around DEC CXX 6.x). This was explicit allocation of course (so far with allocation in constant time). So yes, shit happens. The other thing is that especuially early Java implementations had primitive & poor GC. Incremental & generational collector won't try to access entire heap allmost at once. > Finally garbage collection isn't a linear phase, and it's also > intrinsically not cache friendly (lots of random accesses all over the > allocated memory, most of which are going to be cache misses). > > Nowadays, cache inefficiency can result in a lot of hidden perf hits, > with uncached accesses being 10 or more times slower (from CPU ratios, > RAM latencies, controler/chipset latencies, that all add to the CPU's > own L1/L2 latencies). Memory is 100-300 times slower latency wise than L1 cache. > 10 CPU cycles doesn't sound like much, but that's enough time for a > dozen+ instructions under adequate circumstances, Todays OutOfOrder CPUs can find a lot of useful work during such relatively short (10 cycles is very little) periods of time -- btw even L2 cache hits take longer (out of PC processors only PentiumM has 10 cycle L2 latency, others are worse. > have it happen too > often, and that's enough to make the difference between smooth and > sluggish. You didn't touch the other side. Non compacting allocation is either slow or has significant internal fragmentation, especially heap allocating tiny objects is very wasteful. You'll go out of small L1 cache quick while large portions of your cache lines will contain just unused holes. rgds -- Sebastian Kaliszewski _______________________________________________ fpc-devel maillist - http://lists.freepascal.org/mailman/listinfo/fpc-devel

Reply by email

DrDiettrich
to Jamie McCracken: GC is very inefficient with memory and current implementations tend to cost a lot…
Jamie McCracken wrote
GC is very inefficient with memory and current implementations tend to cost a lot performance wise too.

I don't see how GC is inefficient with memory?
Reference counting and (mark/sweep) garbage collection have a different runtime behaviour: Reference counting occurs with many references to objects, whereas GC only runs when required, or when idle time is left.

GC gets a lot slower with the more objects you create.

It's not the creation that costs GC time, instead it's the destruction of the objects.

Show quoted text 5 lines
Mixing unmanaged with managed code cause severe performance issues (marshalling penalties calling ummanaged c functions) Interoperability with C gets increasingly difficult. Writing bindings becomes much more complicated and time consuming.

IMO it's never a good idea to mix managed and unmanaged code, to suffer from the worst from two worlds at the same time :-(

>GC is worse here because the pointers are not static in GC so

referencing an object means looking up the actual address in a lookup list (so more overhead whenever you call a method or pass an object reference). GC does not use the stack efficiently and has to use a lot of heap so its extremely inefficient. Ever see GC compact swap?<<

I don't know what GC implementation you're referring to. When memory gets too much fragmented, every memory manager has to do a compaction, that's inevitable. But GC offers a chance to update the references to moved objects, so that no address tables are required. Other memory managers don't know about the locations of pointers to the objects, so that moving objects is not possible at all! Consequently GC allows for better use of available memory, where other methods have to stop with "out of memory".

IMO you should learn a bit more about memory management, your argumentation doesn't look well founded to me.

Any comparison of very distinct methods, as for reference counting vs. GC, requires distinct implementations of the same task. Simply compiling the same code for different environments or libraries will favor the model for which that code was written, and never will give comparable performance figures.

Similarly I doubt that using different models in a single application will ever show up where which model should be used for best performance. Even if the performance may increase by tweaking the code in various places, such a procedure IMO will primarily only reduce the overhead, resulting from collisions between the different models, in this specific application. General decisions, like the kind of management for TObject and TComponent types, may result in different performance gain or loss for different applications, or for different usage (memory requirements...) of the same application.

That's why I suggest to keep models separate, so that a programmer can decide for which model he writes his code. This separation also will give no room to flame wars about "the" best model ;-)

DoDi

Reply by email

Ales Katona
to Jan Ruzicka: Enough guys each camp can make distinct implementation. Use this forum to discuss an…

Jan Ruzicka wrote / napísal (a):

Show quoted text 246 lines
Enough guys each camp can make distinct implementation. Use this forum to discuss an interface. Let the results speak for themselves. Lets discuss test code. Lets discuss benchmark code. Instead of discussing bunch of what-ifs let's see how the implementation does. Is there some wiki[1] where we can collect mentioned ideas? Jan [1] (like www.twiki.org or http://c2.com/cgi/wiki) On Feb 25, 2005, at 13:11, Sebastian Kaliszewski wrote: > On Friday 25 February 2005 17:29, Eric Grange wrote: > >>> 1. This is not "entirely true" (the cost is at best logarithmic on the >>> number of objects or your allocator has terrible fragmentation) >> >> >> Not really, you can use a memory map and achieve much lower >> fragmentation than classic memory managers > > > 1. Memory map brings no quarantees of const time. > 2. Same applies to GC managers. BTW GC can use the very same > allocation/deallocation algorithm. > >> (cf. "FastMM" submissions >> in the FastCode project in the b.p.d.basm borland ng). >> In practice, you're memory usage will be a lot lower than that >> of a GC. > > > You're forgetting internal fragmentation. It typical allocator > allocating > block with power of 2 sizes (4, 8, 16, 32, up to page size or small > multiply of page size) assuming flat size distribution you have ~33% > fragmentation. > >> >>> 2. You have to initialise your allocated data anyway. >> >> >> That's not a practical issue because: >> - initialization may be a task of the user's code, thus doesn't >> fall to the memory manager > > > But must happen anyway. > >> - you can use virtual memory facilities and have the initialization >> to zeroes happen automagically, no need to trash the CPU cache >> by explicitly filling with zeroes. > > > No way. It does not. At best it's delayed up to the point when memory > gets > used actually rather than being just allocated. But then OS has to clear > the page. > >> >>> 3. In real life often you have virtual memory system underneath and >>> there is no guarantee whatosoever. >> >> >> The management costs of the VM system are typically negligible > > > Neglibile but not const (they're mostly linear -- as memory must be > cleared > before porcess might use it -- due to basic security requirements). > >> if you >> don't enter the realm of disk swapping, at which point the performance >> of GC schemes completely collapses during garbage collections > > > Modern generational GC's are less suspectible to such problems. > >> and >> compactions, > > > Not every GC is compacting GC. > >> while a classic allocator can live perfectly happily with >> part of its memory swapped out. >> >>> GC allocation (in case of compacting collecotr) is constant time >>> trivially, >> >> >> Allocation is alway constant time whatever the allocator if you do it >> right, > > > Nope. See above & below. > And even of you artificially restrict yourself to small span of small > object > sizes yuo have to check quite a bunch of conditions, and conditional > branches are not free. It's allwayes order or two of magnitude slower > than > adding constant to a heap pointer. > > >> that's not a comparison point, however, GC linear allocation isn't >> CPU-cache friendly, meaning that if you get that memory fast, the first >> actions you'll do on that memory will typically be an order of >> magnitude >> slower than if you had been returned cached memory (particularly >> noticeable for allocations of a few hundreds of kB of temporaries, >> string >> concatenations, growing arrays, etc.). > > > 1. ever heard of memory prefetch? You can do that explicitly, but most > current CPU uarchitectures does that implicitly for simple access > pattersn > (and linear walki is one of the simplest). > 2. sorry but if you allocate few hundred of KB or more at once it > won't be > cached anyway, regardless of the allocator. > 3. temporaries should come from stack anyway. > > >> Another drawback of linear allocation is interleaving, which often >> reduces the cache efficiency by placing heterogeneous data together >> (depends on the application of course, but it can hit you rather >> severely >> on some composite data structures). > > > Sorry, but this goes the opposite way. Randomly scattered > allocations are > worse than using large compact block. Those, who have to optimise their > data for cache use know that, and allocate large memory pools and then > arrange data the way they want. Cache colloring and stuff is easier > to do > when you do not have to deal with stuff scattered everywhere. > > This is one of the *advantages* of compacting allocators, that they > alocate > with no internal fragmentation and that after compacting frequently used > parts are close together, and with just somwehat smart design of > compaction > algorithm things used together are also placed together. This is > certainly > good for cache not bad (it's obvious when you look at typical CPU cache > algorithm -- this minimises the amount of cache-line conflicts). > > >> >>> all the cost is in compacting which is linear. >> >> >> Compacting might be linear, but it's cache unfriendly, which on a >> modern >> CPU means an order of magnitude slower memory operations, and that >> happens practically on the whole allocated memory. > > > Prefetch is your friend. Then GC might be made so that not every > collection > has to hit entire memory. And collection is often incremental > (interleaved > with user code execution). > > >> If some of it was >> swapped out, you can then expect hell (I've seen it happen), and as >> compaction is a necessity of linear allocation, you can never be 100% >> sure to avoid it with a linearly allocating GC. > > > > Well, I had once wait ~half a minute for one few hundred MB > allocation to > finish (that was on 4BG RAM, 4 CPU Tru64 box with code compiled with > one of > the best code-preformance-wise compilers around DEC CXX 6.x). This was > explicit allocation of course (so far with allocation in constant time). > So yes, shit happens. > The other thing is that especuially early Java implementations had > primitive > & poor GC. Incremental & generational collector won't try to access > entire > heap allmost at once. > >> Finally garbage collection isn't a linear phase, and it's also >> intrinsically not cache friendly (lots of random accesses all over the >> allocated memory, most of which are going to be cache misses). >> >> Nowadays, cache inefficiency can result in a lot of hidden perf hits, >> with uncached accesses being 10 or more times slower (from CPU ratios, >> RAM latencies, controler/chipset latencies, that all add to the CPU's >> own L1/L2 latencies). > > > Memory is 100-300 times slower latency wise than L1 cache. > >> 10 CPU cycles doesn't sound like much, but that's enough time for a >> dozen+ instructions under adequate circumstances, > > > Todays OutOfOrder CPUs can find a lot of useful work during such > relatively > short (10 cycles is very little) periods of time -- btw even L2 cache > hits > take longer (out of PC processors only PentiumM has 10 cycle L2 latency, > others are worse. > > >> have it happen too >> often, and that's enough to make the difference between smooth and >> sluggish. > > > > You didn't touch the other side. Non compacting allocation is either > slow or > has significant internal fragmentation, especially heap allocating tiny > objects is very wasteful. You'll go out of small L1 cache quick while > large > portions of your cache lines will contain just unused holes. > > rgds > -- > Sebastian Kaliszewski > > _______________________________________________ > fpc-devel maillist - > http://lists.freepascal.org/mailman/listinfo/fpc-devel > _______________________________________________ fpc-devel maillist - http://lists.freepascal.org/mailman/listinfo/fpc-devel

I don't want to hit the bleeding wound but I think the point is: NO GC.

Each of us has better things to do than try some GCs out which in the end won't be used anyhow.

Ales

Reply by email

Hans-Jörg Vasold
to Florian Klaempfl

for a long time support for loading packages at runtime was "planned for later versions".

But now i can not find that this is planned anymore. Will FreePascal support dynamic packages in future or not.

cu Ha-Joe

Reply by email

Florian Klaempfl
to Hans-Jörg Vasold
Hans-Jörg Vasold wrote 5 lines
for a long time support for loading packages at runtime was "planned for later versions". But now i can not find that this is planned anymore. Will FreePascal support dynamic packages in future or not.

http://www.freepascal.org/wiki/index.php/Detailed_2.1.0_branch_todo

However, it had quite low priority the last years because dyn. loaded packages for a dynamic moving project like fpc with daily snapshots are a real problem. As soon as one unit interface changes, all dependend packages must be recompiled anyways. And usually there are only 1-2 fpc projects running at the same time on a system so main memory savings are little.

Reply by email

Unknown author
to Florian Klaempfl
Florian Klaempfl wrote 15 lines
Hans-Jörg Vasold wrote: > for a long time support for loading packages at runtime was "planned for > later versions". > > But now i can not find that this is planned anymore. Will FreePascal > support dynamic packages in future or not. http://www.freepascal.org/wiki/index.php/Detailed_2.1.0_branch_todo However, it had quite low priority the last years because dyn. loaded packages for a dynamic moving project like fpc with daily snapshots are a real problem. As soon as one unit interface changes, all dependend packages must be recompiled anyways. And usually there are only 1-2 fpc projects running at the same time on a system so main memory savings are little.

This is 100% correct.

On the other hand, for people who use FPC to develop end-user apps, it makes sense: they will normally not change their installed FPC every 2 months.

For example:
At work, we have a plugin system which cannot work unless packages are used. I also think that Lazarus would benefit from it.

Michael.

Reply by email

Marco van de Voort
to Unknown author
Show quoted text 16 lines
On Sat, 26 Feb 2005, Florian Klaempfl wrote: > However, it had quite low priority the last years because dyn. loaded packages > for a dynamic moving project like fpc with daily snapshots are a real problem. > As soon as one unit interface changes, all dependend packages must be > recompiled anyways. And usually there are only 1-2 fpc projects running at the > same time on a system so main memory savings are little. This is 100% correct. On the other hand, for people who use FPC to develop end-user apps, it makes sense: they will normally not change their installed FPC every 2 months. For example: At work, we have a plugin system which cannot work unless packages are used. I also think that Lazarus would benefit from it.

Yes, I think it is plug-in systems more than memory or disc saving.

Reply by email

DrDiettrich
to Unknown author: This is 100% correct. On the other hand, for people who use FPC to develop end-user apps,…
At work, we have a plugin system which cannot work unless packages are used.

Agreed.

I also think that Lazarus would benefit from it.

Currently Lazarus must be rebuilt for the installation of custom controls, which in Delphi can be loaded dynamically. If this behaviour is related to the lack of dynamic package support, I also vote for an implementation with high priority.

I do not ask for a Delphi compatible solution, in detail I don't appreciate that Delphi packages are bound to a specific Delphi version. I understand that changes to FPC or to the RTL may require a rebuild of all affected packages, but perhaps this effect can be reduced, somehow. With regards to Lazarus it would be sufficient when the packages are rebuilt whenever required, but Lazarus itself should be able to dynamically load packages.

DoDi

Reply by email

peter green
to DrDiettrich: I don't see how GC is inefficient with memory? Reference counting and (mark/sweep)…
Show quoted text 18 lines
-----Original Message----- From: Behalf Of DrDiettrich Sent: 26 February 2005 03:34 To: FPC developers' list Subject: Re: [fpc-devel] Modernising Pascal Jamie McCracken wrote: > GC is very inefficient with memory and current implementations tend to > cost a lot performance wise too. I don't see how GC is inefficient with memory? Reference counting and (mark/sweep) garbage collection have a different runtime behaviour: Reference counting occurs with many references to objects, whereas GC only runs when required, or when idle time is left.

one thing i have heared (i don't use garbage collected languages much so i've never seen this myself) is that the GC gets some CPU time and starts causing stuff to be swapped in. This slows other threads down which gives the GC more CPU and causes more stuff to be swapped in.

another effect that i have seen is when CPU usage is high a garbage collected app can suck up all the systems memory. It can get all the memory it needs for itself because it can force run its garbage collector when it runs out but no other app on the system can.

some objects may use resources other than memory which are more valuable and need to be freed asap.

refcounting doesn't suffer from any of theese issues

also note that use of const parameters can eliminate a huge amount of refcounting overhead.

Reply by email

Hans-Jörg Vasold
to Unknown author: This is 100% correct. On the other hand, for people who use FPC to develop end-user apps,…

----- Original Message -----
From:
To: "FPC developers' list"
Sent: Saturday, February 26, 2005 12:05 PM
Subject: Re: [fpc-devel] dynamic package support

Florian Klaempfl wrote 20 lines
> Hans-Jörg Vasold wrote: > > > for a long time support for loading packages at runtime was "planned > > for > > later versions". > > > > But now i can not find that this is planned anymore. Will FreePascal > > support dynamic packages in future or not. > > http://www.freepascal.org/wiki/index.php/Detailed_2.1.0_branch_todo > > However, it had quite low priority the last years because dyn. loaded > packages > for a dynamic moving project like fpc with daily snapshots are a real > problem. > As soon as one unit interface changes, all dependend packages must be > recompiled anyways. And usually there are only 1-2 fpc projects running > at the > same time on a system so main memory savings are little.

Unless you have a _good_ build tool. Porting want ( http://sourceforge.net/projects/want/ ) could help.

On the other hand, for people who use FPC to develop end-user apps, it makes sense: they will normally not change their installed FPC every 2 months.

That's why i ask. We have a large inhouse application (ERP, paper document processing, document managemen server), written mostly in Delphi. The ERP Part is build on the top of tiopf ( http://www.techinsite.com.au/ ). We have started porting the tiopf framework and our application but this makes no sense unless dynamic package loading is supported, so we have stopped our activities.

cu Hans-Joerg

--------------------------------------------------------------------------------

Reply by email

Jamie McCracken
to DrDiettrich: I don't see how GC is inefficient with memory? Reference counting and (mark/sweep)…
DrDiettrich wrote 11 lines
Jamie McCracken wrote: >GC is very inefficient with memory and current implementations tend to >cost a lot performance wise too. I don't see how GC is inefficient with memory? Reference counting and (mark/sweep) garbage collection have a different runtime behaviour: Reference counting occurs with many references to objects, whereas GC only runs when required, or when idle time is left.

The problem with GC is the lifetime of (dead) objects is significantly extended under GC. This means it hogs more memory for much longer. Memory allocation is also potentially slowed as a result of more objects remaining allocated on the heap so less chance of holes appearing which can be reused (unless you use a compacting GC).

GC runs in a seperate thread so will incur additional overhead from that too and you cant always predict when it runs and it can snare you at the most inappropriate times (EG if memory is in short supply the thread must become extremeley aggressive to avoid swap so you will lose performance big time in those cases and with compacting variety even more so if it does end up in swap)

Show quoted text 8 lines
>GC gets a lot slower with the more objects you create. It's not the creation that costs GC time, instead it's the destruction of the objects.

Its potentially both. All GCs have a tradeoff between memory usage and performance. A compacting one may have faster allocation but uses more memory as a result. There is no perfect GC that both uses memory efficiently and gives good performance (when compared to manual).

Show quoted text 31 lines
>Mixing unmanaged with managed code cause severe performance issues >(marshalling penalties calling ummanaged c functions) > >Interoperability with C gets increasingly difficult. Writing bindings >becomes much more complicated and time consuming. IMO it's never a good idea to mix managed and unmanaged code, to suffer from the worst from two worlds at the same time :-( >>GC is worse here because the pointers are not static in GC so referencing an object means looking up the actual address in a lookup list (so more overhead whenever you call a method or pass an object reference). GC does not use the stack efficiently and has to use a lot of heap so its extremely inefficient. Ever see GC compact swap?<< I don't know what GC implementation you're referring to. When memory gets too much fragmented, every memory manager has to do a compaction, that's inevitable. But GC offers a chance to update the references to moved objects, so that no address tables are required. Other memory managers don't know about the locations of pointers to the objects, so that moving objects is not possible at all! Consequently GC allows for better use of available memory, where other methods have to stop with "out of memory". IMO you should learn a bit more about memory management, your argumentation doesn't look well founded to me.

There are over a dozen different GC strategies so some of my points apply to some but not others (same with a lot of the replies here) thats why this thread has become long and controversial - you simply cant make a lot of generalities without specifying which type of GC you are reffering to.

jamie.

Reply by email

DrDiettrich
to peter green: one thing i have heared (i don't use garbage collected languages much so i've never seen…
peter green wrote
one thing i have heared (i don't use garbage collected languages much so i've never seen this myself) is that the GC gets some CPU time and starts causing stuff to be swapped in. This slows other threads down which gives the GC more CPU and causes more stuff to be swapped in.

GC is started either when the system is (almost) idle, or when no more memory is available for some request. In the first case nothing is blocked by GC, and in the second case everything is blocked by out-of-memory, until the GC has collected free memory.

another effect that i have seen is when CPU usage is high a garbage collected app can suck up all the systems memory. It can get all the memory it needs for itself because it can force run its garbage collector when it runs out but no other app on the system can.

When GC runs in idle time, no other apps exist that could be blocked. When GC must swap something into memory, then the system is equipped with too little RAM for the current situation; in such a state everything runs slowly, due to permanent swapping. I know that people often discuss how to reduce swapping overhead, and typically they all miss the simple solution that they should extend their RAM to prevent swapping to occur at all.

some objects may use resources other than memory which are more valuable and need to be freed asap.

Destructors (or finalizers) can be called to release no longer required resources, even when GC is used. When it's known or suspected that an object could be destroyed, the GC can be invoked immediately. GC for itself doesn't eliminate all resource woes, of course. In special situations it may make sense to support GC with additional code, just like with any other memory management method (see below).

refcounting doesn't suffer from any of theese issues

Reference counting has more critical problems, like zombie objects. GC will always find and remove all dead objects, whereas reference counting never can eliminate bidirectionally linked objects, as exist in a GUI (parent <-> child controls).

also note that use of const parameters can eliminate a huge amount of refcounting overhead.

I'm not sure about the real impact of "const". And even if the use of const parameters results in a significant runtime reduction, this only indicates that much more runtime could be saved by not using reference counting at all!

DoDi

Reply by email

DrDiettrich
to Jamie McCracken: The problem with GC is the lifetime of (dead) objects is significantly extended under GC.…
Jamie McCracken wrote 5 lines
The problem with GC is the lifetime of (dead) objects is significantly extended under GC. This means it hogs more memory for much longer. Memory allocation is also potentially slowed as a result of more objects remaining allocated on the heap so less chance of holes appearing which can be reused (unless you use a compacting GC).

Not using memory is a waste of resources. When memory becomes a precious resource, e.g. when swapping may occur, GC may be called to recycle the dead objects.

Show quoted text 6 lines
GC runs in a seperate thread so will incur additional overhead from that too and you cant always predict when it runs and it can snare you at the most inappropriate times (EG if memory is in short supply the thread must become extremeley aggressive to avoid swap so you will lose performance big time in those cases and with compacting variety even more so if it does end up in swap)

IMO it doesn't make much sense to run GC in its own thread. This would result in never ending GC, when the other threads continue to create objects at the same time. If your observations on the runtime behaviour of GC are based on such an implementation, I agree that this is a poor and inappropriate GC implementation.

Show quoted text 7 lines
> It's not the creation that costs GC time, instead it's the destruction > of the objects. Its potentially both. All GCs have a tradeoff between memory usage and performance. A compacting one may have faster allocation but uses more memory as a result. There is no perfect GC that both uses memory efficiently and gives good performance (when compared to manual).

Where do you see any difference between memory allocation in GC and in any other memory manager? I can't see any difference. Every memory manager must know where chunks of free memory exist, and which of these chunks to use for a new object.

Show quoted text 5 lines
There are over a dozen different GC strategies so some of my points apply to some but not others (same with a lot of the replies here) thats why this thread has become long and controversial - you simply cant make a lot of generalities without specifying which type of GC you are reffering to.

Some, IMO most, of the strategies can be dropped immediately, after determination of their complexity. O(n^2) is inacceptable when O(n) is achievable by other methods. I have experience only with mark/sweep GC, which has almost none of the problems which you described.

DoDi

Reply by email