January 13, 2003
In article <avv7ve$2tos$1@digitaldaemon.com>, Ilya Minkov says...
>
>Please, believe me garbage collection (GC) is the future of game design.

I doubt it.

>"Realtime programming" doesn't really include games and other things called "realtime", since they have to cope with disc access and such, graphic card, other devices, which are  NOT REALTIME!

You are right. That is why game programmers avoid accessing slow devices like disks in the middle of a game. If you absolutely must get something off disk you normally stream it in. You set up an interrupt driven process or something like that. So say I need a new model off the disk in a few seconds, I would place a request to the disk streaming subsystem to start getting the stuff off the disk. If you sit there like fool waiting for your CD drive to spin up, then  your game is very choppy.

>You don't even have to believe me.

I don't have to believe you. I write games for a living. ;)

>The author claims, that plugging in the Hans
>Boehm GC into the project reduced the time requiered to load a project
>to 1/8 of original, because GC-malloc is simply better optimized than
>the normal one.
>The manual says that Boehm GC proves to be significantly faster for
>almost any application managing many tiny objects, but tends to be
>slower for applications working with large objects only.

I don't doubt it. Asking the operating system for a buffer is very very slow. On windows it actually pauses your thread, and does an OS context switch. If you are allocating tiny buffers straight from the OS all the time, you need to go back to programming school. Memory should be allocated from the OS in large chunks and then doled out inside your progam as needed - using your own optimized allocators. This is what gets you cache coherency. This is what guarantees that all the vertex data for a model is in cache when you transform it, etc.

It is not fair to compare using the most highly optimized GC you have ever heard of against bad manual memory management practices.

>The reason for the first claim is that it performs a number of optimisations (heap compaction),

There is no heap compaction better than me placing my data structures in memory explicitly for cache coherency. Plus there is no penalty for me having holes in the memory space that are bigger than a memory page anyway. If a whole page is unused it will never be loaded into the cache and will never trouble me. Compacting it away is just wasting bandwidth on the bus that I could be using to load textures.

>And if you're still not convinced, go on and turn it off. You're just not doing yourself a favor. D GC does the same amount of work what ref-counting does once in a while, while ref-counting has to do that at almost any operation.

The alternative to GC is not ref counting. Few objects have such an unpredictable life cycle that they cannot be managed any other way. GC essentially embed the ref counting inside the GC subsystem and makes you do some kind of reference tracking for all memory buffer wether you need it or not. I ref count very few objects in a given project. And when I do I'm typically ref counting entire objects, not every single buffer. Plus I can manipulate the ref counts only when it matters. I don't have a GC system internally twiddling pointer ref counts mindlessly when all that is happening is that a temporary pointer was made for a function call or some other limited scope.


January 14, 2003
rafael baptista wrote:
> 
> I don't doubt it. Asking the operating system for a buffer is very very slow. On
> windows it actually pauses your thread, and does an OS context switch. If you
> are allocating tiny buffers straight from the OS all the time, you need to go
> back to programming school. Memory should be allocated from the OS in large
> chunks and then doled out inside your progam as needed - using your own
> optimized allocators. This is what gets you cache coherency. This is what
> guarantees that all the vertex data for a model is in cache when you transform
> it, etc.
> 

