home *** CD-ROM | disk | FTP | other *** search
/ NetNews Usenet Archive 1992 #18 / NN_1992_18.iso / spool / comp / object / 3271 < prev    next >
Encoding:
Internet Message Format  |  1992-08-20  |  8.2 KB

  1. Xref: sparky comp.object:3271 comp.lang.clos:675
  2. Path: sparky!uunet!mcsun!uknet!gdt!aber!aberfa!pcg
  3. From: pcg@aber.ac.uk (Piercarlo Grandi)
  4. Newsgroups: comp.object,comp.lang.clos
  5. Subject: Re: Beginner's Question - CLOS
  6. Message-ID: <PCG.92Aug20153725@aberdb.aber.ac.uk>
  7. Date: 20 Aug 92 15:37:25 GMT
  8. References: <graham.714230337@galois> <16u2tpINN9ad@early-bird.think.com>
  9.     <1992Aug19.213818.21709@tfs.com> <16unk9INNl1p@early-bird.think.com>
  10. Sender: news@aber.ac.uk (USENET news service)
  11. Reply-To: pcg@aber.ac.uk (Piercarlo Grandi)
  12. Organization: Prifysgol Cymru, Aberystwyth
  13. Lines: 155
  14. In-Reply-To: barmar@think.com's message of 20 Aug 92 00: 05:29 GMT
  15. Nntp-Posting-Host: aberdb
  16.  
  17. On 20 Aug 92 00:05:29 GMT, barmar@think.com (Barry Margolin) said:
  18.  
  19. barmar> (Eric Smith) writes:
  20. barmar> [Regarding dispatching on multiple arguments]
  21.  
  22. Smith> But doesn't that make method dispatch very time consuming?  It
  23. Smith> seems like it would involve searching for the method in tables,
  24.  
  25. Yes, what really happens is that overload resolution is *always* done by
  26. having a table, say something like a relation (operator, signature,
  27. implementation). In single-method overload resolution one knows that the
  28. 'signature' is always just a single typename, so one can just cluster
  29. the full table by that column and split it into subtables, where each
  30. subtable contains all the (operator, implementation) pairs for a
  31. typename. Then if dinamic overload resolution is done the table must be
  32. made available to the runtime overload resolutor agent, and it gets
  33. called 'vtbl' in C++ or 'method dispatch table', else it can just exist
  34. only for the duration of compilation.
  35.  
  36. But even if one does not restrict overloading to the type of the first
  37. argument, there are lots of ways to exploit regularities in the
  38. (operator, signature, implementation) overload table, both at compile
  39. time and run time. At worst, at program linking time, one can glue
  40. together all the tables for all the sepratarely compiled modules, and
  41. build a global hash table. This is what Eiffel implementations do at
  42. system build time for certain reasons of their own, even if they don't
  43. have multiple overloading.
  44.  
  45. Smith> and that the amount of time consumed would depend on how many
  46. Smith> methods were in those tables and how many class arguments each
  47. Smith> method had.
  48.  
  49. Not quite. What happens is that the search is now based on a longer,
  50. composite key, but I surmise that it is not the length of the key that
  51. domaintes the cost of the search, it is the table size and its
  52. clustering.
  53.  
  54. Indeed this usually applies too:
  55.  
  56. barmar> Most generic functions don't have many methods defined for them,
  57. barmar> so the tables generally aren't very large.  Most implementations
  58. barmar> (including PCL, the portable implementation) employ caches so
  59. barmar> that you don't frequently have to do the full search.  The cache
  60. barmar> is usually a hash table keyed off the argument classes; [ ... ]
  61.  
  62. Even if strictly speaking it is not really anything special about
  63. multiple overloads. The same techniques are applied for single
  64. overloads, when the overloading can be more or less unbounded, e.g. in
  65. Smalltalk and Objective-C.
  66.  
  67. The only difference really between multiple and single overloading is
  68. that multiple overloading requires searches using a composite key. Quite
  69. orthogonal to this is the problem of whether overloading is static or
  70. dynamic, and bounded or unbounded:
  71.  
  72. Smith> In C++, method dispatch is trivial to the CPU, such that on most
  73. Smith> modern CPU's it would take a fraction of a microsecond.
  74.  
  75. This is mostly because overload resolution is yes dynamic, but bounded,
  76. in that dynamic overload resultion can only be done among objects
  77. belonging to classes in the same inheritance line; when this is true,
  78. then we can partition the overload resolution table into smaller tables,
  79. one for each line.
  80.  
  81. When instead this condition is weakened, as for example in C++ for
  82. multiple, maybe virtual, inheritance, things get much more difficult,
  83. and are solved more or less haphazardly with larger tables (as
  84. essentially the acyclic inheritance graph is really converted to a tree
  85. by copying shared subtrees), which may or may not be appropriate.
  86.  
  87. Smith> I can see the advantages of dispatching according to the types of
  88. Smith> multiple arguments, but I'm just wondering how much of a speed
  89. Smith> impact it has.
  90.  
  91. In itself, _none_. C++ for example already has it, in the form of static
  92. overload resolution (you can well declare friend functions that are
  93. overloaded on multiple arguments, for example), and since it gets
  94. entirely resolved at compile time, it costs nothing at run time.
  95.  
  96. Bounded dynamic multiple overload resolution is going to cost the same
  97. as bounded dynamic single overload resolution, and so on.
  98.  
  99. In saying above that probably it is not the lenthening of the search key
  100. that dominates the cost, I think that the real is related to the
  101. 'unbounded' and to the 'dynamic', not to the 'multiple' aspects of
  102. overload resolution.
  103.  
  104. But, but: available evidence suggests that using the techniques
  105. mentioned by Margolin, even unbounded and dynamic overload resolution,
  106. whether multiple or single, is speedy enough. Studies of Smalltalk and
  107. Objective-C code and implementations show that in the common case there
  108. are fairly predictable overloading patterns, and with a little care the
  109. overhead of overload resolution can be made a small part of the cost of
  110. running a program.
  111.  
  112. I have read of several instances, especially for application
  113. programming, where Objective-C or Smalltalk code was competitive with
  114. C++ code in speed, even if, thanks to the *possibility* of dynamic and/or
  115. unbounded overload resolution, the flexibility was much greater.
  116.  
  117. In general, while not thinking that it has anything to do with OOness,
  118. dynamic/unbounded overload resolution (aka "latent/dynamic typing" in
  119. some circles) has serious advantages and is less expensive than commonly
  120. expected:
  121.  
  122. Smith> In C++ the same functionality requires more code, but it seems
  123. Smith> like it would still be faster.
  124.  
  125. Probably not, because it amounts to a particular implementation of table
  126. squeezing and searching for multiple bounded overload resolution, and
  127. one that is too rigid, and also probably inefficient; more on this
  128. later.
  129.  
  130. barmar> I've seen discussions about this in comp.lang.c++ and
  131. barmar> comp.object, and I don't think there's a way to get real dynamic
  132. barmar> dispatch on multiple arguments in C++.  The usual way people try
  133. barmar> to implement this is with something like:
  134.  
  135. barmar> [ ... the usual Smalltalk like, seedy N>1 level dispatch trick,
  136. barmar> where resolution for overloaded arguments for N>=2 is
  137. barmar> implemented by hand by the programmer ... ]
  138.  
  139. barmar> The problem with this is that the list of possible destination
  140. barmar> classes is hard-coded into the Displayable base class, rather
  141. barmar> than being extendable.
  142.  
  143. Fully agreed. Another way to put it is that this is not *direct* support
  144. of multiple overloads; it is just like using switches instead of virtual
  145. functions for resolving single overloads.
  146.  
  147. Also it might be quite inefficient, as it creates an overload resolution
  148. tree, where the tree might well be thin and tall, which is bad for
  149. search trees. It is often more efficient to just hash, in this case.
  150.  
  151.   Explanation: if we must search on keys K1,K2,K3,... we can build a
  152.   search tree, with all the values of K1 at the first level, then with
  153.   those of K2 appended, and so on. But if the Kns are sparse, this
  154.   creates a bad or unbalanced tree. It might often be more efficient
  155.   have an hash table, keyed by  HASH_N(K1,K2,K3,...), as Margolin
  156.   mentions above.
  157.  
  158. The fact that a tree is built is just a squeeze of the general
  159. (operator, signature, implementation) table, one done taking advantage
  160. of the fact that in C++ dynamic overloading is bounded.
  161.  
  162. A generic overload resolution system could well analyze the table for
  163. regularities and just obtain the same effect automatically.  I think
  164. that some CLOS implementation do that (aided by the fact that after all
  165. more or less regrettably CLOS does have a notion of inheritance, even if
  166. it is somewhat ennobled by the possibility to redefine in a quite
  167. general way the metaobject protocol).
  168. --
  169. Piercarlo Grandi                   | JNET: pcg@uk.ac.aber
  170. Dept of CS, University of Wales    | UUCP: ...!mcsun!ukc!aber-cs!pcg
  171. Penglais, Aberystwyth SY23 3BZ, UK | INET: pcg@aber.ac.uk
  172.