kong@CS.UCLA.EDU (05/15/87)
[I posted this article to comp.sys.intel, and I guess many of you might be interested in this issue also.] Here is an interesting issue, What are the pros and cons of object based (or capability based) systems, say iAPX 432, in terms of protection mechanisms compared to the conventional systems, say 80286, from the architecture's point of view ? Here are some comparisons between them, 1. protection granularity 80286 uses segments as the basic protection unit. 432 uses objects. 2. protection implementation 80286 uses global and local descriptor tables(GDT and LDT) to define the rights to access the segments. Each memory reference has to check those access rights. 432 uses Access Descriptors (actually, capabilities) to define the access right the user's process has. 3. protection hierarchy 80286 uses ring structured protection hierarchy. The inner most ring is the most trusted OS kernel, the outer ring is the user's process. Each ring has a privilege level which defines the current process privilege, the privilege level will change as the process call a function which resides at the inner ring. 432 uses plain object spaces. There is no hierarchical structure. User can access every objects as long as he has the right capability. 4. instruction set 80286 has certain privileged instructions which can only be executed in the kernel mode. This is the direct consequence of the ring structure and use the segment as the basic protection unit. in 432, you can execute every instructions as long as you have the right capability to access the instruction object. 5. sharing 80286 uses some sharable LDT or aliases to share some data or instructions between some processes. Or you can put them into GDT to be shared by every processes in the system. 432 sharing is achieved by passing capabilities to the sharable object. *** It seems the conventional architecture such as 80286 has different approaches for different problems (i.e. privilege level, ring,...). Sometimes it seems that the conventional architecture can't guarantee a true protection. On the other hand, the architecture of 432 is very uniform; it solves each problems by using the same approach (I.e. the object model), but it has severe ovehead for object access. As we know, the 432 suffers from the slow performance problems which is a direct consequence of this "safest" object model. As a matter of fact, every object based systems has this kind of problems. So, what should be our choice ? a conventional architecture whose performance is acceptable but sometimes has protection problems, or a object based system which has less protection problems but an (unacceptable ?) poor performance ? (I know that at least the current object or capability based systems are very slow, hope we'll see some high performance object based systems.....) 'Kong UUCP : ..!{sdcrdcf,ihnp4,cepu,trwspp,ucbvax}!ucla-cs!kong ARPA : kong@cs.ucla.edu
kempf@hplabsc.UUCP (Jim Kempf) (05/15/87)
In article <6055@shemp.UCLA.EDU>, kong@CS.UCLA.EDU writes: > > direct consequence of this "safest" object model. As a matter of fact, > every object based systems has this kind of problems. So, what should > be our choice ? a conventional architecture whose performance is > acceptable but sometimes has protection problems, or a object based > system which has less protection problems but an (unacceptable ?) poor > performance ? (I know that at least the current object or capability The statement that "every object based system" has poor performance is simply not true. Software object based systems on conventional hardware can get performance which is very good. Objective-C(TM), for example, has messaging speed of about 3X a function call (~30 microseconds on a 16.5 MHx 68020), C++ can get full function call speed for nonvirtual (noninherited) functions, or, for the cost of one level of indirection, inherited functions can be used. The Common Lisp Object System (new standard for Common Lisp o-o programming) can get about 4X a function call for single argument (Smalltalk-like) dispatching, but for that, you can also write methods which dispatch on integers, symbols, and other built-in Common Lisp data types (which you can't do in Objective-C), and have dynamic lookup of methods at run time (which you can't do in C++). Jim Kempf kempf@hplabs.hp.com
ark@alice.UUCP (05/16/87)
In article <1831@hplabsc.UUCP>, kempf@hplabsc.UUCP writes: > C++ can get full function call speed for nonvirtual > (noninherited) functions, or, for the cost of one level of indirection, > inherited functions can be used. and later writes: > and have dynamic lookup of methods at run time (which you can't do in > C++). Two small corrections. First, "virtual" != "inherited". If, for example, I say: class Foo { public: f(); }; class Bar: public Foo { public: g(); }; Foo foo; Bar bar; then calling foo.f(), bar.g(), and bar.f() all require the same amount of overhead, which is equal to an ordinary C function call. Second, C++ does have dynamic lookup of methods at run time -- that's what "virtual" is for: class Foo { public: virtual void print(); }; class Bar: public Foo { public: void print(); }; Foo foo; Bar bar; Foo* fp1; Foo* fp2; fp1 = &foo; fp2 = &bar; Now calling foo.print() and bar.print() still take the same overhead as a C function call, because the compiler can determine the type of foo and bar statically. Calling fp1->print() and fp2->print() involve dynamic lookup, because even though fp1 and fp2 are declared to be Foo pointers, they can really point to a Foo or anything derived from it. In this example, fp1->print() will call the Foo print function and fp2->print() would call the Bar print function. This dynamic lookup requires one extra memory reference per call.
benson@alcatraz.ksr.com (Benson Margulies) (05/17/87)
In article <1831@hplabsc.UUCP> kempf@hplabsc.UUCP (Jim Kempf) writes: >> performance ? (I know that at least the current object or capability > >The statement that "every object based system" has poor performance is >simply not true. Software object based systems on conventional hardware >can get performance which is very good. I think that there has to be a distinction betwen object oriented programming and capability architecture. A capability architecture is a lot more than a hardware implementation of object orientated programming. It is a particular approach to data security/integrity. You can have a completely conventional programming environment on a capability environment, where a capability is just a pointer to a piece of data structure. YOu can do object oriented programming on hardware that don't know anything from Security. The Symbolics 36xx series has architectural support for flavors, but not a hint of a capability. Benson I. Margulies Kendall Square Research Corp. harvard!ksr!benson All comments the responsibility ksr!benson@harvard.harvard.edu of the author, if anyone.
kempf@hplabsc.UUCP (Jim Kempf) (05/17/87)
In article <6894@alice.UUCP>, ark@alice.UUCP writes: > > C++ can get full function call speed for nonvirtual > > (noninherited) functions, or, for the cost of one level of indirection, > > inherited functions can be used. > > and later writes: > > and have dynamic lookup of methods at run time (which you can't do in > > C++). > > Two small corrections. > > First, "virtual" != "inherited". If, for example, I say: > > class Foo { > public: > f(); > }; > > class Bar: public Foo { > public: > g(); > }; > > Foo foo; > Bar bar; > > then calling foo.f(), bar.g(), and bar.f() all require the same amount > of overhead, which is equal to an ordinary C function call. > I don't see how this invalidates my point. My understanding is that g() is defined on the class Bar (and thus not inherited) and f() is defined on the class Foo (and thus not inherited). Since foo is of type Foo and bar is of type Bar, invoking foo.f() is invoking a noninherited function and invoking bar.g() is invoking a noninherited function. Since types are declared at compile time in C++, the compiler presumably arranges for the proper function to get inserted. From your example, neither g() nor f() are declared virtual. > Second, C++ does have dynamic lookup of methods at run time -- that's > what "virtual" is for: > > class Foo { > public: > virtual void print(); > }; > > class Bar: public Foo { > public: > void print(); > }; > > Foo foo; > Bar bar; > Foo* fp1; > Foo* fp2; > > fp1 = &foo; > fp2 = &bar; > > Now calling foo.print() and bar.print() still take the same overhead as > a C function call, because the compiler can determine the type of foo > and bar statically. Calling fp1->print() and fp2->print() involve > dynamic lookup, because even though fp1 and fp2 are declared to be > Foo pointers, they can really point to a Foo or anything derived from > it. In this example, fp1->print() will call the Foo print function > and fp2->print() would call the Bar print function. > > This dynamic lookup requires one extra memory reference per call. But can I define another level: class Fee : public Bar { public: void print(); }; and have this function override the one inherited from Bar? My understanding of C++ is that this is not true (I could be wrong, however, as I'm not a guru). I guess I should have called this "dynamic redefinition" rather than "dynamic lookup". Also, can I define a virtual function halfway down an inheritance chain and have it inherited? Furthermore, your example seems to confirm my assertion that "virtual = inherited". (Apologies to those who are not interested in the esoterica of object oriented programming. Maybe this discussion should be moved to comp.lang?) Jim Kempf kempf@hplabs.hp.com
keith@nih-csl.UUCP (keith gorlen) (05/19/87)
In article <1837@hplabsc.UUCP>, kempf@hplabsc.UUCP (Jim Kempf) writes: > In article <6894@alice.UUCP>, ark@alice.UUCP writes: > > > Second, C++ does have dynamic lookup of methods at run time -- that's > > what "virtual" is for: > > > > class Foo { > > public: > > virtual void print(); > > }; > > > > class Bar: public Foo { > > public: > > void print(); > > }; > > > > Foo foo; > > Bar bar; > > Foo* fp1; > > Foo* fp2; > > > > fp1 = &foo; > > fp2 = &bar; > > > > Now calling foo.print() and bar.print() still take the same overhead as > > a C function call, because the compiler can determine the type of foo > > and bar statically. ... In fact, the code generated for foo.print() and bar.print() uses the virtual function pointer table (vtbl) just as fp1->print() and fp2->print() do. You must write Foo::foo.print() to inhibit the dynamic binding. It is not immediately obvious to me why this is. > > But can I define another level: > > class Fee : public Bar { > public: > > void print(); > > }; > > and have this function override the one inherited from Bar? Yes. > Also, can I define a virtual function halfway down an inheritance > chain and have it inherited? If by this you mean "If I declare Foo::print() as a non-virtual member function and Bar::print() as a virtual member function, will class Fee inherit the definition of Bar::print()?" the answer is yes. Where the C++ dynamic binding mechanism differs from that of Smalltalk-80 is that the table used to locate the virtual member functions ("methods" in Smalltalk parlance) for a class contains only the methods actually defined in the class in Smalltalk, whereas in C++ the table contains pointers to virtual functions defined in the class AND to those inherited from the base class, the base class's base class, etc. Thus, in Smalltalk calling a method involves searching the tables from the class of the current object up toward the root of the class hierarchy until the method is found. In C++, you just index into the table for the class of the current object. C++ trades off space for speed. Also, C++ has nothing analogous to Smalltalk's "super". > (Apologies to those who are not interested in the esoterica of > object oriented programming. Maybe this discussion should be > moved to comp.lang?) Yes, it should! Subject: Re: Do every object based systems suffer from poor performance ? Newsgroups: comp.arch Keywords: capability, object, 80286, 432 Summary: Clarification of C++ virtual functions References: <6055@shemp.UCLA.EDU> <1831@hplabsc.UUCP> <6894@alice.UUCP> <1837@hplabsc.UUCP> -- Keith Gorlen phone: (301) 496-5363 Building 12A, Room 2017 uucp: seismo!elsie!nih-csl!keith National Institutes of Health Bethesda, MD 20892