Dude, what do you think Java (and most other GC-enabled environments) do?  They allocate big buffers from the OS, then divide it into various categories of storage (in Java's case, various generations).  Highly efficient strategies are then used to quickly allocate chunks of memory that used and discarded quickly.  More stable allocations are transferred into storage that has strategies appropriate to that.

If you're doing your own memory allocation code, you have to do a LOT of work, and write a LOT of bug free code, to get to where the Java allocator is.

D needs GC.  You're doing realtime work; stick with C, which is highly appropriate to that task!

RJ

January 14, 2003
Ross Judson wrote:
> Dude, what do you think Java (and most other GC-enabled environments) do?  They allocate big buffers from the OS, then divide it into various categories of storage (in Java's case, various generations).  Highly efficient strategies are then used to quickly allocate chunks of memory that used and discarded quickly.  More stable allocations are transferred into storage that has strategies appropriate to that.
> 
> If you're doing your own memory allocation code, you have to do a LOT of work, and write a LOT of bug free code, to get to where the Java allocator is.
> 
> D needs GC.  You're doing realtime work; stick with C, which is highly appropriate to that task!

But D is nicer than C, and I'd rather use it, and have it be a general usage language, rather than a rapid development language for apps that need more speed than VB.  I like GC, don't get me wrong.  But without hardware and OS support, GC is always going to have some problems with certain kinds of applications.  Since that support doesn't seem to be forthcoming in the near future, D should be ready to accept those cases and have a simple method for *transparently* swapping out what happens with someone writes new or delete.  I shouldn't have to be doing anything complicated to switch it all out.

Evan

January 14, 2003
Ilya Minkov wrote:
> which are highly complex meshes of (concave) objects. Now, as I read 
                                      ^^^^^^^
Sorry, it should read "convex". Please remind me if i ever make another mistake like that, and i'll blow my brain out. ;)

It has to do with the fact, that a ray hits a forward surface in a convex only once, so that back surface culling is sufficent for correct display, no sorting or z-buffer requiered. You simply start with a block you're in and draw the neighbors clipped through the portals. Though many aspects are not important for modern hardware anymore, the overdraw would get completely eliminated, and simlar techniques eliminate overdraw in the other cases.

-i.

January 14, 2003
>
>rafael baptista wrote:
> 
> Asking the operating system for a buffer is very very slow. On
> windows it actually pauses your thread, and does an OS context switch. If you
> are allocating tiny buffers straight from the OS all the time, you need to go
> back to programming school. Memory should be allocated from the OS in large
> chunks and then doled out inside your progam as needed - using your own
> optimized allocators.
> 

>Ross Judson says...
>
>Dude, what do you think Java (and most other GC-enabled environments) do?  They allocate big buffers from the OS, then divide it into various categories of storage (in Java's case, various generations).  Highly efficient strategies are then used to quickly allocate chunks of memory that used and discarded quickly.  More stable allocations are transferred into storage that has strategies appropriate to that.
>
>If you're doing your own memory allocation code, you have to do a LOT of work, and write a LOT of bug free code, to get to where the Java allocator is.
>
>D needs GC.  You're doing realtime work; stick with C, which is highly appropriate to that task!
>
>RJ
>

Thats right, a GC will normally allocate memory in large blocks, which is a good practice, and so will anyone doing proper memory management. So what I mean is just that you can't say GC is faster by comparing it any number of bad practices when managing memory manually.

There is no doubt about it, that you can make systems to automatically manage memory that are resonably efficient in a given domain... which is why I want the freedom to write my own. Its not that hard to do and has lots of advantages.

When necessary I will write my own GC-like manager for certain classes with unpredictable life cycles ( and only for those ). I will write fixed size allocators for other things. I may have tens of allocators going at once guaranteeing that certain types of data will end up on the same page of memory - for cache reasons.

Sometimes I want to replace my heap manager with a debug version that lets me track memory leaks, or lets me graph what subsystems are using up the most memory, or lets me see what subsystems are giving me the most cache misses etc. I may want to receive a warning when certain systems are above their memory budget. I may want to know about any buffers I have overwritten. I want low level control of the memory management all the time. I want maximum flexibility there.

I will accept a slow system defined "printf" function, or a slow file open dialog, or a fat and slow directory listing function. Those are called infrequently. But I don't want anything between me and the processor, and I don't want anything between me and system memory.

Professional programmers know that their customers only care about performance. No one buys software because the code is reusable, or easily to maintain, or simple for newbie programmers to understand. Your customers buy what is the fastest, has the most features, is easiest to use and makes best use of their hardware. They don't care about anything that makes your life easier.

I won't be satisfied with the middle of the road, lowest common denominator, useful for all purposes libraries that ship with my tools. I don't want to cede even one cycle, one byte... nothing to my competitor.

Fast, proper, custom written memory management is not just for games.

Also, ever wonder why you never see any shrink wrapped software written in Java?



January 14, 2003
rafael baptista wrote:
> In article <avv7ve$2tos$1@digitaldaemon.com>, Ilya Minkov says...
> 
>> Please, believe me garbage collection (GC) is the future of game
>> design.
> 
> I doubt it.

OK. Let me see. I've got an OpenGL game somewhere which is written in OCaml. I'll try to compile it and tell you what it yields. I haven't seen it yet, it's not necessarily a complicated one where that all would matter.
OCaml is a 100% GC language with mixed powerful functional and imperative features, combined with OO, and there exists a GCC-based native compiler for it.

>> and such, graphic card, other devices, which are  NOT REALTIME!
> 
> You are right. That is why game programmers avoid accessing slow
> devices like disks in the middle of a game. If you absolutely must
> get something off disk you normally stream it in. You set up an
> interrupt driven process or something like that. So say I need a new
> model off the disk in a few seconds, I would place a request to the
> disk streaming subsystem to start getting the stuff off the disk. If
> you sit there like fool waiting for your CD drive to spin up, then
> your game is very choppy.

How do you know the OS doesn't want to access the disk for some purpose? It frequently does. :) BTW, i might be writing a game in a couple of erhh... years, and i'll try to remember what you said. ;)

> I don't have to believe you. I write games for a living. ;)

OK, OK, i respect Your opinion a lot, you old rusty... :>

>> The author claims, that plugging in the Hans Boehm GC into ...
> I don't doubt it. Asking the operating system for a buffer is very
> very slow. On windows it actually pauses your thread, and does an OS
> context switch. If you are allocating tiny buffers straight from the
> OS all the time, you need to go back to programming school. Memory
> should be allocated from the OS in large chunks and then doled out
> inside your progam as needed - using your own optimized allocators.
> This is what gets you cache coherency. This is what guarantees that
> all the vertex data for a model is in cache when you transform it,
> etc.

Naturally, i won't be keeping vertices one by one. ;)

> It is not fair to compare using the most highly optimized GC you have
> ever heard of against bad manual memory management practices.

OK.

> There is no heap compaction better than me ...

Strong Ego. :>

> in memory explicitly for cache coherency. Plus there is no penalty
> for me having holes in the memory space that are bigger than a memory
> page anyway. If a whole page is unused it will never be loaded into
> the cache and will never trouble me. Compacting it away is just
> wasting bandwidth on the bus that I could be using to load textures.

It might just as well only compact the heap if it makes sense :) (probably late though). And it might avoid moving something if not really needed. It's more an implementation issue than of the principle.

> The alternative to GC is not ref counting. Few objects have such an unpredictable life cycle that they cannot be managed any other way.

You probably mean unmanaged. :) Better GCs avoid bothering with old objects anyway.

