home *** CD-ROM | disk | FTP | other *** search
/ NetNews Usenet Archive 1992 #18 / NN_1992_18.iso / spool / comp / lang / cplus / 12489 < prev    next >
Encoding:
Internet Message Format  |  1992-08-17  |  12.9 KB

  1. Path: sparky!uunet!synaptx!synaptics.com!daveg
  2. From: daveg@synaptics.com (Dave Gillespie)
  3. Newsgroups: comp.lang.c++
  4. Subject: Re: Garbage Collection for C++
  5. Message-ID: <DAVEG.92Aug18002213@synaptx.synaptics.com>
  6. Date: 18 Aug 92 07:22:13 GMT
  7. References: <1992Aug6.014619.2111@ucc.su.OZ.AU>
  8.     <DAVEG.92Aug14194411@synaptx.synaptics.com>
  9.     <1992Aug15.074911.7965@news.mentorg.com>
  10. Sender: daveg@synaptx.Synaptics.Com
  11. Organization: Synaptics, Inc., San Jose, CA
  12. Lines: 249
  13. In-reply-to: bcannard@hppcb36.mentorg.com's message of 15 Aug 92 07:49:11 GMT
  14.  
  15. In article <1992Aug15.074911.7965@news.mentorg.com> bcannard@hppcb36.mentorg.com (Bob Cannard @ PCB x5565) writes:
  16.  
  17. >|> So you use "GCnew" to allocate objects of those classes, and "new"
  18. >|> elsewhere.
  19.  
  20. > I have two problems with this.
  21.  
  22. > The first is that the GC is forced to check every pointer to see if it might
  23. > point to a GC object, and the compiler is required to provide the necessary
  24. > information that identifies all these pointers. The GC is forced to check many
  25. > more pointers than is necessary (I estimate that in my application, roughly
  26. > 90% of the objects would be non-GC). The programmer knows damn well that these
  27. > pointers are irrelevant to the GC, and I get very annoyed when I can't pass
  28. > that information on to the compiler.
  29.  
  30. Okay, maybe there should be a declaration that a pointer does *not*
  31. point to a GC-able object.  The rules would have to be a bit different
  32. from "const" and "volatile", since the unsafe cast is to add, not
  33. remove, the property.  But it would resolve the problem of existing
  34. libraries which don't declare their pointer arguments "GC".
  35.  
  36. Most of the gain from this declaration is to be had for pointers which
  37. are members of structs and class objects (as opposed to variables,
  38. arguments, etc.)  You could make it a declaration on the member rather
  39. than on the pointer type itself.
  40.  
  41. > The second is that having to test for GCness at run time represents an
  42. > additional overhead for the GC which I'd rather avoid.
  43.  
  44. The GC still has to figure out what to do about uninitialized pointers.
  45. I guess if you must explicitly declare pointers to be GC-able then you
  46. could have the compiler initialize them to 0 by default.
  47.  
  48. The overhead for testing if the pointer points in approximately the
  49. right direction is quite cheap.  If you have GC and non-GC heaps in
  50. disjoint memory spaces, it's a single comparison.  Even if they are
  51. interleaved, it can be a single lookup in a table indexed by pages.
  52.  
  53. > In fact, I'm forced to wonder: if the GC has to figure out the difference
  54. > at run time, would it not be more efficient to simply make everything GCable?
  55. > It looks to me as if there is no point in having GC and nonGC coexist, unless
  56. > the two can be distinguished at compile time.
  57.  
  58. Good point.  There is backward compatibility to worry about.
  59.  
  60. >|> The "extra treatment" that GC classes would need basically involves
  61. >|> adding some kind of tag information.  If you declare an entire class
  62. >|> to be GC, it makes sense to put such a tag in every instance of the
  63. >|> class.  If you instead work by allocating regular classes in a
  64. >|> special GC-able way, then it makes more sense to put the tag only
  65. >|> in instances of the class that are in the GC heap.
  66.  
  67. > That looks wrong to me. Suppose I have a pointer to some object. The GC
  68. > comes along and finds this pointer. It must first determine whether or not
  69. > the object is a GC object or a non-GC object. It must then find the tag. If
  70. > the object is part of an array, the tag could be anywhere. How the hell does
  71. > the GC figure out where the tag is? How does it even tell that the object is
  72. > inside an array? The object could contain an index to show which element of the
  73. > array it is, but it would be cheaper to tag each element individually.
  74.  
  75. This is going to be an issue for any C++ garbage collector.  If there
  76. is a pointer to any element of an array, then the whole array is alive
  77. (because of pointer arithmetic).  Indeed, if I have a "char *", I sure
  78. hope the C++ runtime system doesn't decide all the characters after the
  79. one I'm pointing to are garbage!
  80.  
  81. The KCL-like system I outlined before is able to handle this.  A given
  82. page of memory has objects all the same size.  A quick page-table check
  83. tells whether or not this is a GC-able page, and, if so, how big its
  84. objects are.  Note that an "object" is a whole array in this case; after
  85. all, the whole array was allocated with a single "new" call.  Just round
  86. the page offset down to the next lower multiple of the object size to
  87. get a pointer to the beginning of the object (and, thus, the tag).
  88.  
  89. Really big arrays can be treated differently, and somewhat less
  90. efficiently; being big, they're going to be rare.  Say, a binary tree
  91. sorted by memory address, which can be searched in O(lg N) time to
  92. find the object into which a pointer points.
  93.  
  94. >|> I still think declaring pointers to be GC would be too much of a
  95. >|> hassle for the programmer
  96.  
  97. > ???? You've got to be kidding! You can cope with const (or haven't you got
  98. > cfront 3.0 yet - heh heh - evil chortle) but not with GC? If you can't cope
  99. > with hassle, you're in the wrong language! :-)
  100.  
  101. First, you can cast away "const" if you're willing to check the safety
  102. of things by hand.  This is a "relief valve" that is not practical with
  103. a "GC" property.
  104.  
  105. Second, libraries can typically be made "const-safe" by changing the
  106. public declarations of the functions only, not their internals.  (This
  107. is essentially casting away const in an underhanded way.)
  108.  
  109. Third, even adding "const" caused so much heartburn on the part of
  110. the C++ programming world as to induce evil chortles in those who
  111. discuss it, but you are still willing to go through it all over
  112. again with "GC"?
  113.  
  114. Note that the "non-GC" declaration I mentioned before doesn't have
  115. this problem; existing code can be left alone with zero effort.
  116.  
  117. >|> , so we're left having to assume all
  118. >|> pointers may point to GC-allocated objects.  Without adding lots of
  119. >|> tags in places that make C compatiblity awkward, we're left with a
  120. >|> completely "conservative", i.e., brute-force GC algorithm.  We scan
  121. >|> every word on the stack, every word in static storage, and every
  122. >|> word on the non-GC heap for things that look like GC pointers.
  123. >|> (Since this is slow, a good optimization might be to separate the
  124. >|> static area and possibly the non-GC heap into things-which-contain-
  125. >|> pointers and things-which-don't, like massive arrays of doubles
  126. >|> which would be a waste of time to scan.)
  127.  
  128. > Probably worse than the v*rd*mmt reference counting that I'm already using,
  129. > especially if the GC part of the data structure is relatively small. Blech!
  130. > You really want all that inescapable overhead in order to save a bit of hassle?
  131.  
  132. The separation into areas can be done by the compiler.  I think if you
  133. added up the number of words on the stack and the number of pointers in
  134. static memory, you'd find it's suprisingly small for typical programs.
  135. And if your program has a bulky stack, it probably has lots of
  136. "char buf[256]"s in it, in which case why aren't those "string buf"s?
  137. (With the "string" object storing the actual data in the heap, of
  138. course.)
  139.  
  140. >|> To the user this scheme would feel exactly like C++ does now, with
  141. >|> no loopholes as far as I can tell.  The only change is that you
  142. >|> are allowed to say "GCnew" instead of "new", on any kind of data,
  143. >|> in which case that data gets deleted automatically as soon as there
  144. >|> are no pointers of any kind to it left.  Very clean, very simple
  145. >|> (at least where it counts---to the C++ user).
  146.  
  147. > What counts is the *end user*, the customer, the person who buys the
  148. > program. They don't care if it's clean and simple, they care that it does
  149. > the job, does it fast, and doesn't blow up. They notice when programs get
  150. > slower, and they complain. If necessary, by buying from a competitor.
  151.  
  152. Well, the best way to make a program do the job, do it fast, and not
  153. blow up is to keep it clean and simple.  :-)
  154.  
  155. >|> And, "no passing pointers around to Xlib, because Xlib was written
  156. >|> before GC pointers were invented."
  157.  
  158. > Ahem. This is where the "promise not to do anything clever" stuff comes in.
  159. > Just like string.h was invented before const, all it needed was to update the
  160. > _declarations_: the _definitions_ could still be in C, Mongolian Cobol
  161. > or whatever, and would not have to change. If a function can't make that
  162. > promise, you can't give it a GC pointer. Them's the breaks.
  163.  
  164. I predict that them breaks would completely hobble a program trying
  165. to interface to the outside world.  Eventually, your information has
  166. to go to some external interface, which is generally a library routine
  167. written by somebody else, perhaps in another language as you said;
  168. anything that goes to that library can't be GC'd, and that restriction
  169. will trickle back through the program and interfere with everything.
  170. Just my guess, that is.
  171.  
  172. >|> You might be able to squeak out of it by noting that "printf" is not
  173. >|> going to invoke a GC since only "GCnew" calls can do that, and "printf",
  174. >|> being non-GC-aware, will not call "GCnew".
  175.  
  176. > Printf can make the promise for exactly the reasons you give.
  177.  
  178. Okay, I guess I had that one coming.  :-)
  179.  
  180. > The callback problem might be another side of the promise. The signals
  181. > I'm not sure about, but how do they differ from, say, doing a "new" or
  182. > "delete" in a signal handle, which might be invoked while the program
  183. > is in the middle of a "new" or "delete"? Lots to think about here.
  184.  
  185. At least an implementation can protect itself by disabling signals
  186. around its "new" and "delete" routines, if that's what it takes, but
  187. the implementation can't very well disable signals around "printf"
  188. and every other existing library routine.
  189.  
  190. > [comments about GC objects referring to ordinary ones]
  191.  
  192. > I think it only becomes a serious problem if a GC object refers to a non-GC
  193. > object which refers to a GC object which... The non-GC object must not be
  194. > deleted before the GC object gets collected, otherwise the GC is going to get
  195. > soundly screwed even if it is conservative; yet the programmer has no way
  196. > of ensuring that the GC object has already gone. Alternatively, the GC object's
  197. > destructor deletes the non-GC object, and the programmer holds on to a pointer
  198. > to the non-GC object until it's safe to get rid of it. Hmmm...
  199.  
  200. You're right, it's awkward no matter what you do, unless GC objects
  201. refer only to other GC objects.  (That's why I don't like the idea
  202. of declaring classes GC-able; you want to be able to allocate *any*
  203. type, even "double *", to be GC-able.)
  204.  
  205. It's only safe for a GC-able object to maintain a non-GC-able object
  206. if it never lets a pointer to that object outside of itself.  (For
  207. example, the typical "string" class can allocate the underlying data
  208. vector that way as long as "operator[]" doesn't return a reference.)
  209.  
  210. I don't think this lack of safety is enough to veto the idea, though.
  211. It's pretty tame compared to some other booby-traps in C and C++...
  212. As long as you use GC in a consistent way, you'll be fine.
  213.  
  214. > [discussion about foreign functions stashing pointers where GC can't go]
  215.  
  216. >|> I don't see how either tagging or declaring things helps here.  I
  217. >|> didn't mean "what if some novice incorrectly stashes away a pointer
  218. >|> to a GC object," I meant "what if someone *needs* to pass a pointer
  219. >|> to a GC object to a function which will stash it away where the GC
  220. >|> can't find it."
  221.  
  222. > I wasn't talking about novices either, I was talking about detecting situations
  223. > where things have gone wrong, and informing the programmer so that corrective
  224. > action can be taken.
  225.  
  226. I guess so, although this situation would be awfully rare.
  227.  
  228. > To be honest, with the availability of static,
  229. > automatic, and non-GC allocated data structures, I'm not sure that this
  230. > will ever be an insurmountable barrier, because of the ability to make a
  231. > permanent copy, _provided_ the compiler can give warning when it happens.
  232.  
  233. I would tend to agree, possibly even without the warning.  The problem
  234. really is with us right now:  Suppose I pass a pointer to a function,
  235. which saves it away (for caching purposes, say), and then I delete the
  236. pointer not realizing the function still needs it.  The answer is to
  237. read the documentation and program carefully.  If you add a declaration
  238. to allow this to be caught by the compiler, you have to ask whether we
  239. should add declarations for a zillion other obscure pitfalls as well.
  240.  
  241. > If there *are* situations where it is unavoidable, we are going to need a
  242. > locking mechanism. [...]
  243.  
  244. Only if we insist on C++ being completely, totally safe and protected.
  245. Even languages that try to do that generally have a little bit of fine
  246. print at the bottom saying, "oh, yes, and if you use the foreign
  247. function interface, all bets are off."  :-)
  248.  
  249. > Thanks, Dave, interesting stuff.
  250.  
  251. Thanks for keeping things on an interesting level.  I'm frankly surprised
  252. this thread hasn't devolved into a flame war yet.  :-)
  253.  
  254. > Here's hoping that all this discussion will
  255. > eventually get hammered into concrete proposals for a viable system!
  256.  
  257. Agreed!
  258.  
  259.                                 -- Dave
  260. --
  261. Dave Gillespie
  262.   daveg@synaptics.com, uunet!synaptx!daveg
  263.   or: daveg@csvax.cs.caltech.edu
  264.