Slow performance compared to C++, ideas?

Steven Schveighoffer schveiguy at yahoo.com
Wed Jun 5 16:29:12 PDT 2013


On Wed, 05 Jun 2013 18:56:28 -0400, Walter Bright  
<newshound2 at digitalmars.com> wrote:

> On 6/5/2013 3:37 PM, Steven Schveighoffer wrote:
>> No, I think it introduces a new foo.  Calling A.foo does not call  
>> B.foo.  In
>> other words, it hides the original implementation, there are two vtable  
>> entries
>> for foo.
>>
>> At least, that is how I understood the C# description from that post,  
>> and it
>> seems Walter is trying to specify that.  The idea is that B probably  
>> defined foo
>> before A did, and A adding foo should not break B, B didn't even know  
>> about A's
>> foo.
>
> That's right.
>

Prompted by Paulo's reference, I see there is another twist not clear from  
the interview article.

I think it is important to examine the *exact* semantics of C# so we can  
judge which parts to have.

Here is a complete excerpt from the ECMA submission for C#, which I think  
is very informative (bear with the unicode errors, this is copy pasted  
 from this document:  
http://www.ecma-international.org/publications/files/ECMA-ST-WITHDRAWN/ECMA-334,%202nd%20edition,%20December%202002.pdf  
):

8.13 Versioning
Versioning is the process of evolving a component over time in a  
compatible manner. A new version of a component is source compatible with  
a previous version if code that depends on the previous version can, when  
recompiled, work with the new version. In contrast, a new version of a  
component is binary compatible if an application that depended on the old  
version can, without recompilation, work with the new version.
Most languages do not support binary compatibility at all, and many do  
little to facilitate source compatibility. In fact, some languages contain  
flaws that make it impossible, in general, to evolve a class over time  
without breaking at least some client code.
As an example, consider the situation of a base class author who ships a  
class named Base. In the first version, Base contains no method F. A  
component named Derived derives from Base, and introduces an F. This  
Derived class, along with the class Base on which it depends, is released  
to customers, who deploy to numerous clients and servers.

      // Author A
      namespace A
      {
         public class Base // version 1
         {
         }
      }

      // Author B
      namespace B
      {
          class Derived: A.Base
          {
             public virtual void F() {
                System.Console.WriteLine("Derived.F");
             }
          }
      }

So far, so good, but now the versioning trouble begins. The author of Base  
produces a new version, giving it its own method F.

       // Author A
       namespace A
       {
          public class Base // version 2
          {
             public virtual void F() // added in version 2
             {
                System.Console.WriteLine("Base.F");
             }
          }
       }

This new version of Base should be both source and binary compatible with  
the initial version. (If it werenít possible to simply add a method then  
a base class could never evolve.) Unfortunately, the new F in Base makes  
the meaning of Derivedís F unclear. Did Derived mean to override Baseís  
F? This seems unlikely, since when Derived was compiled, Base did not even  
have an F! Further, if Derivedís F does override Baseís F, then it must  
adhere to the contract specified by Baseóa contract that was unspecified  
when Derived was written. In some cases, this is impossible. For example,  
Baseís F might require that overrides of it always call the base.  
Derivedís F could not possibly adhere to such a contract.

C# addresses this versioning problem by requiring developers to state  
their intent clearly. In the original code example, the code was clear,  
since Base did not even have an F. Clearly, Derivedís F is intended as a  
new method rather than an override of a base method, since no base method  
named F exists.
If Base adds an F and ships a new version, then the intent of a binary  
version of Derived is still clearó Derivedís F is semantically  
unrelated, and should not be treated as an override.

However, when Derived is recompiled, the meaning is unclearóthe author of  
Derived may intend its F to override Baseís F, or to hide it. Since the  
intent is unclear, the compiler produces a warning, and by default makes  
Derivedís F hide Baseís F. This course of action duplicates the  
semantics for the case in which Derived is not recompiled. The warning  
that is generated alerts Derivedís author to the presence of the
F method in Base.

If Derived's F is semantically unrelated to Baseís F, then Derivedís  
author can express this intentóand,
in effect, turn off the warningóby using the new keyword in the  
declaration of F.

       // Author A
       namespace A
       {
          public class Base // version 2
          {
             public virtual void F() // added in version 2
             {
                System.Console.WriteLine("Base.F");
             }
          }
       }
// Author B
namespace B
{
    class Derived: A.Base   // version 2a: new
    {
             new public virtual void F() {
                System.Console.WriteLine("Derived.F");
             }
    }
}

On the other hand, Derivedís author might investigate further, and decide  
that Derivedís F should override Baseís F. This intent can be specified  
by using the override keyword, as shown below.

       // Author A
       namespace A
       {
          public class Base // version 2
          {
             public virtual void F() // added in version 2
             {
                System.Console.WriteLine("Base.F");
             }
          }
       }
// Author B
namespace B
{
    class Derived: A.Base   // version 2b: override
    {
             public override virtual void F() {
	       base.F();
                System.Console.WriteLine("Derived.F");
             }
    }
}

The author of Derived has one other option, and that is to change the name  
of F, thus completely avoiding the name collision. Although this change  
would break source and binary compatibility for Derived, the importance of  
this compatibility varies depending on the scenario. If Derived is not  
exposed to other programs, then changing the name of F is likely a good  
idea, as it would improve the readability of the programóthere would no  
longer be any confusion about the meaning of F.


More information about the Digitalmars-d mailing list