> GC essentially embed the ref counting inside the GC subsystem and
> makes you do some kind of reference tracking for all memory buffer
> wether you need it or not. I ref count very few objects in a given
> project. 
But it does it only once in a while.

> And when I do I'm typically ref counting entire objects, not
> every single buffer. Plus I can manipulate the ref counts only when
> it matters. 

I know. I thought about it a lot. Of course even the structures i mentioned can be used with an efficient reference counting. I even came to a conclusion, that it's requiered and GC would be shut off unless either weak pointers, or object notification is implemented. under the last i mean, the GC could run a designated method of every object which provides it, to notify it that the GC cycle has just run and that they have so many "parents" and here's the handle to the rest of GC statistics and so on. So you could teach your object to commit a suicide after it's had only one parent for a while. This parent would be the central object repository, cache.

> I don't have a GC system internally twiddling pointer ref
> counts mindlessly when all that is happening is that a temporary pointer was made for a function call or some other limited scope.

I don't think it's *that* important. It's just worth an experiment i'll be sure to do someday.

-i.


January 15, 2003
rafael baptista wrote:

> Professional programmers know that their customers only care about performance.
> No one buys software because the code is reusable, or easily to maintain, or
> simple for newbie programmers to understand. Your customers buy what is the
> fastest, has the most features, is easiest to use and makes best use of their
> hardware. They don't care about anything that makes your life easier.
> 
> I won't be satisfied with the middle of the road, lowest common denominator,
> useful for all purposes libraries that ship with my tools. I don't want to cede
> even one cycle, one byte... nothing to my competitor.
> 
> Fast, proper, custom written memory management is not just for games. 
> 
> Also, ever wonder why you never see any shrink wrapped software written in Java?
> 

Two things come to mind...first, some customers care about performance.  Most care about _correctness_.  The more a programmer has to do himself, the more opportunity there is to blow the correctness goal. There's a reason we don't do much work in assembly any more.

I'd say that there are a decent number of Java client-side applications out there, but they're not all that well known.  Writing good client-side Java is hard work.  Check out http://www.jgoodies.com for some very polished work by a talented guy.

Ever wonder why you see less and less server-side software written in C?  Most of it is done in Java, these days...there's a reason for that!

RJ


January 23, 2003
"rafael baptista" <rafael_member@pathlink.com> wrote in message news:avsfoq$6nc$1@digitaldaemon.com...
> The GC is worse though - because its in there operating - arbitrarily
stalling
> my program whether I use it or not.

In D, the GC *only* gets called when a call is made to allocate memory. It is not in a separate thread arbitrarilly and randomly running.

To deal with time sensitive code, if you preallocate all the data you'll need for the time sensitive portion, you'll never run the risk of the GC running in the middle of it. Note that a similar problem exists if you use malloc - the library can arbitrarilly decide to call the operating system to allocate more memory to the process, which can take arbitrarilly long amounts of time.

The only way to guarantee latency, even in C++, is to preallocate the data.


January 23, 2003
Some great comments! My replies are inline below.

"rafael b aptista" <rafael_member@pathlink.com> wrote in message news:avukdf$2f0g$1@digitaldaemon.com...
> Running unit tests at startup time has many disadvantages:
> 1. Even in a debug build you have limits on how long you are willing to
wait
> through a unit test. With an offline unit test you can make it do
everything you
> want.

A unit test is not a complete replacement for a comprehensive test suite. It just tests the functionality of individual parts of the project. Unit tests can also be turned on or off on a module by module basis - once one section is debugged, you can turn off unit tests for it until it gets changed. My own experience with D unit testing has been that it has saved me from many, many embarassingly stupid bugs. I don't mind waiting a couple seconds on startup for a debug build. If it takes a long time to run the unit tests, it's probably worth looking at the unit tests and pulling the time consuming bits out into an offline test.

That's really what I do with the D compiler system itself. The basics are done with unit tests, the comprehensive time consuming tests are done with a separate test suite. But the unit tests assure me a basic level of functionality every time without needing to run the long test suite.

> 2. Unit tests are better as text oriented apps, and most modern
applications are
> graphical. You would have to make the unit test spew to a log, and then go
check
> the log. Similarly it is a pain to try to run a gui app in a script. So if
you
> want to make scripts that regress your libraries you have to make dummy
console
> apps that only run the unit test in a text mode.

Unit tests in D are designed to throw an exception when they fail. Catching exceptions and putting up a message box for them isn't a big problem. When I've worked on gui apps, I did use log files extensively. Instead of printf'ing to the console, I just printf'd to a log file.

> 3. Unit tests running at application startup time are not running when
they
> should run. You want a unit test to run every time you change the code
that it
> tests - not necessarily every time you start up your main application.

Doing it when the program starts is a reliable way of doing it. For example, if you have module A and module B, A depends on B, and the code for B changes, it's nice to get the unit test for A run too. Having some external mechanism decide which unit tests to run when particular modules change is difficult to get right, and is certainly wrong if it decides solely on the basis of source file changes.

> Making a unit test function for a class be a class member function has
another
> disadvantage: You are running the unit test after the class constructor,
and the
> unit test is oriented toward testing only that one instance. To unit test
a
> class I prefer to make a non-member function that exercises the whole API
in one
> function - including all the constructors.

You can do that in D with a module level unit test.

> Experienced programmers can disagree about the best way to do unit tests -
and
> that is exactly why unit tests should not be built in to the language.

I suggest you could say that about every language feature - just look at this forum! In any case, in my experience, very very few programmers do unit tests. Why? Because it's a pain to do without language support. In D, unit tests are quick and easy to add, they are right next to the code being tested, and so are more likely to get written and be up to date. Running them at startup means they are more likely to be run.

> The way that D supports unit tests is not the way most programmers who
implement
> unit test chose to implement them. So you are codifying into the language something which is not established as the best practice anyway.

D does not prevent doing unit tests in a non-builtin manner. But (as far as I know) D is the very first language to build in the concept of unit tests, in a quick, convenient, and reliable manner. I suggest that this will encourage a much greater percentage of programmers taking the time to even write any unit tests, which will be all to the better.

I won't argue that D is the best way possible to do unit tests, experience using the language over time may easilly suggest improvements. But it's a great start, and unit tests are a badly needed language feature.


January 24, 2003
Hi.

I'm new to this news group, but not to language design.  D can't be all things to all people, so we might as well live with it's focus.  Garbage collection is already there, and that defines the scope of the D language about as much as any other feature.

That said, here's my thoughts about GC in general:

GC in general buys very little.  Anyone writing complicated programs already have to write recursive destructors to remove objects from relationships with other objects.  The value of GC is simply that one extra line (delete myObject;) doesn't have to be typed.

People argue that GC helps reduce dangling pointer bugs, but rather than a dangling pointer, you've got a memory leak.

While GC doesn't really hurt a language like D for most applications, it trashes it's use (as D's author points out) for real time programming (not going to see any kernels written in D any time soon).

There's also another weak reason I dislike GC: synthesis of datastructures into hardware really doesn't work in a GC based language.

>> If you're doing your own memory allocation code, you have to do a LOT of work, and write a LOT of bug free code, to get to where the Java allocator is.

In any current language, you'd be right.  However, highly optimized memory management can and should be generated by compilers.  We do this now where I work.  I type no extra code, but get automatically generated free-lists, and bug-free recursive destructors.  It's kind of nice.

Bill Cox